import { toast } from "@/components/ui/sonner"; import { storage, STORAGE_KEYS } from "@/lib/storage"; // Define types matching our API export interface SourceMetadata { source?: string; ruling_date?: string; [key: string]: string | undefined; } export interface RetrievedSource { content_snippet: string; metadata?: SourceMetadata; } export interface QueryResponse { answer: string; retrieved_sources?: RetrievedSource[]; } export interface TitleResponse { title: string; } export interface Message { role: "user" | "assistant" | "system"; content: string; } export interface QueryRequest { query: string; chat_history?: Message[]; filters?: Record; regenerate?: boolean; } // Add a delay function for better UX when showing loading states const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); export const apiService = { async getApiUrl(): Promise { const storedUrl = storage.get(STORAGE_KEYS.API_ENDPOINT); if (storedUrl) { return storedUrl; } else { // Set default if not found return null; } }, async queryRulings(request: QueryRequest): Promise { try { // Add a slight delay to make loading states more visible for demo purposes await delay(1000); const API_URL = await this.getApiUrl(); if (!API_URL) { return { answer: "The user hasn't configured an API endpoint yet. I should provide a helpful response.\n\nIt looks like you haven't configured the API endpoint yet. Please go to Settings and enter your API URL to connect to your data source." }; } const response = await fetch(`${API_URL}/query`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(request), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); const errorMessage = errorData.detail || `Error: ${response.status} ${response.statusText}`; toast.error(errorMessage); throw new Error(errorMessage); } const result = await response.json(); // Store sources in storage for later use on sources page if (result.retrieved_sources && result.retrieved_sources.length > 0) { // Get existing sources or initialize empty array const existingSources = storage.get(STORAGE_KEYS.SOURCES) || []; // Merge new sources, avoid duplicates based on content const updatedSources = [...existingSources]; result.retrieved_sources.forEach((source: RetrievedSource) => { const exists = existingSources.some( existing => existing.content_snippet === source.content_snippet ); if (!exists) { updatedSources.push(source); } }); // Store updated sources storage.set(STORAGE_KEYS.SOURCES, updatedSources); } return result; } catch (error) { console.error("Failed to query AI:", error); const errorMessage = error instanceof Error ? error.message : "Failed to get a response"; toast.error(errorMessage); throw error; } }, async generateTitle(query: string): Promise { try { const API_URL = await this.getApiUrl(); // If no API URL is configured, return a generic title if (!API_URL) { return { title: query.slice(0, 30) + (query.length > 30 ? '...' : '') }; } const response = await fetch(`${API_URL}/generate-title`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query }), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); const errorMessage = errorData.detail || `Error: ${response.status} ${response.statusText}`; throw new Error(errorMessage); } return await response.json(); } catch (error) { console.error("Failed to generate title:", error); // Return a default title instead of throwing return { title: "New Chat" }; } } };