File size: 1,960 Bytes
86f402d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | interface StreamChatCallbacks {
onText: (chunk: string) => void;
onToolStart: (tool: string, callId: string) => void;
onToolResult: (tool: string, callId: string, result: Record<string, unknown>) => void;
onDone: () => void;
onError: (message: string) => void;
}
export async function streamChatMessage(
patientId: string,
content: string,
image: File | null,
callbacks: StreamChatCallbacks
): Promise<void> {
try {
const formData = new FormData();
formData.append('content', content);
if (image) formData.append('image', image);
const response = await fetch(`/api/patients/${patientId}/chat`, {
method: 'POST',
body: formData,
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
if (!reader) {
callbacks.onDone();
return;
}
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split('\n').filter(l => l.startsWith('data: '));
for (const line of lines) {
const data = line.slice('data: '.length);
try {
const event = JSON.parse(data);
if (event.type === 'text') {
callbacks.onText(event.content ?? '');
} else if (event.type === 'tool_start') {
callbacks.onToolStart(event.tool, event.call_id);
} else if (event.type === 'tool_result') {
callbacks.onToolResult(event.tool, event.call_id, event.result ?? {});
} else if (event.type === 'done') {
callbacks.onDone();
return;
} else if (event.type === 'error') {
callbacks.onError(event.message ?? 'Unknown error');
}
} catch {
// skip malformed lines
}
}
}
callbacks.onDone();
} catch (error) {
console.error('Stream error:', error);
callbacks.onError(String(error));
}
}
|