feat: Alle 5 verbleibenden SDK-Module auf 100% — RAG, Security-Backlog, Quality, Notfallplan, Loeschfristen
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 34s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 17s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 34s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 17s
Paket A — RAG Proxy: - NEU: admin-compliance/app/api/sdk/v1/rag/[[...path]]/route.ts → Proxy zu ai-compliance-sdk:8090, GET+POST, UUID-Validierung - UPDATE: rag/page.tsx — setTimeout Mock → echte API-Calls GET /regulations → dynamische suggestedQuestions POST /search → Qdrant-Ergebnisse mit score, title, reference Paket B — Security-Backlog + Quality: - NEU: migrations/014_security_backlog.sql + 015_quality.sql - NEU: compliance/api/security_backlog_routes.py — CRUD + Stats - NEU: compliance/api/quality_routes.py — Metrics + Tests CRUD + Stats - UPDATE: security-backlog/page.tsx — mockItems → API - UPDATE: quality/page.tsx — mockMetrics/mockTests → API - UPDATE: compliance/api/__init__.py — Router-Registrierung - NEU: tests/test_security_backlog_routes.py (48 Tests — 48/48 bestanden) - NEU: tests/test_quality_routes.py (67 Tests — 67/67 bestanden) Paket C — Notfallplan Incidents + Templates: - NEU: migrations/016_notfallplan_incidents.sql compliance_notfallplan_incidents + compliance_notfallplan_templates - UPDATE: notfallplan_routes.py — GET/POST/PUT/DELETE für /incidents + /templates - UPDATE: notfallplan/page.tsx — Incidents-Tab + Templates-Tab → API - UPDATE: tests/test_notfallplan_routes.py (+76 neue Tests — alle bestanden) Paket D — Loeschfristen localStorage → API: - NEU: migrations/017_loeschfristen.sql (JSONB: legal_holds, storage_locations, ...) - NEU: compliance/api/loeschfristen_routes.py — CRUD + Stats + Status-Update - UPDATE: loeschfristen/page.tsx — vollständige localStorage → API Migration createNewPolicy → POST (API-UUID als id), deletePolicy → DELETE, handleSaveAndClose → PUT, adoptGeneratedPolicies → POST je Policy apiToPolicy() + policyToPayload() Mapper, saving-State für Buttons - NEU: tests/test_loeschfristen_routes.py (58 Tests — alle bestanden) Gesamt: 253 neue Tests, alle bestanden (48 + 67 + 76 + 58 + bestehende) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useSDK } from '@/lib/sdk'
|
||||
|
||||
// =============================================================================
|
||||
@@ -21,47 +21,11 @@ interface Source {
|
||||
relevance: number
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MOCK DATA
|
||||
// =============================================================================
|
||||
|
||||
const mockHistory: ChatMessage[] = [
|
||||
{
|
||||
id: 'msg-1',
|
||||
role: 'user',
|
||||
content: 'Wann ist eine Datenschutz-Folgenabschaetzung nach DSGVO erforderlich?',
|
||||
timestamp: new Date('2024-01-22T10:30:00'),
|
||||
},
|
||||
{
|
||||
id: 'msg-2',
|
||||
role: 'assistant',
|
||||
content: `Nach Art. 35 DSGVO ist eine Datenschutz-Folgenabschaetzung (DSFA) erforderlich, wenn eine Verarbeitung voraussichtlich ein hohes Risiko fuer die Rechte und Freiheiten natuerlicher Personen zur Folge hat.
|
||||
|
||||
Dies ist insbesondere der Fall bei:
|
||||
|
||||
1. **Systematischer und umfassender Bewertung** persoenlicher Aspekte natuerlicher Personen, einschliesslich Profiling, auf deren Grundlage Entscheidungen getroffen werden
|
||||
|
||||
2. **Umfangreicher Verarbeitung besonderer Kategorien** personenbezogener Daten (Art. 9) oder von Daten ueber strafrechtliche Verurteilungen (Art. 10)
|
||||
|
||||
3. **Systematischer umfangreicher Ueberwachung** oeffentlich zugaenglicher Bereiche
|
||||
|
||||
Die Aufsichtsbehoerden haben zudem sogenannte "Blacklists" veroeffentlicht, die weitere Verarbeitungstaetigkeiten benennen, fuer die eine DSFA durchzufuehren ist.`,
|
||||
sources: [
|
||||
{ title: 'Art. 35 DSGVO', reference: 'DSGVO Art. 35 Abs. 1, 3', relevance: 0.95 },
|
||||
{ title: 'Erwaegungsgrund 91', reference: 'DSGVO EG 91', relevance: 0.85 },
|
||||
{ title: 'DSFA-Blacklist DSK', reference: 'DSK Beschluss 2018', relevance: 0.75 },
|
||||
],
|
||||
timestamp: new Date('2024-01-22T10:30:05'),
|
||||
},
|
||||
]
|
||||
|
||||
const suggestedQuestions = [
|
||||
'Was sind die Rechte der Betroffenen nach DSGVO?',
|
||||
'Wie lange betraegt die Meldefrist bei einer Datenpanne?',
|
||||
'Welche Anforderungen stellt der AI Act an Hochrisiko-KI?',
|
||||
'Wann brauche ich einen Auftragsverarbeitungsvertrag?',
|
||||
'Was muss in einer Datenschutzerklaerung stehen?',
|
||||
]
|
||||
interface Regulation {
|
||||
name: string
|
||||
description?: string
|
||||
collection?: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMPONENTS
|
||||
@@ -98,6 +62,9 @@ function MessageBubble({ message }: { message: ChatMessage }) {
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
{source.title}
|
||||
{source.relevance > 0 && (
|
||||
<span className="text-blue-400">({Math.round(source.relevance * 100)}%)</span>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
@@ -116,11 +83,41 @@ function MessageBubble({ message }: { message: ChatMessage }) {
|
||||
// MAIN PAGE
|
||||
// =============================================================================
|
||||
|
||||
const RAG_API = '/api/sdk/v1/rag'
|
||||
|
||||
const DEFAULT_QUESTIONS = [
|
||||
'Was sind die Rechte der Betroffenen nach DSGVO?',
|
||||
'Wie lange betraegt die Meldefrist bei einer Datenpanne?',
|
||||
'Welche Anforderungen stellt der AI Act an Hochrisiko-KI?',
|
||||
'Wann brauche ich einen Auftragsverarbeitungsvertrag?',
|
||||
'Was muss in einer Datenschutzerklaerung stehen?',
|
||||
]
|
||||
|
||||
export default function RAGPage() {
|
||||
const { state } = useSDK()
|
||||
const [messages, setMessages] = useState<ChatMessage[]>(mockHistory)
|
||||
const [messages, setMessages] = useState<ChatMessage[]>([])
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [suggestedQuestions, setSuggestedQuestions] = useState<string[]>(DEFAULT_QUESTIONS)
|
||||
const [apiError, setApiError] = useState<string | null>(null)
|
||||
|
||||
// Load regulations for suggested questions
|
||||
useEffect(() => {
|
||||
fetch(`${RAG_API}/regulations`)
|
||||
.then(res => res.ok ? res.json() : null)
|
||||
.then(data => {
|
||||
if (data && Array.isArray(data.regulations) && data.regulations.length > 0) {
|
||||
const regs: Regulation[] = data.regulations
|
||||
const dynamicQuestions = regs.slice(0, 5).map((r: Regulation) =>
|
||||
`Was sind die wichtigsten Anforderungen aus ${r.name}?`
|
||||
)
|
||||
setSuggestedQuestions(dynamicQuestions.length > 0 ? dynamicQuestions : DEFAULT_QUESTIONS)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// Keep default questions if API unavailable
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
@@ -136,22 +133,53 @@ export default function RAGPage() {
|
||||
setMessages(prev => [...prev, userMessage])
|
||||
setInputValue('')
|
||||
setIsLoading(true)
|
||||
setApiError(null)
|
||||
|
||||
try {
|
||||
const res = await fetch(`${RAG_API}/search`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ query: inputValue, limit: 5 }),
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status}`)
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
|
||||
// Build assistant message from results
|
||||
const results = data.results || []
|
||||
let content = ''
|
||||
const sources: Source[] = []
|
||||
|
||||
if (results.length === 0) {
|
||||
content = 'Zu dieser Frage wurden keine passenden Dokumente gefunden. Bitte formulieren Sie Ihre Frage anders oder waehlen Sie ein spezifischeres Thema.'
|
||||
} else {
|
||||
const snippets = results.map((r: any, i: number) => {
|
||||
const title = r.metadata?.title || r.metadata?.reference || `Dokument ${i + 1}`
|
||||
const ref = r.metadata?.reference || ''
|
||||
sources.push({ title, reference: ref, relevance: r.score || 0 })
|
||||
return `**${title}${ref ? ` (${ref})` : ''}**\n${r.content || ''}`
|
||||
})
|
||||
content = snippets.join('\n\n---\n\n')
|
||||
}
|
||||
|
||||
// Simulate AI response
|
||||
setTimeout(() => {
|
||||
const assistantMessage: ChatMessage = {
|
||||
id: `msg-${Date.now() + 1}`,
|
||||
role: 'assistant',
|
||||
content: 'Dies ist eine Platzhalter-Antwort. In der produktiven Version wird hier die Antwort des Legal RAG Systems angezeigt, das Ihre Frage auf Basis der integrierten Rechtsdokumente beantwortet.',
|
||||
sources: [
|
||||
{ title: 'DSGVO', reference: 'Art. 5', relevance: 0.9 },
|
||||
{ title: 'AI Act', reference: 'Art. 6', relevance: 0.8 },
|
||||
],
|
||||
content,
|
||||
sources,
|
||||
timestamp: new Date(),
|
||||
}
|
||||
setMessages(prev => [...prev, assistantMessage])
|
||||
} catch (err) {
|
||||
setApiError('RAG-Backend nicht erreichbar. Bitte pruefen Sie die Verbindung zum AI Compliance SDK.')
|
||||
// Remove the user message that didn't get a response
|
||||
setMessages(prev => prev.filter(m => m.id !== userMessage.id))
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSuggestedQuestion = (question: string) => {
|
||||
@@ -184,6 +212,23 @@ export default function RAGPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error Box */}
|
||||
{apiError && (
|
||||
<div className="flex-shrink-0 bg-red-50 border border-red-200 rounded-xl p-4 mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
<p className="text-sm text-red-700">{apiError}</p>
|
||||
<button onClick={() => setApiError(null)} className="ml-auto text-red-400 hover:text-red-600">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Chat Area */}
|
||||
<div className="flex-1 overflow-y-auto bg-gray-50 rounded-xl border border-gray-200 p-6 space-y-6">
|
||||
{messages.length === 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user