import type { CreateInstanceOptions, Instance } from "@/types/instance"; import { handleApiError } from "./errorUtils"; // Adding baseURI as a prefix to support being served behind a subpath // e.g. when llmamctl's `/` is served behind a reverse proxy at `/proxy/...` // the baseURI will be `/proxy/` and the API calls will be made to `/proxy/api/v1/` export const API_BASE = document.baseURI + "api/v1"; // Base API call function with error handling async function apiCall( endpoint: string, options: RequestInit = {}, responseType: "json" | "text" = "json" ): Promise { const url = `${API_BASE}${endpoint}`; // Get auth token from sessionStorage (same as AuthContext) const storedKey = sessionStorage.getItem('llamactl_management_key'); // Prepare headers with auth const headers: Record = { "Content-Type": "application/json", ...(options.headers as Record), }; // Add auth header if available if (storedKey) { headers['Authorization'] = `Bearer ${storedKey}`; } try { const response = await fetch(url, { ...options, headers, }); // Handle errors using centralized error handler await handleApiError(response); // Handle empty responses (like DELETE) if (response.status === 204) { return undefined as T; } // Parse response based on type if (responseType === "text") { const text = await response.text(); return text as T; } else { // Handle empty responses for JSON endpoints const contentLength = response.headers.get('content-length'); if (contentLength === '0') { return {} as T; // Return empty object for empty JSON responses } const data = await response.json() as T; return data; } } catch (error) { if (error instanceof Error) { throw error; } throw new Error("Network error occurred"); } } // Server API functions (moved to llama-cpp backend) export const serverApi = { // GET /backends/llama-cpp/help getHelp: () => apiCall("/backends/llama-cpp/help", {}, "text"), // GET /backends/llama-cpp/version getVersion: () => apiCall("/backends/llama-cpp/version", {}, "text"), // GET /backends/llama-cpp/devices getDevices: () => apiCall("/backends/llama-cpp/devices", {}, "text"), }; // Backend API functions export const backendsApi = { llamaCpp: { // POST /backends/llama-cpp/parse-command parseCommand: (command: string) => apiCall('/backends/llama-cpp/parse-command', { method: 'POST', body: JSON.stringify({ command }), }), }, mlx: { // POST /backends/mlx/parse-command parseCommand: (command: string) => apiCall('/backends/mlx/parse-command', { method: 'POST', body: JSON.stringify({ command }), }), }, vllm: { // POST /backends/vllm/parse-command parseCommand: (command: string) => apiCall('/backends/vllm/parse-command', { method: 'POST', body: JSON.stringify({ command }), }), }, }; // Node API types export interface NodeResponse { address: string; } export type NodesMap = Record; // Node API functions export const nodesApi = { // GET /nodes - returns map of node name to NodeResponse list: () => apiCall("/nodes"), // GET /nodes/{name} get: (name: string) => apiCall(`/nodes/${encodeURIComponent(name)}`), }; // Instance API functions export const instancesApi = { // GET /instances list: () => apiCall("/instances"), // GET /instances/{name} get: (name: string) => apiCall(`/instances/${encodeURIComponent(name)}`), // POST /instances/{name} create: (name: string, options: CreateInstanceOptions) => apiCall(`/instances/${encodeURIComponent(name)}`, { method: "POST", body: JSON.stringify(options), }), // PUT /instances/{name} update: (name: string, options: CreateInstanceOptions) => apiCall(`/instances/${encodeURIComponent(name)}`, { method: "PUT", body: JSON.stringify(options), }), // DELETE /instances/{name} delete: (name: string) => apiCall(`/instances/${encodeURIComponent(name)}`, { method: "DELETE", }), // POST /instances/{name}/start start: (name: string) => apiCall(`/instances/${encodeURIComponent(name)}/start`, { method: "POST", }), // POST /instances/{name}/stop stop: (name: string) => apiCall(`/instances/${encodeURIComponent(name)}/stop`, { method: "POST", }), // POST /instances/{name}/restart restart: (name: string) => apiCall(`/instances/${encodeURIComponent(name)}/restart`, { method: "POST", }), // GET /instances/{name}/logs getLogs: (name: string, lines?: number) => { const params = lines ? `?lines=${lines}` : ""; return apiCall(`/instances/${encodeURIComponent(name)}/logs${params}`, {}, "text"); }, // GET /instances/{name}/proxy/health getHealth: (name: string) => apiCall>(`/instances/${encodeURIComponent(name)}/proxy/health`), };