Spaces:
Sleeping
Sleeping
| // 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; | |
| } | |
| } | |
| } | |