// (c) 2026 TWWIM UG. All rights reserved. (www.twwim.com) import { ApiClient } from '../../ApiClient'; import { apiClient } from '../../ApiClient'; import { KNOWLEDGE_PLAYGROUND_ASK, KNOWLEDGE_PLAYGROUND_UPDATE_CHUNK, } from '@archer/api-interface/endpoints/customer-api'; import type { PlaygroundAskRequest, PlaygroundAskResponse, PlaygroundUpdateChunkRequest, PlaygroundUpdateChunkResponse, } from '@archer/api-interface'; /** A Qdrant point returned by the knowledge API (scroll/get). */ export interface KnowledgePoint { id: string; payload: { tenant_id?: string; type?: string; source?: string; external_id?: string; source_id?: string; title?: string; url?: string; categories?: string[]; tags?: string[]; productType?: string; status?: string; priceMin?: number; priceMax?: number; currency?: string; totalInventory?: number | null; variantCount?: number; imageUrl?: string; category?: string; chunk_index?: number; _text?: string; [key: string]: unknown; }; } export interface KnowledgeScrollResult { points: KnowledgePoint[]; nextPageOffset: string | null; total: number; } export interface KnowledgeSearchResult { id: string; score: number; payload: { title: string; content: string; type: string; url: string; category: string; }; } export interface KnowledgeStats { total: number; byType?: Record; } export interface CreateEntryInput { type: 'PRODUCT' | 'PAGE' | 'DOCUMENT' | 'FAQ' | 'CUSTOM'; source?: string; title: string; content: string; url?: string; category?: string; } export interface KnowledgeDocument { id: string; tenantId: string; filename: string; mimeType: string; sizeBytes: number; status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED'; chunkCount: number; errorMessage: string | null; createdAt: string; updatedAt: string; } export interface KnowledgeDocumentList { items: KnowledgeDocument[]; total: number; } class KnowledgeApi { constructor(private client: ApiClient) {} async listEntries( tenantId: string, params?: { type?: string; limit?: number; offset?: string }, ): Promise { const path = `/tenants/${tenantId}/knowledge/entries`; return this.client.get(path, { params }); } async getEntry(tenantId: string, id: string): Promise { return this.client.get(`/tenants/${tenantId}/knowledge/entries/${id}`); } async createEntry(tenantId: string, input: CreateEntryInput): Promise<{ id: string; title: string; type: string }> { return this.client.post(`/tenants/${tenantId}/knowledge/entries`, input); } async deleteEntry(tenantId: string, id: string): Promise { await this.client.delete(`/tenants/${tenantId}/knowledge/entries/${id}`); } async search( tenantId: string, query: string, params?: { type?: string; category?: string; limit?: number }, ): Promise<{ results: KnowledgeSearchResult[] }> { return this.client.post(`/tenants/${tenantId}/knowledge/search`, { query, ...params }); } async getStats(tenantId: string): Promise { return this.client.get(`/tenants/${tenantId}/knowledge/stats`); } // ── Documents ────────────────────────────────────────────────────────── async listDocuments( tenantId: string, params?: { limit?: number; offset?: number }, ): Promise { return this.client.get(`/tenants/${tenantId}/knowledge/documents`, { params }); } async getDocument(tenantId: string, id: string): Promise { return this.client.get(`/tenants/${tenantId}/knowledge/documents/${id}`); } async uploadDocument(tenantId: string, file: File): Promise { const formData = new FormData(); formData.append('file', file); // Let axios set the multipart Content-Type (with boundary) — don't inherit the // application/json default header from ApiClient. return this.client.post(`/tenants/${tenantId}/knowledge/documents`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); } async deleteDocument(tenantId: string, id: string): Promise { await this.client.delete(`/tenants/${tenantId}/knowledge/documents/${id}`); } /** Builds the URL the browser can hit directly with an Authorization header * (via fetch) — the ApiClient handles the auth context. */ getDocumentDownloadUrl(tenantId: string, id: string): string { return `/tenants/${tenantId}/knowledge/documents/${id}/download`; } // ── Playground (knowledge fine-tuning channel) ───────────────────────── async askPlayground(tenantId: string, body: PlaygroundAskRequest): Promise { const path = KNOWLEDGE_PLAYGROUND_ASK.path.replace(':tenantId', tenantId); return this.client.post(path, body); } async updateChunk( tenantId: string, pointId: string, body: PlaygroundUpdateChunkRequest, ): Promise { const path = KNOWLEDGE_PLAYGROUND_UPDATE_CHUNK.path .replace(':tenantId', tenantId) .replace(':pointId', pointId); return this.client.put(path, body); } } export const knowledgeApi = new KnowledgeApi(apiClient);