export type ChatSummary = { id: string; title: string | null; createdAt: string; updatedAt: string; }; export type SearchSummary = { id: string; title: string | null; query: string | null; createdAt: string; updatedAt: string; }; export type Message = { id: string; createdAt: string; role: "system" | "user" | "assistant" | "tool"; content: string; name: string | null; }; export type ChatDetail = { id: string; title: string | null; createdAt: string; updatedAt: string; messages: Message[]; }; export type SearchResultItem = { id: string; createdAt: string; rank: number; title: string | null; url: string; publishedDate: string | null; author: string | null; text: string | null; highlights: string[] | null; highlightScores: number[] | null; score: number | null; favicon: string | null; image: string | null; }; export type SearchDetail = { id: string; title: string | null; query: string | null; createdAt: string; updatedAt: string; requestId: string | null; latencyMs: number | null; error: string | null; answerText: string | null; answerRequestId: string | null; answerCitations: Array<{ id?: string; url?: string; title?: string | null; publishedDate?: string | null; author?: string | null; text?: string | null; }> | null; answerError: string | null; results: SearchResultItem[]; }; export type CompletionRequestMessage = { role: "system" | "user" | "assistant" | "tool"; content: string; name?: string; }; type CompletionResponse = { chatId: string | null; message: { role: "assistant"; content: string; }; }; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8787"; const ENV_ADMIN_TOKEN = (import.meta.env.VITE_ADMIN_TOKEN as string | undefined)?.trim() || null; let authToken: string | null = ENV_ADMIN_TOKEN; export function getConfiguredToken() { return ENV_ADMIN_TOKEN; } export function setAuthToken(token: string | null) { authToken = token?.trim() || null; } async function api(path: string, init?: RequestInit): Promise { const headers = new Headers(init?.headers ?? {}); const hasBody = init?.body !== undefined && init.body !== null; if (hasBody && !headers.has("Content-Type")) { headers.set("Content-Type", "application/json"); } if (authToken) { headers.set("Authorization", `Bearer ${authToken}`); } const response = await fetch(`${API_BASE_URL}${path}`, { ...init, headers, }); if (!response.ok) { const fallback = `${response.status} ${response.statusText}`; let message = fallback; try { const body = (await response.json()) as { message?: string }; if (body.message) message = body.message; } catch { // keep fallback message } throw new Error(message); } return (await response.json()) as T; } export async function listChats() { const data = await api<{ chats: ChatSummary[] }>("/v1/chats"); return data.chats; } export async function verifySession() { return api<{ authenticated: true; mode: "open" | "token" }>("/v1/auth/session"); } export async function createChat(title?: string) { const data = await api<{ chat: ChatSummary }>("/v1/chats", { method: "POST", body: JSON.stringify({ title }), }); return data.chat; } export async function getChat(chatId: string) { const data = await api<{ chat: ChatDetail }>(`/v1/chats/${chatId}`); return data.chat; } export async function deleteChat(chatId: string) { await api<{ deleted: true }>(`/v1/chats/${chatId}`, { method: "DELETE" }); } export async function listSearches() { const data = await api<{ searches: SearchSummary[] }>("/v1/searches"); return data.searches; } export async function createSearch(body?: { title?: string; query?: string }) { const data = await api<{ search: SearchSummary }>("/v1/searches", { method: "POST", body: JSON.stringify(body ?? {}), }); return data.search; } export async function getSearch(searchId: string) { const data = await api<{ search: SearchDetail }>(`/v1/searches/${searchId}`); return data.search; } export async function deleteSearch(searchId: string) { await api<{ deleted: true }>(`/v1/searches/${searchId}`, { method: "DELETE" }); } export async function runSearch( searchId: string, body: { query?: string; title?: string; type?: "auto" | "fast" | "deep" | "instant"; numResults?: number; includeDomains?: string[]; excludeDomains?: string[]; } ) { const data = await api<{ search: SearchDetail }>(`/v1/searches/${searchId}/run`, { method: "POST", body: JSON.stringify(body), }); return data.search; } export async function runCompletion(body: { chatId: string; provider: "openai" | "anthropic" | "xai"; model: string; messages: CompletionRequestMessage[]; }) { return api("/v1/chat-completions", { method: "POST", body: JSON.stringify(body), }); }