Spaces:
Sleeping
Sleeping
| import type { Data, Layout } from "plotly.js"; | |
| import { getEnv } from "@/env"; | |
| const AGENTIC_BASE_URL = getEnv("VITE_AGENTIC_API_BASE_URL"); | |
| export class AgenticApiError extends Error { | |
| status: number; | |
| data: unknown; | |
| constructor(message: string, status: number, data?: unknown) { | |
| super(message); | |
| this.name = "AgenticApiError"; | |
| this.status = status; | |
| this.data = data; | |
| } | |
| } | |
| export type AgentStreamEvent = | |
| | { type: "sources"; data: unknown[] } | |
| | { type: "status"; data: string } | |
| | { type: "chunk"; data: string } | |
| | { type: "done"; data: { message_id?: string; [key: string]: unknown } } | |
| | { type: "error"; data: string }; | |
| export interface AgentChatRequest { | |
| user_id: string; | |
| analysis_id: string; | |
| message: string; | |
| } | |
| export interface Tool { | |
| command: "/help" | "/report" | string; | |
| name: "help" | "report" | string; | |
| type: "skill" | string; | |
| description: string; | |
| } | |
| export interface ObservabilityPlanningStep { | |
| step?: number; | |
| stage?: string; | |
| objective?: string; | |
| } | |
| export interface ObservabilityPlanning { | |
| goal_restated?: string; | |
| assumptions?: string[]; | |
| steps?: ObservabilityPlanningStep[]; | |
| } | |
| export interface ObservabilityToolCall { | |
| order?: number; | |
| task_id?: string | null; | |
| name: string; | |
| summary?: string; | |
| input?: unknown; | |
| output?: unknown; | |
| status?: string; | |
| error?: string | null; | |
| } | |
| export interface ObservabilityDataUsedSource { | |
| id: string; | |
| name: string; | |
| type: string; | |
| } | |
| export interface ObservabilityDataUsedTable { | |
| id: string; | |
| name: string; | |
| role?: string; | |
| } | |
| export interface ObservabilityDataUsedColumn { | |
| id: string; | |
| name: string; | |
| table?: string; | |
| data_type?: string; | |
| pii?: boolean; | |
| roles?: string[]; | |
| } | |
| export interface ObservabilityDataUsedOutputColumn { | |
| name: string; | |
| kind?: string; | |
| from?: string; | |
| formula?: string; | |
| } | |
| export interface ObservabilityDataUsed { | |
| source: ObservabilityDataUsedSource; | |
| tables?: ObservabilityDataUsedTable[]; | |
| joins?: unknown[]; | |
| columns_read?: ObservabilityDataUsedColumn[]; | |
| output_columns?: ObservabilityDataUsedOutputColumn[]; | |
| filters?: unknown[]; | |
| group_by?: string[]; | |
| order_by?: unknown[]; | |
| limit?: number | null; | |
| rows_returned?: number; | |
| query?: string; | |
| } | |
| export interface ChartSpec { | |
| schema: string; | |
| chart_type: string; | |
| title?: string | null; | |
| plotly: { | |
| data: Data[]; | |
| layout?: Partial<Layout>; | |
| }; | |
| } | |
| export interface ChartItem { | |
| chart_id: string; | |
| chart_type: string; | |
| title?: string | null; | |
| spec: ChartSpec; | |
| created_at: string; | |
| } | |
| export interface ChartsResponse { | |
| status: "success" | "empty" | "not_found"; | |
| message: string; | |
| count: number; | |
| charts: ChartItem[]; | |
| } | |
| export interface Observability { | |
| analysis_id: string; | |
| message_id: string; | |
| intent?: string; | |
| generated_at?: string; | |
| planning: ObservabilityPlanning | null; | |
| thinking: string | null; | |
| tool_calls: ObservabilityToolCall[]; | |
| data_used?: ObservabilityDataUsed[]; | |
| sources: unknown[]; | |
| } | |
| export interface ReportSummary { | |
| report_id: string; | |
| version: number; | |
| generated_at: string; | |
| record_count?: number; | |
| } | |
| export interface ReportFinding { | |
| text: string; | |
| record_ids?: string[]; | |
| supporting_data?: unknown; | |
| } | |
| export interface ReportDetail { | |
| report_id: string; | |
| analysis_id: string; | |
| user_id: string; | |
| version: number; | |
| generated_at: string; | |
| problem_statement?: { | |
| objective?: string; | |
| business_questions?: string[]; | |
| }; | |
| record_ids?: string[]; | |
| executive_summary?: string; | |
| findings?: ReportFinding[]; | |
| caveats?: ReportFinding[]; | |
| open_questions?: ReportFinding[]; | |
| data_sources?: unknown[]; | |
| method_steps?: unknown[]; | |
| rendered_markdown?: string; | |
| } | |
| async function parseAgenticError(res: Response): Promise<AgenticApiError> { | |
| const body = await res.json().catch(() => null); | |
| const message = | |
| (body && typeof body === "object" && "detail" in body && String((body as { detail?: string }).detail)) || | |
| (body && typeof body === "object" && "message" in body && String((body as { message?: string }).message)) || | |
| `HTTP ${res.status}`; | |
| return new AgenticApiError(message, res.status, body); | |
| } | |
| async function agenticJson<T>(path: string, options: RequestInit = {}): Promise<T> { | |
| const headers = new Headers(options.headers); | |
| if (!headers.has("Content-Type")) headers.set("Content-Type", "application/json"); | |
| const res = await fetch(`${AGENTIC_BASE_URL}${path}`, { | |
| ...options, | |
| headers, | |
| }); | |
| if (!res.ok) throw await parseAgenticError(res); | |
| return (await res.json()) as T; | |
| } | |
| function parseSseData(eventName: string, data: string): AgentStreamEvent { | |
| if (eventName === "sources") { | |
| try { | |
| const parsed = JSON.parse(data); | |
| return { type: "sources", data: Array.isArray(parsed) ? parsed : [] }; | |
| } catch { | |
| return { type: "sources", data: [] }; | |
| } | |
| } | |
| if (eventName === "done") { | |
| try { | |
| return { type: "done", data: JSON.parse(data) }; | |
| } catch { | |
| return { type: "done", data: {} }; | |
| } | |
| } | |
| if (eventName === "error") return { type: "error", data }; | |
| if (eventName === "status") return { type: "status", data }; | |
| return { type: "chunk", data }; | |
| } | |
| async function readEventStream(res: Response, onEvent: (event: AgentStreamEvent) => void): Promise<void> { | |
| if (!res.ok) throw await parseAgenticError(res); | |
| if (!res.body) throw new AgenticApiError("Streaming is not supported by this browser", 0); | |
| const reader = res.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let buffer = ""; | |
| const emitBlock = (rawBlock: string) => { | |
| const lines = rawBlock.split(/\r?\n/); | |
| let eventName = "chunk"; | |
| const dataLines: string[] = []; | |
| for (const line of lines) { | |
| if (line.startsWith("event:")) eventName = line.slice(6).trim(); | |
| if (line.startsWith("data:")) { | |
| const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5); | |
| dataLines.push(value); | |
| } | |
| } | |
| if (dataLines.length === 0) return; | |
| onEvent(parseSseData(eventName, dataLines.join("\n"))); | |
| }; | |
| while (true) { | |
| const { value, done } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const parts = buffer.split(/\r?\n\r?\n/); | |
| buffer = parts.pop() ?? ""; | |
| parts.forEach(emitBlock); | |
| } | |
| if (buffer.trim()) emitBlock(buffer); | |
| } | |
| export async function streamChat(request: AgentChatRequest, onEvent: (event: AgentStreamEvent) => void): Promise<void> { | |
| const res = await fetch(`${AGENTIC_BASE_URL}/api/v2/chat/stream`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify(request), | |
| }); | |
| await readEventStream(res, onEvent); | |
| } | |
| export async function streamHelp( | |
| request: { user_id: string; analysis_id: string }, | |
| onEvent: (event: AgentStreamEvent) => void | |
| ): Promise<void> { | |
| const res = await fetch(`${AGENTIC_BASE_URL}/api/v1/tools/help`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify(request), | |
| }); | |
| await readEventStream(res, onEvent); | |
| } | |
| export const listTools = (): Promise<{ count: number; tools: Tool[] }> => agenticJson("/api/v1/tools/list"); | |
| export const getObservability = (analysisId: string, messageId: string): Promise<Observability> => | |
| agenticJson( | |
| `/api/v1/traceability?analysis_id=${encodeURIComponent(analysisId)}&message_id=${encodeURIComponent(messageId)}` | |
| ); | |
| export const getCharts = (messageId: string): Promise<ChartsResponse> => | |
| agenticJson(`/api/v1/charts?message_id=${encodeURIComponent(messageId)}`); | |
| export const generateReport = (analysisId: string, userId: string): Promise<ReportDetail> => | |
| agenticJson( | |
| `/api/v1/tools/report?analysis_id=${encodeURIComponent(analysisId)}&user_id=${encodeURIComponent(userId)}`, | |
| { method: "POST" } | |
| ); | |
| export const listReportVersions = (analysisId: string): Promise<ReportSummary[]> => | |
| agenticJson(`/api/v1/tools/report/${encodeURIComponent(analysisId)}`); | |
| export const getReportVersion = (analysisId: string, version: number): Promise<ReportDetail> => | |
| agenticJson(`/api/v1/tools/report/${encodeURIComponent(analysisId)}/${encodeURIComponent(String(version))}`); | |