pikamomo's picture
fix
a7c9ddd
// Use empty string for same-origin requests in production (HF Spaces)
// Only fallback to localhost:8000 if VITE_API_BASE_URL is truly undefined
const baseURL =
import.meta.env.VITE_API_BASE_URL !== undefined
? import.meta.env.VITE_API_BASE_URL
: "http://localhost:8000";
export interface ResearchRequest {
topic: string;
search_api?: string;
}
export interface ResearchStreamEvent {
type: string;
[key: string]: unknown;
}
export interface StreamOptions {
signal?: AbortSignal;
}
export async function runResearchStream(
payload: ResearchRequest,
onEvent: (event: ResearchStreamEvent) => void,
options: StreamOptions = {}
): Promise<void> {
const response = await fetch(`${baseURL}/research/stream`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "text/event-stream"
},
body: JSON.stringify(payload),
signal: options.signal
});
if (!response.ok) {
const errorText = await response.text().catch(() => "");
throw new Error(
errorText || `Research request failed, status code: ${response.status}`
);
}
const body = response.body;
if (!body) {
throw new Error("Browser does not support streaming responses, unable to get research progress");
}
const reader = body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { value, done } = await reader.read();
buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
let boundary = buffer.indexOf("\n\n");
while (boundary !== -1) {
const rawEvent = buffer.slice(0, boundary).trim();
buffer = buffer.slice(boundary + 2);
if (rawEvent.startsWith("data:")) {
const dataPayload = rawEvent.slice(5).trim();
if (dataPayload) {
try {
const event = JSON.parse(dataPayload) as ResearchStreamEvent;
onEvent(event);
if (event.type === "error" || event.type === "done") {
return;
}
} catch (error) {
console.error("Failed to parse streaming event:", error, dataPayload);
}
}
}
boundary = buffer.indexOf("\n\n");
}
if (done) {
// Handle possible trailing events
if (buffer.trim()) {
const rawEvent = buffer.trim();
if (rawEvent.startsWith("data:")) {
const dataPayload = rawEvent.slice(5).trim();
if (dataPayload) {
try {
const event = JSON.parse(dataPayload) as ResearchStreamEvent;
onEvent(event);
} catch (error) {
console.error("Failed to parse streaming event:", error, dataPayload);
}
}
}
}
break;
}
}
}