/** * Compliance Advisor Chat API * * Verbindet das ComplianceAdvisorWidget mit: * 1. Multi-Collection-RAG ueber die ai-compliance-sdk (bge-m3) — siehe advisor-rag * 2. Strukturierten Controls zum erkannten Thema — buildControlsContext * 3. LLM-Kaskade OVH (prod) -> Ollama (Dev) — siehe advisor-llm * * Laenderspezifische Filterung (DE, AT, CH, EU). Streamt die Antwort als Text. */ import { NextRequest, NextResponse } from 'next/server' import { readSoulFile } from '@/lib/sdk/agents/soul-reader' import { buildControlsContext } from '@/lib/sdk/agents/controls-augmentation' import { queryAdvisorRAG } from '@/lib/sdk/agents/advisor-rag' import { streamAdvisorAnswer, type ChatMessage } from '@/lib/sdk/agents/advisor-llm' type Country = 'DE' | 'AT' | 'CH' | 'EU' const FALLBACK_SYSTEM_PROMPT = `# Compliance Advisor Agent ## Identitaet Du bist der BreakPilot Compliance-Berater. Du hilfst Nutzern des AI Compliance SDK, Datenschutz- und Compliance-Fragen in verstaendlicher Sprache zu beantworten. ## Kernprinzipien - Quellenbasiert: Verweise auf DSGVO-Artikel, BDSG-Paragraphen - Verstaendlich: Einfache, praxisnahe Sprache - Ehrlich: Bei Unsicherheit empfehle Rechtsberatung - Deutsch als Hauptsprache` const COUNTRY_LABELS: Record = { DE: 'Deutschland', AT: 'Oesterreich', CH: 'Schweiz', EU: 'EU-weit', } function countryBlock(c: Country): string { const label = COUNTRY_LABELS[c] const nationalLaws = c === 'DE' ? 'BDSG, TDDDG, TKG, UWG' : c === 'AT' ? 'AT DSG, ECG, TKG, KSchG, MedienG' : 'CH DSG, DSV, OR, UWG, FMG' const guidance = c === 'EU' ? 'EU-weiten Fragen: Beziehe dich auf EU-Verordnungen und -Richtlinien' : `${label}: Beziehe nationale Gesetze (${nationalLaws}) mit ein` return `\n\n## Laenderspezifische Auskunft Der Nutzer hat "${label} (${c})" gewaehlt. - Beziehe dich AUSSCHLIESSLICH auf ${c}-Recht + anwendbares EU-Recht - Nenne IMMER explizit das Land in deiner Antwort - Verwende NIEMALS Gesetze eines anderen Landes - Bei ${guidance}` } export async function POST(request: NextRequest) { try { const body = await request.json() const { message, history = [], currentStep = 'default', country } = body if (!message || typeof message !== 'string') { return NextResponse.json({ error: 'Message is required' }, { status: 400 }) } const validCountry = (['DE', 'AT', 'CH', 'EU'] as const).includes(country) ? (country as Country) : undefined // 1. RAG (ai-sdk, bge-m3) + strukturierte Controls zum Thema — beide parallel const [ragContext, controlsContext] = await Promise.all([ queryAdvisorRAG(message), buildControlsContext(message), ]) // 2. System-Prompt zusammenbauen const soulPrompt = await readSoulFile('compliance-advisor') let systemContent = soulPrompt || FALLBACK_SYSTEM_PROMPT if (validCountry) systemContent += countryBlock(validCountry) if (ragContext) { systemContent += `\n\n## Relevanter Kontext aus dem RAG-System\n\nNutze die folgenden Quellen fuer deine Antwort. Verweise in deiner Antwort auf die jeweilige Quelle:\n\n${ragContext}` } if (controlsContext) systemContent += `\n\n${controlsContext}` systemContent += `\n\n## Aktueller SDK-Schritt\nDer Nutzer befindet sich im SDK-Schritt: ${currentStep}` // 3. Nachrichten (History auf die letzten 6 begrenzen) const messages: ChatMessage[] = [ { role: 'system', content: systemContent }, ...history.slice(-6).map((h: { role: string; content: string }) => ({ role: h.role === 'user' ? 'user' : 'assistant', content: h.content, })), { role: 'user', content: message }, ] // 4. LLM-Kaskade -> Plain-Text-Stream const stream = await streamAdvisorAnswer(messages) if (!stream) { return NextResponse.json( { error: 'LLM nicht erreichbar. Weder OVH/LiteLLM noch Ollama haben geantwortet.' }, { status: 502 }, ) } return new NextResponse(stream, { headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-cache', Connection: 'keep-alive', }, }) } catch (error) { console.error('Compliance advisor chat error:', error) return NextResponse.json( { error: 'Verbindung zum LLM fehlgeschlagen.' }, { status: 503 }, ) } }