Files
Sybil-2/web/src/lib/api.ts

204 lines
4.9 KiB
TypeScript
Raw Normal View History

2026-02-13 23:15:12 -08:00
export type ChatSummary = {
id: string;
title: string | null;
createdAt: string;
updatedAt: string;
};
2026-02-13 23:49:55 -08:00
export type SearchSummary = {
id: string;
title: string | null;
query: string | null;
createdAt: string;
updatedAt: string;
};
2026-02-13 23:15:12 -08:00
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[];
};
2026-02-13 23:49:55 -08:00
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;
2026-02-14 00:14:10 -08:00
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;
2026-02-13 23:49:55 -08:00
results: SearchResultItem[];
};
2026-02-13 23:15:12 -08:00
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<T>(path: string, init?: RequestInit): Promise<T> {
const headers = new Headers(init?.headers ?? {});
2026-02-14 01:10:27 -08:00
const hasBody = init?.body !== undefined && init.body !== null;
if (hasBody && !headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
2026-02-13 23:15:12 -08:00
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;
}
2026-02-14 01:10:27 -08:00
export async function deleteChat(chatId: string) {
await api<{ deleted: true }>(`/v1/chats/${chatId}`, { method: "DELETE" });
}
2026-02-13 23:49:55 -08:00
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;
}
2026-02-14 01:10:27 -08:00
export async function deleteSearch(searchId: string) {
await api<{ deleted: true }>(`/v1/searches/${searchId}`, { method: "DELETE" });
}
2026-02-13 23:49:55 -08:00
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;
}
2026-02-13 23:15:12 -08:00
export async function runCompletion(body: {
chatId: string;
provider: "openai" | "anthropic" | "xai";
model: string;
messages: CompletionRequestMessage[];
}) {
return api<CompletionResponse>("/v1/chat-completions", {
method: "POST",
body: JSON.stringify(body),
});
}