/* api.js - Chan Compass frontend -> Gradio Server backend. JSON helpers + a Server-Sent-Events reader for streaming LLM output. */ window.CCApi = (function () { async function jget(url) { const r = await fetch(url); if (!r.ok) throw new Error(await r.text()); return r.json(); } async function jpost(url, body, timeoutMs = 180000) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const r = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body || {}), signal: controller.signal, }); if (!r.ok) throw new Error(await r.text()); return r.json(); } catch (e) { if (e.name === "AbortError") throw new Error("Request timed out after " + (timeoutMs / 1000) + "s — the server may still be running. Refresh and try again."); throw e; } finally { clearTimeout(timer); } } /* Stream an SSE endpoint. onChunk(obj) per message; resolves on `done`. */ function stream(url, onChunk) { return new Promise((resolve, reject) => { const es = new EventSource(url); es.onmessage = (e) => { try { onChunk(JSON.parse(e.data)); } catch (_) { onChunk({ text: e.data }); } }; es.addEventListener("done", () => { es.close(); resolve(); }); es.onerror = () => { es.close(); resolve(); }; }); } return { lastResults: () => jget("/api/last-results"), runSignals: (pool, force) => jpost("/api/signals/run", { pool, force }), signalSummary: (ticker, onChunk) => stream("/api/signals/summary?ticker=" + encodeURIComponent(ticker), onChunk), rotation: () => jget("/api/rotation"), rotationNarrative: (onChunk) => stream("/api/rotation/narrative", onChunk), holdings: () => jget("/api/news/holdings"), saveHoldings: (holdings) => jpost("/api/news/save", { holdings }), checkNews: (onChunk) => stream("/api/news/check", onChunk), runResearch: (ticker, onChunk) => stream("/api/research/run?ticker=" + encodeURIComponent(ticker), onChunk), reports: () => jget("/api/research/reports"), report: (name) => jget("/api/research/report?name=" + encodeURIComponent(name)), autoRun: () => jpost("/api/automation/run", {}), autoState: () => jget("/api/automation/state"), publishTraces: (repo) => jpost("/api/automation/publish-traces", { repo }), marketStatus: () => jget("/api/market/status"), modelList: () => jget("/api/model/list"), modelLoad: (model) => jpost("/api/model/load", { model }), modelStatus: () => jget("/api/model/status"), modelTest: () => jpost("/api/model/test", {}), modelTestStatus: () => jget("/api/model/test-status"), finetuneStatus: () => jget("/api/model/finetune-status"), exportDataset: () => jpost("/api/model/export-dataset", {}), email: (content, to, tag) => jpost("/api/email", { content, to, tag }), }; })();