web / frontend /src /services /apiService.ts
Chandima Prabhath
feat: Add chat components and modals for enhanced user interaction
1904e4c
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<string, any>;
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<string> {
const storedUrl = storage.get<string>(STORAGE_KEYS.API_ENDPOINT);
if (storedUrl) {
return storedUrl;
} else {
// Set default if not found
return null;
}
},
async queryRulings(request: QueryRequest): Promise<QueryResponse> {
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: "<think>The user hasn't configured an API endpoint yet. I should provide a helpful response.</think>\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<RetrievedSource[]>(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<TitleResponse> {
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" };
}
}
};