"use client" const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000" /** * Enhanced Query Options */ export interface QueryOptions { knowledgeBases?: string[] // kenya_law, kenya_news, parliament, general model?: string // gemini-2.5-flash, gpt-4o-mini, gpt-4o, claude-3.5-sonnet useReranking?: boolean // Enable intelligent re-ranking useHyDE?: boolean // Enable HyDE query expansion temperature?: number // LLM temperature (0-1) userId?: string // User ID for profiling } export interface StreamCallback { onToken?: (token: string) => void onComplete?: (response: unknown) => void onError?: (error: Error) => void } export class ApiClient { private baseURL: string constructor(baseURL: string = API_URL) { this.baseURL = baseURL } private getHeaders(): HeadersInit { const headers: HeadersInit = { "Content-Type": "application/json", } // Add session token if available const sessionToken = localStorage.getItem("session_token") if (sessionToken) { headers["X-Session-Token"] = sessionToken } return headers } private getAuthOnlyHeaders(): HeadersInit { const headers: HeadersInit = {} // Add session token if available (no Content-Type for FormData) const sessionToken = localStorage.getItem("session_token") if (sessionToken) { headers["X-Session-Token"] = sessionToken } return headers } async get(endpoint: string): Promise { const response = await fetch(`${this.baseURL}${endpoint}`, { method: "GET", headers: this.getHeaders(), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: "Request failed" })) throw new Error(error.detail || `HTTP ${response.status}`) } return response.json() } async post(endpoint: string, data?: unknown): Promise { const response = await fetch(`${this.baseURL}${endpoint}`, { method: "POST", headers: this.getHeaders(), body: data ? JSON.stringify(data) : undefined, }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: "Request failed" })) throw new Error(error.detail || `HTTP ${response.status}`) } return response.json() } async put(endpoint: string, data?: unknown): Promise { const response = await fetch(`${this.baseURL}${endpoint}`, { method: "PUT", headers: this.getHeaders(), body: data ? JSON.stringify(data) : undefined, }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: "Request failed" })) throw new Error(error.detail || `HTTP ${response.status}`) } return response.json() } async delete(endpoint: string): Promise { const response = await fetch(`${this.baseURL}${endpoint}`, { method: "DELETE", headers: this.getHeaders(), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: "Request failed" })) throw new Error(error.detail || `HTTP ${response.status}`) } return response.json() } /** * Upload files using FormData (multipart/form-data) * Used for media uploads to /api/v1/media/* endpoints */ async uploadFormData(endpoint: string, formData: FormData): Promise { const response = await fetch(`${this.baseURL}${endpoint}`, { method: "POST", headers: this.getAuthOnlyHeaders(), // No Content-Type - browser sets it with boundary body: formData, }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: "Upload failed" })) throw new Error(error.detail || `HTTP ${response.status}`) } return response.json() } /** * Upload a single file to the media API */ async uploadFile( endpoint: string, file: File, additionalFields?: Record ): Promise { const formData = new FormData() formData.append("file", file) if (additionalFields) { Object.entries(additionalFields).forEach(([key, value]) => { formData.append(key, value) }) } return this.uploadFormData(endpoint, formData) } } export const apiClient = new ApiClient()