78 lines
1.5 KiB
TypeScript
78 lines
1.5 KiB
TypeScript
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<T>(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"),
|
|
};
|
|
}
|