Add web frontend
This commit is contained in:
109
web/src/lib/api.ts
Normal file
109
web/src/lib/api.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export type ChatSummary = {
|
||||
id: string;
|
||||
title: 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 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 ?? {});
|
||||
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 runCompletion(body: {
|
||||
chatId: string;
|
||||
provider: "openai" | "anthropic" | "xai";
|
||||
model: string;
|
||||
messages: CompletionRequestMessage[];
|
||||
}) {
|
||||
return api<CompletionResponse>("/v1/chat-completions", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
6
web/src/lib/utils.ts
Normal file
6
web/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
Reference in New Issue
Block a user