/** * SDK Backend Client * * Client for communicating with the SDK Backend (Go service) * for RAG search and document generation. */ // ============================================================================= // TYPES // ============================================================================= export interface SearchResult { id: string content: string source: string score: number metadata?: Record highlights?: string[] } export interface SearchResponse { query: string topK: number results: SearchResult[] source: 'qdrant' | 'mock' } export interface CorpusStatus { status: 'ready' | 'unavailable' | 'indexing' collections: string[] documents: number lastUpdated?: string } export interface GenerateRequest { tenantId: string context: Record template?: string language?: 'de' | 'en' useRag?: boolean ragQuery?: string maxTokens?: number temperature?: number } export interface GenerateResponse { content: string generatedAt: string model: string tokensUsed: number ragSources?: SearchResult[] confidence?: number } export interface SDKBackendResponse { success: boolean data?: T error?: string code?: string } // ============================================================================= // CONFIGURATION // ============================================================================= const SDK_BACKEND_URL = process.env.NEXT_PUBLIC_SDK_BACKEND_URL || 'http://localhost:8085' const DEFAULT_TIMEOUT = 60000 // 60 seconds for generation // ============================================================================= // SDK BACKEND CLIENT // ============================================================================= export class SDKBackendClient { private baseUrl: string private timeout: number constructor(options: { baseUrl?: string timeout?: number } = {}) { this.baseUrl = options.baseUrl || SDK_BACKEND_URL this.timeout = options.timeout || DEFAULT_TIMEOUT } // --------------------------------------------------------------------------- // Private Methods // --------------------------------------------------------------------------- private async fetch( path: string, options: RequestInit = {} ): Promise { const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), this.timeout) try { const response = await fetch(`${this.baseUrl}${path}`, { ...options, signal: controller.signal, headers: { 'Content-Type': 'application/json', ...options.headers, }, }) const data = await response.json() as SDKBackendResponse if (!response.ok || !data.success) { throw new Error(data.error || `HTTP ${response.status}`) } return data.data as T } finally { clearTimeout(timeoutId) } } // --------------------------------------------------------------------------- // RAG Search // --------------------------------------------------------------------------- /** * Search the legal corpus using semantic search */ async search( query: string, options: { topK?: number collection?: string filter?: string } = {} ): Promise { const params = new URLSearchParams({ q: query, top_k: String(options.topK || 5), }) if (options.collection) { params.set('collection', options.collection) } if (options.filter) { params.set('filter', options.filter) } return this.fetch(`/sdk/v1/rag/search?${params}`) } /** * Get the status of the legal corpus */ async getCorpusStatus(): Promise { return this.fetch('/sdk/v1/rag/status') } /** * Index a new document into the corpus */ async indexDocument( collection: string, id: string, content: string, metadata?: Record ): Promise<{ indexed: boolean; id: string }> { return this.fetch('/sdk/v1/rag/index', { method: 'POST', body: JSON.stringify({ collection, id, content, metadata, }), }) } // --------------------------------------------------------------------------- // Document Generation // --------------------------------------------------------------------------- /** * Generate a Data Protection Impact Assessment (DSFA) */ async generateDSFA(request: GenerateRequest): Promise { return this.fetch('/sdk/v1/generate/dsfa', { method: 'POST', body: JSON.stringify(request), }) } /** * Generate Technical and Organizational Measures (TOM) */ async generateTOM(request: GenerateRequest): Promise { return this.fetch('/sdk/v1/generate/tom', { method: 'POST', body: JSON.stringify(request), }) } /** * Generate a Processing Activity Register (VVT) */ async generateVVT(request: GenerateRequest): Promise { return this.fetch('/sdk/v1/generate/vvt', { method: 'POST', body: JSON.stringify(request), }) } /** * Generate an expert opinion/assessment (Gutachten) */ async generateGutachten(request: GenerateRequest): Promise { return this.fetch('/sdk/v1/generate/gutachten', { method: 'POST', body: JSON.stringify(request), }) } // --------------------------------------------------------------------------- // Health Check // --------------------------------------------------------------------------- /** * Check if the SDK backend is healthy */ async healthCheck(): Promise<{ status: string timestamp: string services: { database: boolean rag: boolean llm: boolean } }> { try { const response = await fetch(`${this.baseUrl}/health`, { method: 'GET', }) return response.json() } catch { return { status: 'unhealthy', timestamp: new Date().toISOString(), services: { database: false, rag: false, llm: false, }, } } } } // ============================================================================= // SINGLETON INSTANCE // ============================================================================= let clientInstance: SDKBackendClient | null = null export function getSDKBackendClient(): SDKBackendClient { if (!clientInstance) { clientInstance = new SDKBackendClient() } return clientInstance } export function resetSDKBackendClient(): void { clientInstance = null } // ============================================================================= // UTILITY FUNCTIONS // ============================================================================= /** * Check if a query is likely a legal/compliance search */ export function isLegalQuery(query: string): boolean { const legalKeywords = [ 'dsgvo', 'gdpr', 'datenschutz', 'privacy', 'ai act', 'ki-gesetz', 'ai-verordnung', 'nis2', 'cybersicherheit', 'artikel', 'article', 'art.', 'verordnung', 'regulation', 'richtlinie', 'directive', 'gesetz', 'law', 'compliance', 'dsfa', 'dpia', 'folgenabschätzung', 'tom', 'maßnahmen', 'measures', 'vvt', 'verarbeitungsverzeichnis', ] const normalizedQuery = query.toLowerCase() return legalKeywords.some(keyword => normalizedQuery.includes(keyword)) } /** * Extract regulation references from text */ export function extractRegulationReferences(text: string): Array<{ regulation: string article: string fullReference: string }> { const references: Array<{ regulation: string article: string fullReference: string }> = [] // Match patterns like "Art. 5 DSGVO", "Article 6 GDPR", "Art. 35 Abs. 1 DSGVO" const patterns = [ /Art(?:ikel|\.|\s)*(\d+)(?:\s*Abs\.\s*(\d+))?\s*(DSGVO|GDPR|AI\s*Act|NIS2)/gi, /Article\s*(\d+)(?:\s*para(?:graph)?\s*(\d+))?\s*(DSGVO|GDPR|AI\s*Act|NIS2)/gi, ] for (const pattern of patterns) { let match while ((match = pattern.exec(text)) !== null) { references.push({ regulation: match[3].toUpperCase().replace(/\s+/g, ' '), article: match[2] ? `${match[1]} Abs. ${match[2]}` : match[1], fullReference: match[0], }) } } return references }