/** * Knowledge Base Service * Handles all knowledge-related API operations using TanStack Query patterns */ import { invalidateETagCache } from "../../projects/shared/apiWithEtag"; import { callKnowledgeAPI, uploadWithEnhancedErrors } from "./apiWithEnhancedErrors"; import type { ChunksResponse, CodeExamplesResponse, CrawlRequest, CrawlStartResponse, KnowledgeItem, KnowledgeItemsFilter, KnowledgeItemsResponse, KnowledgeSource, RefreshResponse, SearchOptions, SearchResultsResponse, UploadMetadata, } from "../types"; export const knowledgeService = { /** * Get lightweight summaries of knowledge items * Use this for card displays and frequent updates */ async getKnowledgeSummaries(filter?: KnowledgeItemsFilter): Promise { const params = new URLSearchParams(); if (filter?.page) params.append("page", filter.page.toString()); if (filter?.per_page) params.append("per_page", filter.per_page.toString()); if (filter?.knowledge_type) params.append("knowledge_type", filter.knowledge_type); if (filter?.search) params.append("search", filter.search); if (filter?.tags?.length) { for (const tag of filter.tags) { params.append("tags", tag); } } const queryString = params.toString(); const endpoint = `/api/knowledge-items/summary${queryString ? `?${queryString}` : ""}`; return callKnowledgeAPI(endpoint); }, /** * Get a specific knowledge item */ async getKnowledgeItem(sourceId: string): Promise { return callKnowledgeAPI(`/api/knowledge-items/${sourceId}`); }, /** * Delete a knowledge item */ async deleteKnowledgeItem(sourceId: string): Promise<{ success: boolean; message: string }> { const response = await callKnowledgeAPI<{ success: boolean; message: string }>(`/api/knowledge-items/${sourceId}`, { method: "DELETE", }); // Invalidate cache after deletion invalidateETagCache("/api/knowledge-items"); invalidateETagCache("/api/knowledge-items/summary"); invalidateETagCache(`/api/knowledge-items/${sourceId}`); return response; }, /** * Update a knowledge item */ async updateKnowledgeItem(sourceId: string, updates: Partial): Promise { const response = await callKnowledgeAPI(`/api/knowledge-items/${sourceId}`, { method: "PUT", body: JSON.stringify(updates), }); // Invalidate both list and specific item cache invalidateETagCache("/api/knowledge-items"); invalidateETagCache("/api/knowledge-items/summary"); invalidateETagCache(`/api/knowledge-items/${sourceId}`); return response; }, /** * Start crawling a URL */ async crawlUrl(request: CrawlRequest): Promise { const response = await callKnowledgeAPI("/api/knowledge-items/crawl", { method: "POST", body: JSON.stringify(request), }); // Invalidate list cache as new item will be added invalidateETagCache("/api/knowledge-items"); invalidateETagCache("/api/knowledge-items/summary"); return response; }, /** * Refresh an existing knowledge item */ async refreshKnowledgeItem(sourceId: string): Promise { const response = await callKnowledgeAPI(`/api/knowledge-items/${sourceId}/refresh`, { method: "POST", }); // Invalidate caches invalidateETagCache("/api/knowledge-items"); invalidateETagCache("/api/knowledge-items/summary"); invalidateETagCache(`/api/knowledge-items/${sourceId}`); return response; }, /** * Upload a document */ async uploadDocument( file: File, metadata: UploadMetadata, ): Promise<{ success: boolean; progressId: string; message: string; filename: string }> { const formData = new FormData(); formData.append("file", file); if (metadata.knowledge_type) { formData.append("knowledge_type", metadata.knowledge_type); } if (metadata.tags?.length) { formData.append("tags", JSON.stringify(metadata.tags)); } // Use enhanced upload wrapper with OpenAI error handling const result = await uploadWithEnhancedErrors("/documents/upload", formData, 30000); // Invalidate list cache invalidateETagCache("/api/knowledge-items"); invalidateETagCache("/api/knowledge-items/summary"); return result; }, /** * Stop a running crawl */ async stopCrawl(progressId: string): Promise<{ success: boolean; message: string }> { return callKnowledgeAPI<{ success: boolean; message: string }>(`/api/knowledge-items/stop/${progressId}`, { method: "POST", }); }, /** * Get document chunks for a knowledge item with pagination */ async getKnowledgeItemChunks( sourceId: string, options?: { domainFilter?: string; limit?: number; offset?: number; }, ): Promise { const params = new URLSearchParams(); if (options?.domainFilter) { params.append("domain_filter", options.domainFilter); } if (options?.limit !== undefined) { params.append("limit", options.limit.toString()); } if (options?.offset !== undefined) { params.append("offset", options.offset.toString()); } const queryString = params.toString(); const endpoint = `/api/knowledge-items/${sourceId}/chunks${queryString ? `?${queryString}` : ""}`; return callKnowledgeAPI(endpoint); }, /** * Get code examples for a knowledge item with pagination */ async getCodeExamples( sourceId: string, options?: { limit?: number; offset?: number; }, ): Promise { const params = new URLSearchParams(); if (options?.limit !== undefined) { params.append("limit", options.limit.toString()); } if (options?.offset !== undefined) { params.append("offset", options.offset.toString()); } const queryString = params.toString(); const endpoint = `/api/knowledge-items/${sourceId}/code-examples${queryString ? `?${queryString}` : ""}`; return callKnowledgeAPI(endpoint); }, /** * Search the knowledge base */ async searchKnowledgeBase(options: SearchOptions): Promise { return callKnowledgeAPI("/api/knowledge-items/search", { method: "POST", body: JSON.stringify(options), }); }, /** * Get available knowledge sources */ async getKnowledgeSources(): Promise { return callKnowledgeAPI("/api/knowledge-items/sources"); }, };