import toast from 'react-hot-toast'; const WIPLogTypes = ['plans', 'tools', 'code']; const AllLogTypes = [ 'plans', 'tools', 'code', 'final_code', 'final_error', ] as const; export type ChunkBody = { type: (typeof AllLogTypes)[number]; status: 'started' | 'completed' | 'failed' | 'running'; timestamp?: string; payload: | Array> // PlansBody | ToolsBody | PrismaJson.FinalCodeBody['payload'] // CodeBody & FinalCodeBody | PrismaJson.StructuredError; // ErrorBody }; export type WIPChunkBodyGroup = PrismaJson.MessageBody & { timestamp?: string; duration?: number; }; /** * Formats the stream logs and returns an array of grouped sections. * * @param content - The content of the stream logs. * @returns An array of grouped sections and an optional final code result. */ export const formatStreamLogs = ( content: WIPChunkBodyGroup[] | null, ): [ WIPChunkBodyGroup[], PrismaJson.FinalCodeBody['payload']?, PrismaJson.StructuredError?, ] => { if (!content) return [[], undefined, undefined]; // Merge consecutive logs of the same type to the latest status const groupedSections = content.reduce((acc: WIPChunkBodyGroup[], curr) => { const lastGroup = acc[acc.length - 1]; if ( acc.length > 0 && lastGroup.type === curr.type && curr.status !== 'started' ) { acc[acc.length - 1] = { ...curr, // always use the timestamp of the first log timestamp: lastGroup?.timestamp, // duration is the difference between the last log and the first log duration: lastGroup?.timestamp && curr.timestamp ? Date.parse(curr.timestamp) - Date.parse(lastGroup.timestamp) : undefined, }; } else { acc.push(curr); } return acc; }, []); return [ groupedSections.filter(section => WIPLogTypes.includes(section.type)), groupedSections.find(section => section.type === 'final_code') ?.payload as PrismaJson.FinalCodeBody['payload'], groupedSections.find(section => section.type === 'final_error') ?.payload as PrismaJson.StructuredError, ]; };