export type SseEvent = { event: string; data: string; }; export type TencentReplyEvent = { type?: "reply"; payload?: { content?: string; is_evil?: boolean; is_final?: boolean; is_from_self?: boolean; is_llm_generated?: boolean; }; }; export type TencentErrorEvent = { type?: "error"; error?: { code?: number; message?: string; }; message?: string; status?: number; }; export function parseJsonSafely(value: string): T | null { try { return JSON.parse(value) as T; } catch { return null; } } export function splitSseEvents(buffer: string): { events: SseEvent[]; rest: string; } { const blocks = buffer.split(/\r?\n\r?\n/); const rest = blocks.pop() ?? ""; const events = blocks .map(parseSseEventBlock) .filter((event): event is SseEvent => event !== null); return { events, rest }; } function parseSseEventBlock(block: string): SseEvent | null { const lines = block.split(/\r?\n/); let eventName = "message"; const dataLines: string[] = []; for (const rawLine of lines) { const line = rawLine.trimEnd(); if (!line || line.startsWith(":")) { continue; } if (line.startsWith("event:")) { eventName = line.slice(6).trim(); continue; } if (line.startsWith("data:")) { dataLines.push(line.slice(5).trimStart()); } } if (dataLines.length === 0) { return null; } return { event: eventName, data: dataLines.join("\n"), }; }