///
import type { Observation, StepResult, Task } from './types'
const BASE = (import.meta.env.VITE_API_URL as string | undefined) || ''
async function post(path: string, body: unknown): Promise {
const r = await fetch(`${BASE}${path}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
})
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`)
return r.json() as Promise
}
async function get(path: string): Promise {
const r = await fetch(`${BASE}${path}`)
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`)
return r.json() as Promise
}
export const api = {
reset: (task_id: string, model = 'manual') => post('/reset', { task_id, model }),
step: (action: Record) => post('/step', action),
state: () => get>('/state'),
tasks: () => get<{ tasks: Task[] }>('/tasks'),
leaderboard: () => get<{ leaderboard: unknown[] }>('/leaderboard'),
stats: () => get>('/stats'),
history: () => get<{ history: unknown[] }>('/history'),
replay: (runId: string) => get>(`/replay/${runId}`),
customScenarios: () => get<{ custom_scenarios: string[] }>('/scenarios/custom'),
createCustomScenario: (body: Record) => post>('/scenarios/custom', body),
memory: () => get<{ memory: unknown[] }>('/memory'),
submitFeedback: (body: Record) => post>('/feedback', body),
}
// SSE helper for live agent streaming
export function createAgentStream(body: Record, onEvent: (e: Record) => void, onDone: () => void) {
const ctrl = new AbortController()
fetch(`${BASE}/agent/run`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body), signal: ctrl.signal,
}).then(async res => {
const reader = res.body?.getReader()
const decoder = new TextDecoder()
if (!reader) { onDone(); return }
let buf = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buf += decoder.decode(value, { stream: true })
const lines = buf.split('\n')
buf = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
try { onEvent(JSON.parse(line.slice(6))) } catch {}
}
}
}
onDone()
}).catch(() => onDone())
return () => ctrl.abort()
}
export function createCompareStream(body: Record, onEvent: (e: Record) => void, onDone: () => void) {
const ctrl = new AbortController()
fetch(`${BASE}/agent/compare`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body), signal: ctrl.signal,
}).then(async res => {
const reader = res.body?.getReader()
const decoder = new TextDecoder()
if (!reader) { onDone(); return }
let buf = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buf += decoder.decode(value, { stream: true })
const lines = buf.split('\n')
buf = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
try { onEvent(JSON.parse(line.slice(6))) } catch {}
}
}
}
onDone()
}).catch(() => onDone())
return () => ctrl.abort()
}