Files
Agent-System/web/src/api/taskRuns.ts
T

100 lines
3.1 KiB
TypeScript

import { parseSSEChunk, type StreamEvent } from './chatStream';
import { mergeAuthHeaders, getBearerAuthHeaders } from './authHeaders';
export type CreateTaskRunBody = Record<string, unknown> & {
session_id: string;
project_id: number;
user_id?: number;
client_turn_id?: string;
user_message?: string;
};
export type CreateTaskRunResponse = {
run_id: string;
status: string;
workspace_id?: string;
};
export async function createTaskRun(body: CreateTaskRunBody): Promise<CreateTaskRunResponse> {
const res = await fetch('/api/v1/tasks/runs', {
method: 'POST',
headers: getBearerAuthHeaders('application/json'),
body: JSON.stringify(body),
});
if (!res.ok) {
let detail = '';
let errorCode = '';
try {
const payload = (await res.json()) as {
message?: string;
error?: string;
error_code?: string;
detail?: string | { reason?: string };
};
const rawCode = payload.error_code || payload.error || '';
errorCode = rawCode === 'document_busy' ? 'DOC_BUSY' : rawCode;
const structuredDetail = typeof payload.detail === 'object' ? payload.detail?.reason : payload.detail;
detail = payload.message || structuredDetail || errorCode || '';
} catch {
// ignore non-json body
}
throw Object.assign(
new Error(`create task run failed: HTTP ${res.status}${detail ? `: ${detail}` : ''}`),
{ httpStatus: res.status, errorCode, detail },
);
}
return res.json();
}
export async function subscribeTaskRunEvents(
runId: string,
handlers: { onEvent: (ev: StreamEvent) => void; afterSeq?: number },
signal?: AbortSignal,
): Promise<void> {
const after = handlers.afterSeq ?? 0;
const url = `/api/v1/tasks/runs/${encodeURIComponent(runId)}/events?after_seq=${after}`;
const resp = await fetch(url, {
method: 'GET',
headers: mergeAuthHeaders({ Accept: 'text/event-stream' }),
signal,
});
if (!resp.ok || !resp.body) {
throw new Error(`subscribe task run failed: HTTP ${resp.status}`);
}
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buf = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
const parsed = parseSSEChunk(buf);
buf = parsed.rest;
for (const ev of parsed.events) handlers.onEvent(ev);
}
if (buf) {
const flushed = parseSSEChunk(buf, { flush: true });
for (const ev of flushed.events) handlers.onEvent(ev);
}
}
export async function cancelTaskRun(runId: string): Promise<void> {
const res = await fetch(`/api/v1/tasks/runs/${encodeURIComponent(runId)}/cancel`, {
method: 'POST',
headers: getBearerAuthHeaders(),
});
if (!res.ok) {
throw new Error(`cancel task run failed: HTTP ${res.status}`);
}
}
export async function getTaskRun(runId: string) {
const res = await fetch(`/api/v1/tasks/runs/${encodeURIComponent(runId)}`, {
headers: getBearerAuthHeaders(),
});
if (!res.ok) {
throw new Error(`get task run failed: HTTP ${res.status}`);
}
return res.json();
}