From 529c37d91ae18a05637901cd3d0acc0379911c61 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 5 Mar 2026 17:24:15 +0100 Subject: [PATCH] chore: diverse Bereinigungen und Fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - admin-compliance: .dockerignore + Dockerfile bereinigt - dsfa-corpus/route.ts + legal-corpus/route.ts entfernt (obsolet) - webhooks/woodpecker/route.ts: minor fix - dsfa/[id]/page.tsx: Refactoring - service_modules.py + README.md: aktualisiert - Migration 028 → 032 umbenannt (legal_documents_extend) - docs: index.md + DEVELOPER.md aktualisiert Co-Authored-By: Claude Sonnet 4.6 --- admin-compliance/.dockerignore | 1 - admin-compliance/Dockerfile | 2 - admin-compliance/app/api/dsfa-corpus/route.ts | 100 ---------- .../app/api/legal-corpus/route.ts | 180 ------------------ .../app/api/webhooks/woodpecker/route.ts | 2 +- admin-compliance/app/sdk/dsfa/[id]/page.tsx | 126 ++++++------ backend-compliance/compliance/data/README.md | 2 +- .../compliance/data/service_modules.py | 6 +- ...end.sql => 032_legal_documents_extend.sql} | 0 docs-src/index.md | 3 +- .../services/ai-compliance-sdk/DEVELOPER.md | 2 +- 11 files changed, 64 insertions(+), 360 deletions(-) delete mode 100644 admin-compliance/app/api/dsfa-corpus/route.ts delete mode 100644 admin-compliance/app/api/legal-corpus/route.ts rename backend-compliance/migrations/{028_legal_documents_extend.sql => 032_legal_documents_extend.sql} (100%) diff --git a/admin-compliance/.dockerignore b/admin-compliance/.dockerignore index bc749cb..7f2853d 100644 --- a/admin-compliance/.dockerignore +++ b/admin-compliance/.dockerignore @@ -20,7 +20,6 @@ edu-search-service school-service voice-service geo-service -klausur-service studio-v2 website scripts diff --git a/admin-compliance/Dockerfile b/admin-compliance/Dockerfile index d9e4328..10fb46a 100644 --- a/admin-compliance/Dockerfile +++ b/admin-compliance/Dockerfile @@ -16,13 +16,11 @@ COPY . . ARG NEXT_PUBLIC_API_URL ARG NEXT_PUBLIC_OLD_ADMIN_URL ARG NEXT_PUBLIC_SDK_URL -ARG NEXT_PUBLIC_KLAUSUR_SERVICE_URL # Set environment variables for build ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL ENV NEXT_PUBLIC_OLD_ADMIN_URL=$NEXT_PUBLIC_OLD_ADMIN_URL ENV NEXT_PUBLIC_SDK_URL=$NEXT_PUBLIC_SDK_URL -ENV NEXT_PUBLIC_KLAUSUR_SERVICE_URL=$NEXT_PUBLIC_KLAUSUR_SERVICE_URL # Build the application RUN npm run build diff --git a/admin-compliance/app/api/dsfa-corpus/route.ts b/admin-compliance/app/api/dsfa-corpus/route.ts deleted file mode 100644 index 2b4d70a..0000000 --- a/admin-compliance/app/api/dsfa-corpus/route.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * DSFA Corpus API Proxy - * - * Proxies requests to klausur-service for DSFA RAG operations. - * Endpoints: /api/v1/dsfa-rag/stats, /api/v1/dsfa-rag/sources - */ - -import { NextRequest, NextResponse } from 'next/server' - -const KLAUSUR_SERVICE_URL = process.env.KLAUSUR_SERVICE_URL || 'http://klausur-service:8086' - -export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url) - const action = searchParams.get('action') - - try { - let url = `${KLAUSUR_SERVICE_URL}/api/v1/dsfa-rag` - - switch (action) { - case 'status': - url += '/stats' - break - case 'sources': - url += '/sources' - break - case 'source-detail': { - const code = searchParams.get('code') - if (!code) { - return NextResponse.json({ error: 'Missing code parameter' }, { status: 400 }) - } - url += `/sources/${encodeURIComponent(code)}` - break - } - default: - return NextResponse.json({ error: 'Unknown action' }, { status: 400 }) - } - - const res = await fetch(url, { - headers: { - 'Content-Type': 'application/json', - }, - cache: 'no-store', - }) - - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } catch (error) { - console.error('DSFA corpus proxy error:', error) - return NextResponse.json( - { error: 'Failed to connect to klausur-service' }, - { status: 503 } - ) - } -} - -export async function POST(request: NextRequest) { - const { searchParams } = new URL(request.url) - const action = searchParams.get('action') - - try { - let url = `${KLAUSUR_SERVICE_URL}/api/v1/dsfa-rag` - - switch (action) { - case 'init': { - url += '/init' - const res = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - case 'ingest': { - const body = await request.json() - const sourceCode = body.source_code - if (!sourceCode) { - return NextResponse.json({ error: 'Missing source_code' }, { status: 400 }) - } - url += `/sources/${encodeURIComponent(sourceCode)}/ingest` - const res = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - default: - return NextResponse.json({ error: 'Unknown action' }, { status: 400 }) - } - } catch (error) { - console.error('DSFA corpus proxy error:', error) - return NextResponse.json( - { error: 'Failed to connect to klausur-service' }, - { status: 503 } - ) - } -} diff --git a/admin-compliance/app/api/legal-corpus/route.ts b/admin-compliance/app/api/legal-corpus/route.ts deleted file mode 100644 index 4d0a2c8..0000000 --- a/admin-compliance/app/api/legal-corpus/route.ts +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Legal Corpus API Proxy - * - * Proxies requests to klausur-service for RAG operations. - * This allows the client-side RAG page to call the API without CORS issues. - */ - -import { NextRequest, NextResponse } from 'next/server' - -const KLAUSUR_SERVICE_URL = process.env.KLAUSUR_SERVICE_URL || 'http://klausur-service:8086' -const QDRANT_URL = process.env.QDRANT_URL || 'http://qdrant:6333' - -export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url) - const action = searchParams.get('action') - - try { - let url = `${KLAUSUR_SERVICE_URL}/api/v1/admin/legal-corpus` - - switch (action) { - case 'status': { - // Query Qdrant directly for collection stats - const qdrantRes = await fetch(`${QDRANT_URL}/collections/bp_legal_corpus`, { - cache: 'no-store', - }) - if (!qdrantRes.ok) { - return NextResponse.json({ error: 'Qdrant not available' }, { status: 503 }) - } - const qdrantData = await qdrantRes.json() - const result = qdrantData.result || {} - return NextResponse.json({ - collection: 'bp_legal_corpus', - totalPoints: result.points_count || 0, - vectorSize: result.config?.params?.vectors?.size || 0, - status: result.status || 'unknown', - regulations: {}, - }) - } - case 'search': - const query = searchParams.get('query') - const topK = searchParams.get('top_k') || '5' - const regulations = searchParams.get('regulations') - url += `/search?query=${encodeURIComponent(query || '')}&top_k=${topK}` - if (regulations) { - url += `®ulations=${encodeURIComponent(regulations)}` - } - break - case 'ingestion-status': - url += '/ingestion-status' - break - case 'regulations': - url += '/regulations' - break - case 'custom-documents': - url += '/custom-documents' - break - case 'pipeline-checkpoints': - url = `${KLAUSUR_SERVICE_URL}/api/v1/admin/pipeline/checkpoints` - break - case 'pipeline-status': - url = `${KLAUSUR_SERVICE_URL}/api/v1/admin/pipeline/status` - break - case 'traceability': { - const chunkId = searchParams.get('chunk_id') - const regulation = searchParams.get('regulation') - url += `/traceability?chunk_id=${encodeURIComponent(chunkId || '')}®ulation=${encodeURIComponent(regulation || '')}` - break - } - default: - return NextResponse.json({ error: 'Unknown action' }, { status: 400 }) - } - - const res = await fetch(url, { - headers: { - 'Content-Type': 'application/json', - }, - cache: 'no-store', - }) - - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } catch (error) { - console.error('Legal corpus proxy error:', error) - return NextResponse.json( - { error: 'Failed to connect to klausur-service' }, - { status: 503 } - ) - } -} - -export async function POST(request: NextRequest) { - const { searchParams } = new URL(request.url) - const action = searchParams.get('action') - - try { - let url = `${KLAUSUR_SERVICE_URL}/api/v1/admin/legal-corpus` - - switch (action) { - case 'ingest': { - url += '/ingest' - const body = await request.json() - const res = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - case 'add-link': { - url += '/add-link' - const body = await request.json() - const res = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - case 'upload': { - url += '/upload' - // Forward FormData directly - const formData = await request.formData() - const res = await fetch(url, { - method: 'POST', - body: formData, - }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - case 'start-pipeline': { - url = `${KLAUSUR_SERVICE_URL}/api/v1/admin/pipeline/start` - const body = await request.json() - const res = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - default: - return NextResponse.json({ error: 'Unknown action' }, { status: 400 }) - } - } catch (error) { - console.error('Legal corpus proxy error:', error) - return NextResponse.json( - { error: 'Failed to connect to klausur-service' }, - { status: 503 } - ) - } -} - -export async function DELETE(request: NextRequest) { - const { searchParams } = new URL(request.url) - const action = searchParams.get('action') - const docId = searchParams.get('docId') - - try { - if (action === 'delete-document' && docId) { - const url = `${KLAUSUR_SERVICE_URL}/api/v1/admin/legal-corpus/custom-documents/${docId}` - const res = await fetch(url, { method: 'DELETE' }) - const data = await res.json() - return NextResponse.json(data, { status: res.status }) - } - - return NextResponse.json({ error: 'Unknown action' }, { status: 400 }) - } catch (error) { - console.error('Legal corpus proxy error:', error) - return NextResponse.json( - { error: 'Failed to connect to klausur-service' }, - { status: 503 } - ) - } -} diff --git a/admin-compliance/app/api/webhooks/woodpecker/route.ts b/admin-compliance/app/api/webhooks/woodpecker/route.ts index c73a93e..218adcb 100644 --- a/admin-compliance/app/api/webhooks/woodpecker/route.ts +++ b/admin-compliance/app/api/webhooks/woodpecker/route.ts @@ -14,7 +14,7 @@ const LOG_EXTRACT_URL = process.env.NEXT_PUBLIC_APP_URL : 'http://localhost:3002/api/infrastructure/log-extract/extract' // Test service API URL for backlog insertion -const TEST_SERVICE_URL = process.env.TEST_SERVICE_URL || 'http://localhost:8086' +const TEST_SERVICE_URL = process.env.TEST_SERVICE_URL || 'http://localhost:8002' // ============================================================================= // Helper Functions diff --git a/admin-compliance/app/sdk/dsfa/[id]/page.tsx b/admin-compliance/app/sdk/dsfa/[id]/page.tsx index 9451bbe..dc18008 100644 --- a/admin-compliance/app/sdk/dsfa/[id]/page.tsx +++ b/admin-compliance/app/sdk/dsfa/[id]/page.tsx @@ -53,8 +53,6 @@ import { ReviewScheduleSection, AIUseCaseSection, } from '@/components/sdk/dsfa' -import { SourceAttribution } from '@/components/sdk/dsfa/SourceAttribution' -import type { DSFALicenseCode, SourceAttributionProps } from '@/lib/sdk/types' // ============================================================================= // SECTION EDITORS @@ -967,30 +965,21 @@ function SDMCoverageOverview({ dsfa }: { dsfa: DSFA }) { // RAG SEARCH PANEL // ============================================================================= -const RAG_API_BASE = process.env.NEXT_PUBLIC_KLAUSUR_SERVICE_URL || 'http://localhost:8086' - interface RAGSearchResult { - chunk_id: string - content: string - score: number - source_code: string - source_name: string - attribution_text: string - license_code: string - license_name: string - license_url?: string + text: string + regulation_code: string + regulation_name: string + regulation_short: string + category: string + article?: string source_url?: string - document_type?: string - category?: string - section_title?: string + score: number } interface RAGSearchResponse { query: string results: RAGSearchResult[] - total_results: number - licenses_used: string[] - attribution_notice: string + count: number } function RAGSearchPanel({ @@ -1023,12 +1012,15 @@ function RAGSearchPanel({ setError(null) try { - const params = new URLSearchParams({ query: searchQuery, limit: '5' }) - if (categories?.length) { - categories.forEach(c => params.append('categories', c)) - } - - const response = await fetch(`${RAG_API_BASE}/api/v1/dsfa-rag/search?${params}`) + const response = await fetch('/api/sdk/v1/rag/search', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: searchQuery, + collection: 'bp_dsfa_corpus', + top_k: 5, + }), + }) if (!response.ok) throw new Error(`Suche fehlgeschlagen (${response.status})`) const data: RAGSearchResponse = await response.json() setResults(data) @@ -1040,25 +1032,16 @@ function RAGSearchPanel({ } } - const handleInsert = (text: string, chunkId: string) => { + const handleInsert = (text: string, resultId: string) => { if (onInsertText) { onInsertText(text) } else { navigator.clipboard.writeText(text) } - setCopiedId(chunkId) + setCopiedId(resultId) setTimeout(() => setCopiedId(null), 2000) } - const sourcesForAttribution: SourceAttributionProps['sources'] = (results?.results || []).map(r => ({ - sourceCode: r.source_code, - sourceName: r.source_name, - attributionText: r.attribution_text, - licenseCode: r.license_code as DSFALicenseCode, - sourceUrl: r.source_url, - score: r.score, - })) - if (!isOpen) { return ( - - - ))} - - {/* Source Attribution */} - + ) + })} )} diff --git a/backend-compliance/compliance/data/README.md b/backend-compliance/compliance/data/README.md index b4282c5..843352d 100644 --- a/backend-compliance/compliance/data/README.md +++ b/backend-compliance/compliance/data/README.md @@ -18,7 +18,7 @@ Diese Dateien enthalten die Seed-Daten für das Compliance-Modul. |-----|--------------|-----------| | `backend` | API/Backend Services | consent-service, python-backend | | `database` | Datenbanken | PostgreSQL, Qdrant, Valkey | -| `ai` | KI/ML Services | klausur-service, embedding-service | +| `ai` | KI/ML Services | embedding-service, ai-compliance-sdk | | `communication` | Chat/Video | Matrix, Jitsi | | `storage` | Speichersysteme | MinIO, DSMS | | `infrastructure` | Infrastruktur | Vault, Mailpit, Backup | diff --git a/backend-compliance/compliance/data/service_modules.py b/backend-compliance/compliance/data/service_modules.py index aba7a8e..171de69 100644 --- a/backend-compliance/compliance/data/service_modules.py +++ b/backend-compliance/compliance/data/service_modules.py @@ -93,7 +93,7 @@ BREAKPILOT_SERVICES: List[Dict[str, Any]] = [ {"code": "DSA", "relevance": LOW, "notes": "Transparenz bei Gebühren"}, ] }, - { + { # Lehrer-Stack (keine Compliance-Runtime-Dependency) "name": "school-service", "display_name": "School Service", "description": "Schulverwaltung, Klassen, Noten und Zeugnisse", @@ -113,7 +113,7 @@ BREAKPILOT_SERVICES: List[Dict[str, Any]] = [ {"code": "BSI-TR-03161-1", "relevance": HIGH, "notes": "Sicherheit für Bildungsanwendungen"}, ] }, - { + { # Lehrer-Stack (keine Compliance-Runtime-Dependency) "name": "calendar-service", "display_name": "Calendar Service", "description": "Kalender, Termine und Stundenplanung", @@ -136,7 +136,7 @@ BREAKPILOT_SERVICES: List[Dict[str, Any]] = [ # ========================================================================= # AI / ML SERVICES # ========================================================================= - { + { # Lehrer-Stack (keine Compliance-Runtime-Dependency) "name": "klausur-service", "display_name": "Klausur Service (AI Correction)", "description": "KI-gestützte Klausurbewertung, PDF-Analyse und Feedback-Generierung", diff --git a/backend-compliance/migrations/028_legal_documents_extend.sql b/backend-compliance/migrations/032_legal_documents_extend.sql similarity index 100% rename from backend-compliance/migrations/028_legal_documents_extend.sql rename to backend-compliance/migrations/032_legal_documents_extend.sql diff --git a/docs-src/index.md b/docs-src/index.md index 25770e8..2b19c86 100644 --- a/docs-src/index.md +++ b/docs-src/index.md @@ -10,7 +10,8 @@ Willkommen zur Dokumentation des **BreakPilot Compliance**-Stacks (Team B: DSGVO | **breakpilot-lehrer** | Bildungs-Stack | Port 8010 | | **breakpilot-compliance** (dieses Projekt) | DSGVO/Compliance-Stack | Port 8011 | -Compliance haengt von Core ab (PostgreSQL, Valkey, Vault, Qdrant, MinIO, Embedding, RAG). +Compliance haengt **ausschliesslich von Core** ab (PostgreSQL, Valkey, Vault, Qdrant, MinIO, Embedding, RAG). +Es gibt **keine Laufzeitabhaengigkeit** zu breakpilot-lehrer. --- diff --git a/docs-src/services/ai-compliance-sdk/DEVELOPER.md b/docs-src/services/ai-compliance-sdk/DEVELOPER.md index ddef101..102dfa9 100644 --- a/docs-src/services/ai-compliance-sdk/DEVELOPER.md +++ b/docs-src/services/ai-compliance-sdk/DEVELOPER.md @@ -323,7 +323,7 @@ context_str = rag.format_for_prompt(results) ```typescript import { queryRAG } from '@/lib/sdk/drafting-engine/rag-query' -// Sucht via klausur-service DSFA-RAG +// Sucht via ai-compliance-sdk RAG (Qdrant) const ragContext = await queryRAG('DSFA Art. 35 DSGVO', 3) // → "[Quelle 1: DSGVO]\nArt. 35 regelt die DSFA..." ```