Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
327
admin-lehrer/ai-compliance-sdk/internal/api/checkpoint.go
Normal file
327
admin-lehrer/ai-compliance-sdk/internal/api/checkpoint.go
Normal file
@@ -0,0 +1,327 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Checkpoint represents a checkpoint definition
|
||||
type Checkpoint struct {
|
||||
ID string `json:"id"`
|
||||
Step string `json:"step"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
BlocksProgress bool `json:"blocksProgress"`
|
||||
RequiresReview string `json:"requiresReview"`
|
||||
AutoValidate bool `json:"autoValidate"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// CheckpointHandler handles checkpoint-related requests
|
||||
type CheckpointHandler struct {
|
||||
checkpoints map[string]Checkpoint
|
||||
}
|
||||
|
||||
// NewCheckpointHandler creates a new checkpoint handler
|
||||
func NewCheckpointHandler() *CheckpointHandler {
|
||||
return &CheckpointHandler{
|
||||
checkpoints: initCheckpoints(),
|
||||
}
|
||||
}
|
||||
|
||||
func initCheckpoints() map[string]Checkpoint {
|
||||
return map[string]Checkpoint{
|
||||
"CP-UC": {
|
||||
ID: "CP-UC",
|
||||
Step: "use-case-workshop",
|
||||
Name: "Use Case Erfassung",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Mindestens ein Use Case muss erfasst sein",
|
||||
},
|
||||
"CP-SCAN": {
|
||||
ID: "CP-SCAN",
|
||||
Step: "screening",
|
||||
Name: "System Screening",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "SBOM und Security Scan müssen abgeschlossen sein",
|
||||
},
|
||||
"CP-MOD": {
|
||||
ID: "CP-MOD",
|
||||
Step: "modules",
|
||||
Name: "Modul-Zuweisung",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Mindestens ein Compliance-Modul muss zugewiesen sein",
|
||||
},
|
||||
"CP-REQ": {
|
||||
ID: "CP-REQ",
|
||||
Step: "requirements",
|
||||
Name: "Anforderungen",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Anforderungen müssen aus Regulierungen abgeleitet sein",
|
||||
},
|
||||
"CP-CTRL": {
|
||||
ID: "CP-CTRL",
|
||||
Step: "controls",
|
||||
Name: "Controls",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Controls müssen den Anforderungen zugeordnet sein",
|
||||
},
|
||||
"CP-EVI": {
|
||||
ID: "CP-EVI",
|
||||
Step: "evidence",
|
||||
Name: "Nachweise",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Nachweise für Controls müssen dokumentiert sein",
|
||||
},
|
||||
"CP-CHK": {
|
||||
ID: "CP-CHK",
|
||||
Step: "audit-checklist",
|
||||
Name: "Audit Checklist",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Prüfliste muss generiert und überprüft sein",
|
||||
},
|
||||
"CP-RISK": {
|
||||
ID: "CP-RISK",
|
||||
Step: "risks",
|
||||
Name: "Risikobewertung",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Kritische Risiken müssen Mitigationsmaßnahmen haben",
|
||||
},
|
||||
"CP-AI": {
|
||||
ID: "CP-AI",
|
||||
Step: "ai-act",
|
||||
Name: "AI Act Klassifizierung",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "LEGAL",
|
||||
AutoValidate: false,
|
||||
Description: "KI-System muss klassifiziert sein",
|
||||
},
|
||||
"CP-OBL": {
|
||||
ID: "CP-OBL",
|
||||
Step: "obligations",
|
||||
Name: "Pflichtenübersicht",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Rechtliche Pflichten müssen identifiziert sein",
|
||||
},
|
||||
"CP-DSFA": {
|
||||
ID: "CP-DSFA",
|
||||
Step: "dsfa",
|
||||
Name: "DSFA",
|
||||
Type: "RECOMMENDED",
|
||||
BlocksProgress: false,
|
||||
RequiresReview: "DSB",
|
||||
AutoValidate: false,
|
||||
Description: "Datenschutz-Folgenabschätzung muss erstellt und genehmigt sein",
|
||||
},
|
||||
"CP-TOM": {
|
||||
ID: "CP-TOM",
|
||||
Step: "tom",
|
||||
Name: "TOMs",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "NONE",
|
||||
AutoValidate: true,
|
||||
Description: "Technische und organisatorische Maßnahmen müssen definiert sein",
|
||||
},
|
||||
"CP-VVT": {
|
||||
ID: "CP-VVT",
|
||||
Step: "vvt",
|
||||
Name: "Verarbeitungsverzeichnis",
|
||||
Type: "REQUIRED",
|
||||
BlocksProgress: true,
|
||||
RequiresReview: "DSB",
|
||||
AutoValidate: false,
|
||||
Description: "Verarbeitungsverzeichnis muss vollständig sein",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetAll returns all checkpoint definitions
|
||||
func (h *CheckpointHandler) GetAll(c *gin.Context) {
|
||||
tenantID := c.Query("tenantId")
|
||||
|
||||
checkpointList := make([]Checkpoint, 0, len(h.checkpoints))
|
||||
for _, cp := range h.checkpoints {
|
||||
checkpointList = append(checkpointList, cp)
|
||||
}
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"tenantId": tenantID,
|
||||
"checkpoints": checkpointList,
|
||||
})
|
||||
}
|
||||
|
||||
// Validate validates a specific checkpoint
|
||||
func (h *CheckpointHandler) Validate(c *gin.Context) {
|
||||
var req struct {
|
||||
TenantID string `json:"tenantId" binding:"required"`
|
||||
CheckpointID string `json:"checkpointId" binding:"required"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
checkpoint, ok := h.checkpoints[req.CheckpointID]
|
||||
if !ok {
|
||||
ErrorResponse(c, http.StatusNotFound, "Checkpoint not found", "CHECKPOINT_NOT_FOUND")
|
||||
return
|
||||
}
|
||||
|
||||
// Perform validation based on checkpoint ID
|
||||
result := h.validateCheckpoint(checkpoint, req.Data)
|
||||
|
||||
SuccessResponse(c, result)
|
||||
}
|
||||
|
||||
func (h *CheckpointHandler) validateCheckpoint(checkpoint Checkpoint, data map[string]interface{}) CheckpointResult {
|
||||
result := CheckpointResult{
|
||||
CheckpointID: checkpoint.ID,
|
||||
Passed: true,
|
||||
ValidatedAt: now(),
|
||||
ValidatedBy: "SYSTEM",
|
||||
Errors: []ValidationError{},
|
||||
Warnings: []ValidationError{},
|
||||
}
|
||||
|
||||
// Validation logic based on checkpoint
|
||||
switch checkpoint.ID {
|
||||
case "CP-UC":
|
||||
useCases, _ := data["useCases"].([]interface{})
|
||||
if len(useCases) == 0 {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "uc-min-count",
|
||||
Field: "useCases",
|
||||
Message: "Mindestens ein Use Case muss erstellt werden",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
}
|
||||
|
||||
case "CP-SCAN":
|
||||
screening, _ := data["screening"].(map[string]interface{})
|
||||
if screening == nil || screening["status"] != "COMPLETED" {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "scan-complete",
|
||||
Field: "screening",
|
||||
Message: "Security Scan muss abgeschlossen sein",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
}
|
||||
|
||||
case "CP-MOD":
|
||||
modules, _ := data["modules"].([]interface{})
|
||||
if len(modules) == 0 {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "mod-min-count",
|
||||
Field: "modules",
|
||||
Message: "Mindestens ein Modul muss zugewiesen werden",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
}
|
||||
|
||||
case "CP-RISK":
|
||||
risks, _ := data["risks"].([]interface{})
|
||||
criticalUnmitigated := 0
|
||||
for _, r := range risks {
|
||||
risk, ok := r.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
severity, _ := risk["severity"].(string)
|
||||
if severity == "CRITICAL" || severity == "HIGH" {
|
||||
mitigations, _ := risk["mitigation"].([]interface{})
|
||||
if len(mitigations) == 0 {
|
||||
criticalUnmitigated++
|
||||
}
|
||||
}
|
||||
}
|
||||
if criticalUnmitigated > 0 {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "critical-risks-mitigated",
|
||||
Field: "risks",
|
||||
Message: "Kritische Risiken ohne Mitigationsmaßnahmen gefunden",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
}
|
||||
|
||||
case "CP-DSFA":
|
||||
dsfa, _ := data["dsfa"].(map[string]interface{})
|
||||
if dsfa == nil {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "dsfa-exists",
|
||||
Field: "dsfa",
|
||||
Message: "DSFA muss erstellt werden",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
} else if dsfa["status"] != "APPROVED" {
|
||||
result.Warnings = append(result.Warnings, ValidationError{
|
||||
RuleID: "dsfa-approved",
|
||||
Field: "dsfa",
|
||||
Message: "DSFA sollte vom DSB genehmigt werden",
|
||||
Severity: "WARNING",
|
||||
})
|
||||
}
|
||||
|
||||
case "CP-TOM":
|
||||
toms, _ := data["toms"].([]interface{})
|
||||
if len(toms) == 0 {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "tom-min-count",
|
||||
Field: "toms",
|
||||
Message: "Mindestens eine TOM muss definiert werden",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
}
|
||||
|
||||
case "CP-VVT":
|
||||
vvt, _ := data["vvt"].([]interface{})
|
||||
if len(vvt) == 0 {
|
||||
result.Passed = false
|
||||
result.Errors = append(result.Errors, ValidationError{
|
||||
RuleID: "vvt-min-count",
|
||||
Field: "vvt",
|
||||
Message: "Mindestens eine Verarbeitungstätigkeit muss dokumentiert werden",
|
||||
Severity: "ERROR",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
365
admin-lehrer/ai-compliance-sdk/internal/api/generate.go
Normal file
365
admin-lehrer/ai-compliance-sdk/internal/api/generate.go
Normal file
@@ -0,0 +1,365 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/llm"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/rag"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GenerateHandler handles document generation requests
|
||||
type GenerateHandler struct {
|
||||
llmService *llm.Service
|
||||
ragService *rag.Service
|
||||
}
|
||||
|
||||
// NewGenerateHandler creates a new generate handler
|
||||
func NewGenerateHandler(llmService *llm.Service, ragService *rag.Service) *GenerateHandler {
|
||||
return &GenerateHandler{
|
||||
llmService: llmService,
|
||||
ragService: ragService,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateDSFA generates a Data Protection Impact Assessment
|
||||
func (h *GenerateHandler) GenerateDSFA(c *gin.Context) {
|
||||
var req GenerateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
// Get RAG context if requested
|
||||
var ragSources []SearchResult
|
||||
if req.UseRAG && h.ragService != nil {
|
||||
query := req.RAGQuery
|
||||
if query == "" {
|
||||
query = "DSFA Datenschutz-Folgenabschätzung Anforderungen"
|
||||
}
|
||||
results, _ := h.ragService.Search(c.Request.Context(), query, 5, "legal_corpus", "regulation:DSGVO")
|
||||
for _, r := range results {
|
||||
ragSources = append(ragSources, SearchResult{
|
||||
ID: r.ID,
|
||||
Content: r.Content,
|
||||
Source: r.Source,
|
||||
Score: r.Score,
|
||||
Metadata: r.Metadata,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Generate DSFA content
|
||||
content, tokensUsed, err := h.llmService.GenerateDSFA(c.Request.Context(), req.Context, ragSources)
|
||||
if err != nil {
|
||||
// Return mock content if LLM fails
|
||||
content = h.getMockDSFA(req.Context)
|
||||
tokensUsed = 0
|
||||
}
|
||||
|
||||
SuccessResponse(c, GenerateResponse{
|
||||
Content: content,
|
||||
GeneratedAt: now(),
|
||||
Model: h.llmService.GetModel(),
|
||||
TokensUsed: tokensUsed,
|
||||
RAGSources: ragSources,
|
||||
Confidence: 0.85,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateTOM generates Technical and Organizational Measures
|
||||
func (h *GenerateHandler) GenerateTOM(c *gin.Context) {
|
||||
var req GenerateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
// Get RAG context if requested
|
||||
var ragSources []SearchResult
|
||||
if req.UseRAG && h.ragService != nil {
|
||||
query := req.RAGQuery
|
||||
if query == "" {
|
||||
query = "technische organisatorische Maßnahmen TOM Datenschutz"
|
||||
}
|
||||
results, _ := h.ragService.Search(c.Request.Context(), query, 5, "legal_corpus", "")
|
||||
for _, r := range results {
|
||||
ragSources = append(ragSources, SearchResult{
|
||||
ID: r.ID,
|
||||
Content: r.Content,
|
||||
Source: r.Source,
|
||||
Score: r.Score,
|
||||
Metadata: r.Metadata,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Generate TOM content
|
||||
content, tokensUsed, err := h.llmService.GenerateTOM(c.Request.Context(), req.Context, ragSources)
|
||||
if err != nil {
|
||||
content = h.getMockTOM(req.Context)
|
||||
tokensUsed = 0
|
||||
}
|
||||
|
||||
SuccessResponse(c, GenerateResponse{
|
||||
Content: content,
|
||||
GeneratedAt: now(),
|
||||
Model: h.llmService.GetModel(),
|
||||
TokensUsed: tokensUsed,
|
||||
RAGSources: ragSources,
|
||||
Confidence: 0.82,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateVVT generates Processing Activity Register
|
||||
func (h *GenerateHandler) GenerateVVT(c *gin.Context) {
|
||||
var req GenerateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
// Get RAG context if requested
|
||||
var ragSources []SearchResult
|
||||
if req.UseRAG && h.ragService != nil {
|
||||
query := req.RAGQuery
|
||||
if query == "" {
|
||||
query = "Verarbeitungsverzeichnis Art. 30 DSGVO"
|
||||
}
|
||||
results, _ := h.ragService.Search(c.Request.Context(), query, 5, "legal_corpus", "regulation:DSGVO")
|
||||
for _, r := range results {
|
||||
ragSources = append(ragSources, SearchResult{
|
||||
ID: r.ID,
|
||||
Content: r.Content,
|
||||
Source: r.Source,
|
||||
Score: r.Score,
|
||||
Metadata: r.Metadata,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Generate VVT content
|
||||
content, tokensUsed, err := h.llmService.GenerateVVT(c.Request.Context(), req.Context, ragSources)
|
||||
if err != nil {
|
||||
content = h.getMockVVT(req.Context)
|
||||
tokensUsed = 0
|
||||
}
|
||||
|
||||
SuccessResponse(c, GenerateResponse{
|
||||
Content: content,
|
||||
GeneratedAt: now(),
|
||||
Model: h.llmService.GetModel(),
|
||||
TokensUsed: tokensUsed,
|
||||
RAGSources: ragSources,
|
||||
Confidence: 0.88,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateGutachten generates an expert opinion/assessment
|
||||
func (h *GenerateHandler) GenerateGutachten(c *gin.Context) {
|
||||
var req GenerateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
// Get RAG context if requested
|
||||
var ragSources []SearchResult
|
||||
if req.UseRAG && h.ragService != nil {
|
||||
query := req.RAGQuery
|
||||
if query == "" {
|
||||
query = "Compliance Bewertung Gutachten"
|
||||
}
|
||||
results, _ := h.ragService.Search(c.Request.Context(), query, 5, "legal_corpus", "")
|
||||
for _, r := range results {
|
||||
ragSources = append(ragSources, SearchResult{
|
||||
ID: r.ID,
|
||||
Content: r.Content,
|
||||
Source: r.Source,
|
||||
Score: r.Score,
|
||||
Metadata: r.Metadata,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Gutachten content
|
||||
content, tokensUsed, err := h.llmService.GenerateGutachten(c.Request.Context(), req.Context, ragSources)
|
||||
if err != nil {
|
||||
content = h.getMockGutachten(req.Context)
|
||||
tokensUsed = 0
|
||||
}
|
||||
|
||||
SuccessResponse(c, GenerateResponse{
|
||||
Content: content,
|
||||
GeneratedAt: now(),
|
||||
Model: h.llmService.GetModel(),
|
||||
TokensUsed: tokensUsed,
|
||||
RAGSources: ragSources,
|
||||
Confidence: 0.80,
|
||||
})
|
||||
}
|
||||
|
||||
// Mock content generators for when LLM is not available
|
||||
func (h *GenerateHandler) getMockDSFA(context map[string]interface{}) string {
|
||||
return `# Datenschutz-Folgenabschätzung (DSFA)
|
||||
|
||||
## 1. Systematische Beschreibung der Verarbeitungsvorgänge
|
||||
|
||||
Die geplante Verarbeitung umfasst die Analyse von Kundendaten mittels KI-gestützter Systeme zur Verbesserung der Servicequalität und Personalisierung von Angeboten.
|
||||
|
||||
### Verarbeitungszwecke:
|
||||
- Kundensegmentierung und Analyse des Nutzerverhaltens
|
||||
- Personalisierte Empfehlungen
|
||||
- Optimierung von Geschäftsprozessen
|
||||
|
||||
### Rechtsgrundlage:
|
||||
- Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse)
|
||||
- Alternativ: Art. 6 Abs. 1 lit. a DSGVO (Einwilligung)
|
||||
|
||||
## 2. Bewertung der Notwendigkeit und Verhältnismäßigkeit
|
||||
|
||||
Die Verarbeitung ist für die genannten Zwecke erforderlich und verhältnismäßig. Alternative Maßnahmen wurden geprüft, jedoch sind diese weniger effektiv.
|
||||
|
||||
## 3. Risikobewertung
|
||||
|
||||
### Identifizierte Risiken:
|
||||
| Risiko | Eintrittswahrscheinlichkeit | Schwere | Maßnahmen |
|
||||
|--------|---------------------------|---------|-----------|
|
||||
| Unbefugter Zugriff | Mittel | Hoch | Verschlüsselung, Zugangskontrolle |
|
||||
| Profilbildung | Hoch | Mittel | Anonymisierung, Einwilligung |
|
||||
| Datenverlust | Niedrig | Hoch | Backup, Redundanz |
|
||||
|
||||
## 4. Maßnahmen zur Risikominderung
|
||||
|
||||
- Implementierung von Verschlüsselung (AES-256)
|
||||
- Strenge Zugriffskontrollen nach dem Least-Privilege-Prinzip
|
||||
- Regelmäßige Datenschutz-Schulungen
|
||||
- Audit-Logging aller Zugriffe
|
||||
|
||||
## 5. Stellungnahme des Datenschutzbeauftragten
|
||||
|
||||
[Hier Stellungnahme einfügen]
|
||||
|
||||
## 6. Dokumentation der Konsultation
|
||||
|
||||
Erstellt am: ${new Date().toISOString()}
|
||||
Status: ENTWURF
|
||||
`
|
||||
}
|
||||
|
||||
func (h *GenerateHandler) getMockTOM(context map[string]interface{}) string {
|
||||
return `# Technische und Organisatorische Maßnahmen (TOMs)
|
||||
|
||||
## 1. Vertraulichkeit (Art. 32 Abs. 1 lit. b DSGVO)
|
||||
|
||||
### 1.1 Zutrittskontrolle
|
||||
- Alarmanlage
|
||||
- Chipkarten-/Transponder-System
|
||||
- Videoüberwachung der Eingänge
|
||||
- Besuchererfassung und -begleitung
|
||||
|
||||
### 1.2 Zugangskontrolle
|
||||
- Passwort-Richtlinie (min. 12 Zeichen, Komplexitätsanforderungen)
|
||||
- Multi-Faktor-Authentifizierung
|
||||
- Automatische Bildschirmsperre
|
||||
- VPN für Remote-Zugriffe
|
||||
|
||||
### 1.3 Zugriffskontrolle
|
||||
- Rollenbasiertes Berechtigungskonzept
|
||||
- Need-to-know-Prinzip
|
||||
- Regelmäßige Überprüfung der Zugriffsrechte
|
||||
- Protokollierung aller Zugriffe
|
||||
|
||||
## 2. Integrität (Art. 32 Abs. 1 lit. b DSGVO)
|
||||
|
||||
### 2.1 Weitergabekontrolle
|
||||
- Transportverschlüsselung (TLS 1.3)
|
||||
- Ende-zu-Ende-Verschlüsselung für sensible Daten
|
||||
- Sichere E-Mail-Kommunikation (S/MIME)
|
||||
|
||||
### 2.2 Eingabekontrolle
|
||||
- Protokollierung aller Datenänderungen
|
||||
- Benutzeridentifikation bei Änderungen
|
||||
- Audit-Trail für alle Transaktionen
|
||||
|
||||
## 3. Verfügbarkeit (Art. 32 Abs. 1 lit. c DSGVO)
|
||||
|
||||
### 3.1 Verfügbarkeitskontrolle
|
||||
- Tägliche Backups
|
||||
- Georedundante Datenspeicherung
|
||||
- USV-Anlage
|
||||
- Notfallplan
|
||||
|
||||
### 3.2 Wiederherstellung
|
||||
- Dokumentierte Wiederherstellungsverfahren
|
||||
- Regelmäßige Backup-Tests
|
||||
- Maximale Wiederherstellungszeit: 4 Stunden
|
||||
|
||||
## 4. Belastbarkeit (Art. 32 Abs. 1 lit. b DSGVO)
|
||||
|
||||
- Lastverteilung
|
||||
- DDoS-Schutz
|
||||
- Skalierbare Infrastruktur
|
||||
`
|
||||
}
|
||||
|
||||
func (h *GenerateHandler) getMockVVT(context map[string]interface{}) string {
|
||||
return `# Verzeichnis der Verarbeitungstätigkeiten (Art. 30 DSGVO)
|
||||
|
||||
## Verarbeitungstätigkeit: Kundenanalyse und Personalisierung
|
||||
|
||||
### Angaben nach Art. 30 Abs. 1 DSGVO:
|
||||
|
||||
| Feld | Inhalt |
|
||||
|------|--------|
|
||||
| **Name des Verantwortlichen** | [Unternehmensname] |
|
||||
| **Kontaktdaten** | [Adresse, E-Mail, Telefon] |
|
||||
| **Datenschutzbeauftragter** | [Name, Kontakt] |
|
||||
| **Zweck der Verarbeitung** | Kundensegmentierung, Personalisierung, Serviceoptimierung |
|
||||
| **Kategorien betroffener Personen** | Kunden, Interessenten |
|
||||
| **Kategorien personenbezogener Daten** | Kontaktdaten, Nutzungsdaten, Transaktionsdaten |
|
||||
| **Kategorien von Empfängern** | Interne Abteilungen, IT-Dienstleister |
|
||||
| **Drittlandtransfer** | Nein / Ja (mit Angabe der Garantien) |
|
||||
| **Löschfristen** | 3 Jahre nach letzter Aktivität |
|
||||
| **TOM-Referenz** | Siehe TOM-Dokument v1.0 |
|
||||
|
||||
### Rechtsgrundlage:
|
||||
Art. 6 Abs. 1 lit. f DSGVO - Berechtigtes Interesse
|
||||
|
||||
### Dokumentation:
|
||||
- Erstellt: ${new Date().toISOString()}
|
||||
- Letzte Aktualisierung: ${new Date().toISOString()}
|
||||
- Version: 1.0
|
||||
`
|
||||
}
|
||||
|
||||
func (h *GenerateHandler) getMockGutachten(context map[string]interface{}) string {
|
||||
return `# Compliance-Gutachten
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Das geprüfte KI-System erfüllt die wesentlichen Anforderungen der DSGVO und des AI Acts. Es wurden jedoch Optimierungspotenziale identifiziert.
|
||||
|
||||
## Prüfungsumfang
|
||||
|
||||
- DSGVO-Konformität
|
||||
- AI Act Compliance
|
||||
- NIS2-Anforderungen
|
||||
|
||||
## Bewertungsergebnis
|
||||
|
||||
| Bereich | Bewertung | Handlungsbedarf |
|
||||
|---------|-----------|-----------------|
|
||||
| Datenschutz | Gut | Gering |
|
||||
| KI-Risikoeinstufung | Erfüllt | Keiner |
|
||||
| Cybersicherheit | Befriedigend | Mittel |
|
||||
|
||||
## Empfehlungen
|
||||
|
||||
1. Verstärkung der Dokumentation
|
||||
2. Regelmäßige Audits einplanen
|
||||
3. Schulungsmaßnahmen erweitern
|
||||
|
||||
Erstellt am: ${new Date().toISOString()}
|
||||
`
|
||||
}
|
||||
182
admin-lehrer/ai-compliance-sdk/internal/api/rag.go
Normal file
182
admin-lehrer/ai-compliance-sdk/internal/api/rag.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/rag"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RAGHandler handles RAG search requests
|
||||
type RAGHandler struct {
|
||||
ragService *rag.Service
|
||||
}
|
||||
|
||||
// NewRAGHandler creates a new RAG handler
|
||||
func NewRAGHandler(ragService *rag.Service) *RAGHandler {
|
||||
return &RAGHandler{
|
||||
ragService: ragService,
|
||||
}
|
||||
}
|
||||
|
||||
// Search performs semantic search on the legal corpus
|
||||
func (h *RAGHandler) Search(c *gin.Context) {
|
||||
query := c.Query("q")
|
||||
if query == "" {
|
||||
ErrorResponse(c, http.StatusBadRequest, "Query parameter 'q' is required", "MISSING_QUERY")
|
||||
return
|
||||
}
|
||||
|
||||
topK := 5
|
||||
if topKStr := c.Query("top_k"); topKStr != "" {
|
||||
if parsed, err := strconv.Atoi(topKStr); err == nil && parsed > 0 {
|
||||
topK = parsed
|
||||
}
|
||||
}
|
||||
|
||||
collection := c.DefaultQuery("collection", "legal_corpus")
|
||||
filter := c.Query("filter") // e.g., "regulation:DSGVO" or "category:ai_act"
|
||||
|
||||
// Check if RAG service is available
|
||||
if h.ragService == nil {
|
||||
// Return mock data when RAG is not available
|
||||
SuccessResponse(c, gin.H{
|
||||
"query": query,
|
||||
"topK": topK,
|
||||
"results": h.getMockResults(query),
|
||||
"source": "mock",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
results, err := h.ragService.Search(c.Request.Context(), query, topK, collection, filter)
|
||||
if err != nil {
|
||||
ErrorResponse(c, http.StatusInternalServerError, "Search failed: "+err.Error(), "SEARCH_FAILED")
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"query": query,
|
||||
"topK": topK,
|
||||
"results": results,
|
||||
"source": "qdrant",
|
||||
})
|
||||
}
|
||||
|
||||
// GetCorpusStatus returns the status of the legal corpus
|
||||
func (h *RAGHandler) GetCorpusStatus(c *gin.Context) {
|
||||
if h.ragService == nil {
|
||||
SuccessResponse(c, gin.H{
|
||||
"status": "unavailable",
|
||||
"collections": []string{},
|
||||
"documents": 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
status, err := h.ragService.GetCorpusStatus(c.Request.Context())
|
||||
if err != nil {
|
||||
ErrorResponse(c, http.StatusInternalServerError, "Failed to get corpus status", "STATUS_FAILED")
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(c, status)
|
||||
}
|
||||
|
||||
// IndexDocument indexes a new document into the corpus
|
||||
func (h *RAGHandler) IndexDocument(c *gin.Context) {
|
||||
var req struct {
|
||||
Collection string `json:"collection" binding:"required"`
|
||||
ID string `json:"id" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
if h.ragService == nil {
|
||||
ErrorResponse(c, http.StatusServiceUnavailable, "RAG service not available", "SERVICE_UNAVAILABLE")
|
||||
return
|
||||
}
|
||||
|
||||
err := h.ragService.IndexDocument(c.Request.Context(), req.Collection, req.ID, req.Content, req.Metadata)
|
||||
if err != nil {
|
||||
ErrorResponse(c, http.StatusInternalServerError, "Failed to index document: "+err.Error(), "INDEX_FAILED")
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"indexed": true,
|
||||
"id": req.ID,
|
||||
"collection": req.Collection,
|
||||
"indexedAt": now(),
|
||||
})
|
||||
}
|
||||
|
||||
// getMockResults returns mock search results for development
|
||||
func (h *RAGHandler) getMockResults(query string) []SearchResult {
|
||||
// Simplified mock results based on common compliance queries
|
||||
results := []SearchResult{
|
||||
{
|
||||
ID: "dsgvo-art-5",
|
||||
Content: "Art. 5 DSGVO - Grundsätze für die Verarbeitung personenbezogener Daten: Personenbezogene Daten müssen auf rechtmäßige Weise, nach Treu und Glauben und in einer für die betroffene Person nachvollziehbaren Weise verarbeitet werden.",
|
||||
Source: "DSGVO",
|
||||
Score: 0.95,
|
||||
Metadata: map[string]string{
|
||||
"article": "5",
|
||||
"regulation": "DSGVO",
|
||||
"category": "grundsaetze",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "dsgvo-art-6",
|
||||
Content: "Art. 6 DSGVO - Rechtmäßigkeit der Verarbeitung: Die Verarbeitung ist nur rechtmäßig, wenn mindestens eine der folgenden Bedingungen erfüllt ist: Einwilligung, Vertragserfüllung, rechtliche Verpflichtung, lebenswichtige Interessen, öffentliche Aufgabe, berechtigtes Interesse.",
|
||||
Source: "DSGVO",
|
||||
Score: 0.89,
|
||||
Metadata: map[string]string{
|
||||
"article": "6",
|
||||
"regulation": "DSGVO",
|
||||
"category": "rechtsgrundlage",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "ai-act-art-6",
|
||||
Content: "Art. 6 AI Act - Klassifizierungsregeln für Hochrisiko-KI-Systeme: Ein KI-System gilt als Hochrisiko-System, wenn es als Sicherheitskomponente eines Produkts verwendet wird oder selbst ein Produkt ist, das unter die in Anhang II aufgeführten Harmonisierungsrechtsvorschriften fällt.",
|
||||
Source: "AI Act",
|
||||
Score: 0.85,
|
||||
Metadata: map[string]string{
|
||||
"article": "6",
|
||||
"regulation": "AI_ACT",
|
||||
"category": "hochrisiko",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "nis2-art-21",
|
||||
Content: "Art. 21 NIS2 - Risikomanagementmaßnahmen: Wesentliche und wichtige Einrichtungen müssen geeignete und verhältnismäßige technische, operative und organisatorische Maßnahmen ergreifen, um die Risiken für die Sicherheit der Netz- und Informationssysteme zu beherrschen.",
|
||||
Source: "NIS2",
|
||||
Score: 0.78,
|
||||
Metadata: map[string]string{
|
||||
"article": "21",
|
||||
"regulation": "NIS2",
|
||||
"category": "risikomanagement",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "dsgvo-art-35",
|
||||
Content: "Art. 35 DSGVO - Datenschutz-Folgenabschätzung: Hat eine Form der Verarbeitung, insbesondere bei Verwendung neuer Technologien, aufgrund der Art, des Umfangs, der Umstände und der Zwecke der Verarbeitung voraussichtlich ein hohes Risiko für die Rechte und Freiheiten natürlicher Personen zur Folge, so führt der Verantwortliche vorab eine Abschätzung der Folgen der vorgesehenen Verarbeitungsvorgänge für den Schutz personenbezogener Daten durch.",
|
||||
Source: "DSGVO",
|
||||
Score: 0.75,
|
||||
Metadata: map[string]string{
|
||||
"article": "35",
|
||||
"regulation": "DSGVO",
|
||||
"category": "dsfa",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
96
admin-lehrer/ai-compliance-sdk/internal/api/router.go
Normal file
96
admin-lehrer/ai-compliance-sdk/internal/api/router.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Response represents a standard API response
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
// SuccessResponse creates a success response
|
||||
func SuccessResponse(c *gin.Context, data interface{}) {
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Success: true,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorResponse creates an error response
|
||||
func ErrorResponse(c *gin.Context, status int, err string, code string) {
|
||||
c.JSON(status, Response{
|
||||
Success: false,
|
||||
Error: err,
|
||||
Code: code,
|
||||
})
|
||||
}
|
||||
|
||||
// StateData represents state response data
|
||||
type StateData struct {
|
||||
TenantID string `json:"tenantId"`
|
||||
State interface{} `json:"state"`
|
||||
Version int `json:"version"`
|
||||
LastModified string `json:"lastModified"`
|
||||
}
|
||||
|
||||
// ValidationError represents a validation error
|
||||
type ValidationError struct {
|
||||
RuleID string `json:"ruleId"`
|
||||
Field string `json:"field"`
|
||||
Message string `json:"message"`
|
||||
Severity string `json:"severity"`
|
||||
}
|
||||
|
||||
// CheckpointResult represents checkpoint validation result
|
||||
type CheckpointResult struct {
|
||||
CheckpointID string `json:"checkpointId"`
|
||||
Passed bool `json:"passed"`
|
||||
ValidatedAt string `json:"validatedAt"`
|
||||
ValidatedBy string `json:"validatedBy"`
|
||||
Errors []ValidationError `json:"errors"`
|
||||
Warnings []ValidationError `json:"warnings"`
|
||||
}
|
||||
|
||||
// SearchResult represents a RAG search result
|
||||
type SearchResult struct {
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
Source string `json:"source"`
|
||||
Score float64 `json:"score"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
Highlights []string `json:"highlights,omitempty"`
|
||||
}
|
||||
|
||||
// GenerateRequest represents a document generation request
|
||||
type GenerateRequest struct {
|
||||
TenantID string `json:"tenantId" binding:"required"`
|
||||
Context map[string]interface{} `json:"context"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
UseRAG bool `json:"useRag"`
|
||||
RAGQuery string `json:"ragQuery,omitempty"`
|
||||
MaxTokens int `json:"maxTokens,omitempty"`
|
||||
Temperature float64 `json:"temperature,omitempty"`
|
||||
}
|
||||
|
||||
// GenerateResponse represents a document generation response
|
||||
type GenerateResponse struct {
|
||||
Content string `json:"content"`
|
||||
GeneratedAt string `json:"generatedAt"`
|
||||
Model string `json:"model"`
|
||||
TokensUsed int `json:"tokensUsed"`
|
||||
RAGSources []SearchResult `json:"ragSources,omitempty"`
|
||||
Confidence float64 `json:"confidence,omitempty"`
|
||||
}
|
||||
|
||||
// Timestamps helper
|
||||
func now() string {
|
||||
return time.Now().UTC().Format(time.RFC3339)
|
||||
}
|
||||
171
admin-lehrer/ai-compliance-sdk/internal/api/state.go
Normal file
171
admin-lehrer/ai-compliance-sdk/internal/api/state.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/db"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// StateHandler handles state management requests
|
||||
type StateHandler struct {
|
||||
dbPool *db.Pool
|
||||
memStore *db.InMemoryStore
|
||||
}
|
||||
|
||||
// NewStateHandler creates a new state handler
|
||||
func NewStateHandler(dbPool *db.Pool) *StateHandler {
|
||||
return &StateHandler{
|
||||
dbPool: dbPool,
|
||||
memStore: db.NewInMemoryStore(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetState retrieves state for a tenant
|
||||
func (h *StateHandler) GetState(c *gin.Context) {
|
||||
tenantID := c.Param("tenantId")
|
||||
if tenantID == "" {
|
||||
ErrorResponse(c, http.StatusBadRequest, "tenantId is required", "MISSING_TENANT_ID")
|
||||
return
|
||||
}
|
||||
|
||||
var state *db.SDKState
|
||||
var err error
|
||||
|
||||
// Try database first, fall back to in-memory
|
||||
if h.dbPool != nil {
|
||||
state, err = h.dbPool.GetState(c.Request.Context(), tenantID)
|
||||
} else {
|
||||
state, err = h.memStore.GetState(tenantID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ErrorResponse(c, http.StatusNotFound, "State not found", "STATE_NOT_FOUND")
|
||||
return
|
||||
}
|
||||
|
||||
// Generate ETag
|
||||
etag := generateETag(state.Version, state.UpdatedAt.String())
|
||||
|
||||
// Check If-None-Match header
|
||||
if c.GetHeader("If-None-Match") == etag {
|
||||
c.Status(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse state JSON
|
||||
var stateData interface{}
|
||||
if err := json.Unmarshal(state.State, &stateData); err != nil {
|
||||
stateData = state.State
|
||||
}
|
||||
|
||||
c.Header("ETag", etag)
|
||||
c.Header("Last-Modified", state.UpdatedAt.Format("Mon, 02 Jan 2006 15:04:05 GMT"))
|
||||
c.Header("Cache-Control", "private, no-cache")
|
||||
|
||||
SuccessResponse(c, StateData{
|
||||
TenantID: state.TenantID,
|
||||
State: stateData,
|
||||
Version: state.Version,
|
||||
LastModified: state.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
})
|
||||
}
|
||||
|
||||
// SaveState saves state for a tenant
|
||||
func (h *StateHandler) SaveState(c *gin.Context) {
|
||||
var req struct {
|
||||
TenantID string `json:"tenantId" binding:"required"`
|
||||
UserID string `json:"userId"`
|
||||
State json.RawMessage `json:"state" binding:"required"`
|
||||
Version *int `json:"version"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST")
|
||||
return
|
||||
}
|
||||
|
||||
// Check If-Match header for optimistic locking
|
||||
var expectedVersion *int
|
||||
if ifMatch := c.GetHeader("If-Match"); ifMatch != "" {
|
||||
v, err := strconv.Atoi(ifMatch)
|
||||
if err == nil {
|
||||
expectedVersion = &v
|
||||
}
|
||||
} else if req.Version != nil {
|
||||
expectedVersion = req.Version
|
||||
}
|
||||
|
||||
var state *db.SDKState
|
||||
var err error
|
||||
|
||||
// Try database first, fall back to in-memory
|
||||
if h.dbPool != nil {
|
||||
state, err = h.dbPool.SaveState(c.Request.Context(), req.TenantID, req.UserID, req.State, expectedVersion)
|
||||
} else {
|
||||
state, err = h.memStore.SaveState(req.TenantID, req.UserID, req.State, expectedVersion)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "version conflict" {
|
||||
ErrorResponse(c, http.StatusConflict, "Version conflict. State was modified by another request.", "VERSION_CONFLICT")
|
||||
return
|
||||
}
|
||||
ErrorResponse(c, http.StatusInternalServerError, "Failed to save state", "SAVE_FAILED")
|
||||
return
|
||||
}
|
||||
|
||||
// Generate ETag
|
||||
etag := generateETag(state.Version, state.UpdatedAt.String())
|
||||
|
||||
// Parse state JSON
|
||||
var stateData interface{}
|
||||
if err := json.Unmarshal(state.State, &stateData); err != nil {
|
||||
stateData = state.State
|
||||
}
|
||||
|
||||
c.Header("ETag", etag)
|
||||
c.Header("Last-Modified", state.UpdatedAt.Format("Mon, 02 Jan 2006 15:04:05 GMT"))
|
||||
|
||||
SuccessResponse(c, StateData{
|
||||
TenantID: state.TenantID,
|
||||
State: stateData,
|
||||
Version: state.Version,
|
||||
LastModified: state.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteState deletes state for a tenant
|
||||
func (h *StateHandler) DeleteState(c *gin.Context) {
|
||||
tenantID := c.Param("tenantId")
|
||||
if tenantID == "" {
|
||||
ErrorResponse(c, http.StatusBadRequest, "tenantId is required", "MISSING_TENANT_ID")
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Try database first, fall back to in-memory
|
||||
if h.dbPool != nil {
|
||||
err = h.dbPool.DeleteState(c.Request.Context(), tenantID)
|
||||
} else {
|
||||
err = h.memStore.DeleteState(tenantID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ErrorResponse(c, http.StatusInternalServerError, "Failed to delete state", "DELETE_FAILED")
|
||||
return
|
||||
}
|
||||
|
||||
SuccessResponse(c, gin.H{
|
||||
"tenantId": tenantID,
|
||||
"deletedAt": now(),
|
||||
})
|
||||
}
|
||||
|
||||
// generateETag creates an ETag from version and timestamp
|
||||
func generateETag(version int, timestamp string) string {
|
||||
return "\"" + strconv.Itoa(version) + "-" + timestamp[:8] + "\""
|
||||
}
|
||||
173
admin-lehrer/ai-compliance-sdk/internal/db/postgres.go
Normal file
173
admin-lehrer/ai-compliance-sdk/internal/db/postgres.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// Pool wraps a pgxpool.Pool with SDK-specific methods
|
||||
type Pool struct {
|
||||
*pgxpool.Pool
|
||||
}
|
||||
|
||||
// SDKState represents the state stored in the database
|
||||
type SDKState struct {
|
||||
ID string `json:"id"`
|
||||
TenantID string `json:"tenant_id"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
State json.RawMessage `json:"state"`
|
||||
Version int `json:"version"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// NewPostgresPool creates a new database connection pool
|
||||
func NewPostgresPool(connectionString string) (*Pool, error) {
|
||||
config, err := pgxpool.ParseConfig(connectionString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse connection string: %w", err)
|
||||
}
|
||||
|
||||
config.MaxConns = 10
|
||||
config.MinConns = 2
|
||||
config.MaxConnLifetime = 1 * time.Hour
|
||||
config.MaxConnIdleTime = 30 * time.Minute
|
||||
|
||||
pool, err := pgxpool.NewWithConfig(context.Background(), config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create connection pool: %w", err)
|
||||
}
|
||||
|
||||
// Test connection
|
||||
if err := pool.Ping(context.Background()); err != nil {
|
||||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
return &Pool{Pool: pool}, nil
|
||||
}
|
||||
|
||||
// GetState retrieves state for a tenant
|
||||
func (p *Pool) GetState(ctx context.Context, tenantID string) (*SDKState, error) {
|
||||
query := `
|
||||
SELECT id, tenant_id, user_id, state, version, created_at, updated_at
|
||||
FROM sdk_states
|
||||
WHERE tenant_id = $1
|
||||
`
|
||||
|
||||
var state SDKState
|
||||
err := p.QueryRow(ctx, query, tenantID).Scan(
|
||||
&state.ID,
|
||||
&state.TenantID,
|
||||
&state.UserID,
|
||||
&state.State,
|
||||
&state.Version,
|
||||
&state.CreatedAt,
|
||||
&state.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
// SaveState saves or updates state for a tenant with optimistic locking
|
||||
func (p *Pool) SaveState(ctx context.Context, tenantID string, userID string, state json.RawMessage, expectedVersion *int) (*SDKState, error) {
|
||||
query := `
|
||||
INSERT INTO sdk_states (tenant_id, user_id, state, version)
|
||||
VALUES ($1, $2, $3, 1)
|
||||
ON CONFLICT (tenant_id) DO UPDATE SET
|
||||
state = $3,
|
||||
user_id = COALESCE($2, sdk_states.user_id),
|
||||
version = sdk_states.version + 1,
|
||||
updated_at = NOW()
|
||||
WHERE ($4::int IS NULL OR sdk_states.version = $4)
|
||||
RETURNING id, tenant_id, user_id, state, version, created_at, updated_at
|
||||
`
|
||||
|
||||
var result SDKState
|
||||
err := p.QueryRow(ctx, query, tenantID, userID, state, expectedVersion).Scan(
|
||||
&result.ID,
|
||||
&result.TenantID,
|
||||
&result.UserID,
|
||||
&result.State,
|
||||
&result.Version,
|
||||
&result.CreatedAt,
|
||||
&result.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// DeleteState deletes state for a tenant
|
||||
func (p *Pool) DeleteState(ctx context.Context, tenantID string) error {
|
||||
query := `DELETE FROM sdk_states WHERE tenant_id = $1`
|
||||
_, err := p.Exec(ctx, query, tenantID)
|
||||
return err
|
||||
}
|
||||
|
||||
// InMemoryStore provides an in-memory fallback when database is not available
|
||||
type InMemoryStore struct {
|
||||
states map[string]*SDKState
|
||||
}
|
||||
|
||||
// NewInMemoryStore creates a new in-memory store
|
||||
func NewInMemoryStore() *InMemoryStore {
|
||||
return &InMemoryStore{
|
||||
states: make(map[string]*SDKState),
|
||||
}
|
||||
}
|
||||
|
||||
// GetState retrieves state from memory
|
||||
func (s *InMemoryStore) GetState(tenantID string) (*SDKState, error) {
|
||||
state, ok := s.states[tenantID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("state not found")
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// SaveState saves state to memory
|
||||
func (s *InMemoryStore) SaveState(tenantID string, userID string, state json.RawMessage, expectedVersion *int) (*SDKState, error) {
|
||||
existing, exists := s.states[tenantID]
|
||||
|
||||
// Optimistic locking check
|
||||
if expectedVersion != nil && exists && existing.Version != *expectedVersion {
|
||||
return nil, fmt.Errorf("version conflict")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
version := 1
|
||||
createdAt := now
|
||||
|
||||
if exists {
|
||||
version = existing.Version + 1
|
||||
createdAt = existing.CreatedAt
|
||||
}
|
||||
|
||||
newState := &SDKState{
|
||||
ID: fmt.Sprintf("%s-%d", tenantID, time.Now().UnixNano()),
|
||||
TenantID: tenantID,
|
||||
UserID: userID,
|
||||
State: state,
|
||||
Version: version,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
s.states[tenantID] = newState
|
||||
return newState, nil
|
||||
}
|
||||
|
||||
// DeleteState deletes state from memory
|
||||
func (s *InMemoryStore) DeleteState(tenantID string) error {
|
||||
delete(s.states, tenantID)
|
||||
return nil
|
||||
}
|
||||
384
admin-lehrer/ai-compliance-sdk/internal/llm/service.go
Normal file
384
admin-lehrer/ai-compliance-sdk/internal/llm/service.go
Normal file
@@ -0,0 +1,384 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchResult matches the RAG service result structure
|
||||
type SearchResult struct {
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
Source string `json:"source"`
|
||||
Score float64 `json:"score"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Service provides LLM functionality for document generation
|
||||
type Service struct {
|
||||
apiKey string
|
||||
model string
|
||||
}
|
||||
|
||||
// NewService creates a new LLM service
|
||||
func NewService(apiKey string) *Service {
|
||||
model := "claude-3-5-sonnet-20241022"
|
||||
if apiKey == "" {
|
||||
model = "mock"
|
||||
}
|
||||
return &Service{
|
||||
apiKey: apiKey,
|
||||
model: model,
|
||||
}
|
||||
}
|
||||
|
||||
// GetModel returns the current model name
|
||||
func (s *Service) GetModel() string {
|
||||
return s.model
|
||||
}
|
||||
|
||||
// GenerateDSFA generates a Data Protection Impact Assessment
|
||||
func (s *Service) GenerateDSFA(ctx context.Context, context map[string]interface{}, ragSources []SearchResult) (string, int, error) {
|
||||
if s.apiKey == "" {
|
||||
return "", 0, fmt.Errorf("LLM not configured")
|
||||
}
|
||||
|
||||
// Build prompt with context and RAG sources
|
||||
prompt := s.buildDSFAPrompt(context, ragSources)
|
||||
|
||||
// In production, this would call the Anthropic API
|
||||
// response, err := s.callAnthropicAPI(ctx, prompt)
|
||||
// if err != nil {
|
||||
// return "", 0, err
|
||||
// }
|
||||
|
||||
// For now, simulate a response
|
||||
content := s.generateDSFAContent(context, ragSources)
|
||||
tokensUsed := len(strings.Split(content, " ")) * 2 // Rough estimate
|
||||
|
||||
return content, tokensUsed, nil
|
||||
}
|
||||
|
||||
// GenerateTOM generates Technical and Organizational Measures
|
||||
func (s *Service) GenerateTOM(ctx context.Context, context map[string]interface{}, ragSources []SearchResult) (string, int, error) {
|
||||
if s.apiKey == "" {
|
||||
return "", 0, fmt.Errorf("LLM not configured")
|
||||
}
|
||||
|
||||
content := s.generateTOMContent(context, ragSources)
|
||||
tokensUsed := len(strings.Split(content, " ")) * 2
|
||||
|
||||
return content, tokensUsed, nil
|
||||
}
|
||||
|
||||
// GenerateVVT generates a Processing Activity Register
|
||||
func (s *Service) GenerateVVT(ctx context.Context, context map[string]interface{}, ragSources []SearchResult) (string, int, error) {
|
||||
if s.apiKey == "" {
|
||||
return "", 0, fmt.Errorf("LLM not configured")
|
||||
}
|
||||
|
||||
content := s.generateVVTContent(context, ragSources)
|
||||
tokensUsed := len(strings.Split(content, " ")) * 2
|
||||
|
||||
return content, tokensUsed, nil
|
||||
}
|
||||
|
||||
// GenerateGutachten generates an expert opinion/assessment
|
||||
func (s *Service) GenerateGutachten(ctx context.Context, context map[string]interface{}, ragSources []SearchResult) (string, int, error) {
|
||||
if s.apiKey == "" {
|
||||
return "", 0, fmt.Errorf("LLM not configured")
|
||||
}
|
||||
|
||||
content := s.generateGutachtenContent(context, ragSources)
|
||||
tokensUsed := len(strings.Split(content, " ")) * 2
|
||||
|
||||
return content, tokensUsed, nil
|
||||
}
|
||||
|
||||
// buildDSFAPrompt builds the prompt for DSFA generation
|
||||
func (s *Service) buildDSFAPrompt(context map[string]interface{}, ragSources []SearchResult) string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("Du bist ein Datenschutz-Experte und erstellst eine Datenschutz-Folgenabschätzung (DSFA) gemäß Art. 35 DSGVO.\n\n")
|
||||
|
||||
// Add context
|
||||
if useCaseName, ok := context["useCaseName"].(string); ok {
|
||||
sb.WriteString(fmt.Sprintf("Use Case: %s\n", useCaseName))
|
||||
}
|
||||
if description, ok := context["description"].(string); ok {
|
||||
sb.WriteString(fmt.Sprintf("Beschreibung: %s\n", description))
|
||||
}
|
||||
|
||||
// Add RAG context
|
||||
if len(ragSources) > 0 {
|
||||
sb.WriteString("\nRelevante rechtliche Grundlagen:\n")
|
||||
for _, source := range ragSources {
|
||||
sb.WriteString(fmt.Sprintf("- %s (%s)\n", source.Content[:min(200, len(source.Content))], source.Source))
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("\nErstelle eine vollständige DSFA mit allen erforderlichen Abschnitten.")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Content generation functions (would be replaced by actual LLM calls in production)
|
||||
func (s *Service) generateDSFAContent(context map[string]interface{}, ragSources []SearchResult) string {
|
||||
useCaseName := "KI-gestützte Datenverarbeitung"
|
||||
if name, ok := context["useCaseName"].(string); ok {
|
||||
useCaseName = name
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`# Datenschutz-Folgenabschätzung (DSFA)
|
||||
|
||||
## Use Case: %s
|
||||
|
||||
## 1. Systematische Beschreibung der Verarbeitungsvorgänge
|
||||
|
||||
Die geplante Verarbeitung umfasst die Analyse von Daten mittels KI-gestützter Systeme.
|
||||
|
||||
### 1.1 Verarbeitungszwecke
|
||||
- Automatisierte Analyse und Verarbeitung
|
||||
- Optimierung von Geschäftsprozessen
|
||||
- Qualitätssicherung
|
||||
|
||||
### 1.2 Rechtsgrundlage
|
||||
Gemäß Art. 6 Abs. 1 lit. f DSGVO basiert die Verarbeitung auf dem berechtigten Interesse des Verantwortlichen.
|
||||
|
||||
### 1.3 Kategorien verarbeiteter Daten
|
||||
- Nutzungsdaten
|
||||
- Metadaten
|
||||
- Aggregierte Analysedaten
|
||||
|
||||
## 2. Bewertung der Notwendigkeit und Verhältnismäßigkeit
|
||||
|
||||
### 2.1 Notwendigkeit
|
||||
Die Verarbeitung ist erforderlich, um die definierten Geschäftsziele zu erreichen.
|
||||
|
||||
### 2.2 Verhältnismäßigkeit
|
||||
Alternative Methoden wurden geprüft. Die gewählte Verarbeitungsmethode stellt den geringsten Eingriff bei gleichem Nutzen dar.
|
||||
|
||||
## 3. Risikobewertung
|
||||
|
||||
### 3.1 Identifizierte Risiken
|
||||
|
||||
| Risiko | Wahrscheinlichkeit | Schwere | Gesamtbewertung |
|
||||
|--------|-------------------|---------|-----------------|
|
||||
| Unbefugter Zugriff | Mittel | Hoch | HOCH |
|
||||
| Datenverlust | Niedrig | Hoch | MITTEL |
|
||||
| Fehlinterpretation | Mittel | Mittel | MITTEL |
|
||||
|
||||
### 3.2 Maßnahmen zur Risikominderung
|
||||
|
||||
1. **Technische Maßnahmen**
|
||||
- Verschlüsselung (AES-256)
|
||||
- Zugriffskontrollen
|
||||
- Audit-Logging
|
||||
|
||||
2. **Organisatorische Maßnahmen**
|
||||
- Schulungen
|
||||
- Dokumentation
|
||||
- Regelmäßige Überprüfungen
|
||||
|
||||
## 4. Genehmigungsstatus
|
||||
|
||||
| Rolle | Status | Datum |
|
||||
|-------|--------|-------|
|
||||
| Projektleiter | AUSSTEHEND | - |
|
||||
| DSB | AUSSTEHEND | - |
|
||||
| Geschäftsführung | AUSSTEHEND | - |
|
||||
|
||||
---
|
||||
*Generiert mit KI-Unterstützung. Manuelle Überprüfung erforderlich.*
|
||||
`, useCaseName)
|
||||
}
|
||||
|
||||
func (s *Service) generateTOMContent(context map[string]interface{}, ragSources []SearchResult) string {
|
||||
return `# Technische und Organisatorische Maßnahmen (TOMs)
|
||||
|
||||
## 1. Vertraulichkeit (Art. 32 Abs. 1 lit. b DSGVO)
|
||||
|
||||
### 1.1 Zutrittskontrolle
|
||||
- [ ] Alarmanlage installiert
|
||||
- [ ] Chipkarten-System aktiv
|
||||
- [ ] Besucherprotokoll geführt
|
||||
|
||||
### 1.2 Zugangskontrolle
|
||||
- [ ] Starke Passwort-Policy (12+ Zeichen)
|
||||
- [ ] MFA aktiviert
|
||||
- [ ] Automatische Bildschirmsperre
|
||||
|
||||
### 1.3 Zugriffskontrolle
|
||||
- [ ] Rollenbasierte Berechtigungen
|
||||
- [ ] Need-to-know Prinzip
|
||||
- [ ] Quartalsweise Berechtigungsüberprüfung
|
||||
|
||||
## 2. Integrität (Art. 32 Abs. 1 lit. b DSGVO)
|
||||
|
||||
### 2.1 Weitergabekontrolle
|
||||
- [ ] TLS 1.3 für alle Übertragungen
|
||||
- [ ] E-Mail-Verschlüsselung
|
||||
- [ ] Sichere File-Transfer-Protokolle
|
||||
|
||||
### 2.2 Eingabekontrolle
|
||||
- [ ] Vollständiges Audit-Logging
|
||||
- [ ] Benutzeridentifikation bei Änderungen
|
||||
- [ ] Unveränderliche Protokolle
|
||||
|
||||
## 3. Verfügbarkeit (Art. 32 Abs. 1 lit. c DSGVO)
|
||||
|
||||
### 3.1 Verfügbarkeitskontrolle
|
||||
- [ ] Tägliche Backups
|
||||
- [ ] Georedundante Speicherung
|
||||
- [ ] USV-System
|
||||
- [ ] Dokumentierter Notfallplan
|
||||
|
||||
### 3.2 Wiederherstellung
|
||||
- [ ] RPO: 1 Stunde
|
||||
- [ ] RTO: 4 Stunden
|
||||
- [ ] Jährliche Wiederherstellungstests
|
||||
|
||||
## 4. Belastbarkeit
|
||||
|
||||
- [ ] DDoS-Schutz implementiert
|
||||
- [ ] Lastverteilung aktiv
|
||||
- [ ] Skalierbare Infrastruktur
|
||||
|
||||
---
|
||||
*Generiert mit KI-Unterstützung. Manuelle Überprüfung erforderlich.*
|
||||
`
|
||||
}
|
||||
|
||||
func (s *Service) generateVVTContent(context map[string]interface{}, ragSources []SearchResult) string {
|
||||
return `# Verzeichnis der Verarbeitungstätigkeiten (Art. 30 DSGVO)
|
||||
|
||||
## Verarbeitungstätigkeit Nr. 1
|
||||
|
||||
### Stammdaten
|
||||
|
||||
| Feld | Wert |
|
||||
|------|------|
|
||||
| **Bezeichnung** | KI-gestützte Datenanalyse |
|
||||
| **Verantwortlicher** | [Unternehmen] |
|
||||
| **DSB** | [Name, Kontakt] |
|
||||
| **Abteilung** | IT / Data Science |
|
||||
|
||||
### Verarbeitungsdetails
|
||||
|
||||
| Feld | Wert |
|
||||
|------|------|
|
||||
| **Zweck** | Optimierung von Geschäftsprozessen durch KI-Analyse |
|
||||
| **Rechtsgrundlage** | Art. 6 Abs. 1 lit. f DSGVO |
|
||||
| **Betroffene Kategorien** | Kunden, Mitarbeiter, Geschäftspartner |
|
||||
| **Datenkategorien** | Nutzungsdaten, Metadaten, Analyseergebnisse |
|
||||
|
||||
### Empfänger
|
||||
|
||||
| Kategorie | Beispiele |
|
||||
|-----------|-----------|
|
||||
| Intern | IT-Abteilung, Management |
|
||||
| Auftragsverarbeiter | Cloud-Provider (mit AVV) |
|
||||
| Dritte | Keine |
|
||||
|
||||
### Drittlandtransfer
|
||||
|
||||
| Frage | Antwort |
|
||||
|-------|---------|
|
||||
| Übermittlung in Drittländer? | Nein / Ja |
|
||||
| Falls ja, Garantien | [Standardvertragsklauseln / Angemessenheitsbeschluss] |
|
||||
|
||||
### Löschfristen
|
||||
|
||||
| Datenkategorie | Frist | Grundlage |
|
||||
|----------------|-------|-----------|
|
||||
| Nutzungsdaten | 12 Monate | Betriebliche Notwendigkeit |
|
||||
| Analyseergebnisse | 36 Monate | Geschäftszweck |
|
||||
| Audit-Logs | 10 Jahre | Handelsrechtlich |
|
||||
|
||||
### Technisch-Organisatorische Maßnahmen
|
||||
|
||||
Verweis auf TOM-Dokument Version 1.0
|
||||
|
||||
---
|
||||
*Generiert mit KI-Unterstützung. Manuelle Überprüfung erforderlich.*
|
||||
`
|
||||
}
|
||||
|
||||
func (s *Service) generateGutachtenContent(context map[string]interface{}, ragSources []SearchResult) string {
|
||||
return `# Compliance-Gutachten
|
||||
|
||||
## Management Summary
|
||||
|
||||
Das geprüfte System erfüllt die wesentlichen Anforderungen der anwendbaren Regulierungen. Es bestehen Optimierungspotenziale, die priorisiert adressiert werden sollten.
|
||||
|
||||
## 1. Prüfungsumfang
|
||||
|
||||
### 1.1 Geprüfte Regulierungen
|
||||
- DSGVO (EU 2016/679)
|
||||
- AI Act (EU 2024/...)
|
||||
- NIS2 (EU 2022/2555)
|
||||
|
||||
### 1.2 Prüfungsmethodik
|
||||
- Dokumentenprüfung
|
||||
- Technische Analyse
|
||||
- Interviews mit Stakeholdern
|
||||
|
||||
## 2. Ergebnisse
|
||||
|
||||
### 2.1 DSGVO-Konformität
|
||||
|
||||
| Bereich | Bewertung | Handlungsbedarf |
|
||||
|---------|-----------|-----------------|
|
||||
| Rechtmäßigkeit | ✓ Erfüllt | Gering |
|
||||
| Transparenz | ◐ Teilweise | Mittel |
|
||||
| Datensicherheit | ✓ Erfüllt | Gering |
|
||||
| Betroffenenrechte | ◐ Teilweise | Mittel |
|
||||
|
||||
### 2.2 AI Act-Konformität
|
||||
|
||||
| Bereich | Bewertung | Handlungsbedarf |
|
||||
|---------|-----------|-----------------|
|
||||
| Risikoklassifizierung | ✓ Erfüllt | Keiner |
|
||||
| Dokumentation | ◐ Teilweise | Mittel |
|
||||
| Human Oversight | ✓ Erfüllt | Gering |
|
||||
|
||||
### 2.3 NIS2-Konformität
|
||||
|
||||
| Bereich | Bewertung | Handlungsbedarf |
|
||||
|---------|-----------|-----------------|
|
||||
| Risikomanagement | ✓ Erfüllt | Gering |
|
||||
| Incident Reporting | ◐ Teilweise | Hoch |
|
||||
| Supply Chain | ○ Nicht erfüllt | Kritisch |
|
||||
|
||||
## 3. Empfehlungen
|
||||
|
||||
### Kritisch (sofort)
|
||||
1. Supply-Chain-Risikomanagement implementieren
|
||||
2. Incident-Reporting-Prozess etablieren
|
||||
|
||||
### Hoch (< 3 Monate)
|
||||
3. Transparenzdokumentation vervollständigen
|
||||
4. Betroffenenrechte-Portal optimieren
|
||||
|
||||
### Mittel (< 6 Monate)
|
||||
5. AI Act Dokumentation erweitern
|
||||
6. Schulungsmaßnahmen durchführen
|
||||
|
||||
## 4. Fazit
|
||||
|
||||
Das System zeigt einen guten Compliance-Stand mit klar definierten Verbesserungsbereichen. Bei Umsetzung der Empfehlungen ist eine vollständige Konformität erreichbar.
|
||||
|
||||
---
|
||||
*Erstellt: [Datum]*
|
||||
*Gutachter: [Name]*
|
||||
*Version: 1.0*
|
||||
`
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
208
admin-lehrer/ai-compliance-sdk/internal/rag/service.go
Normal file
208
admin-lehrer/ai-compliance-sdk/internal/rag/service.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package rag
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SearchResult represents a search result from the RAG system
|
||||
type SearchResult struct {
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
Source string `json:"source"`
|
||||
Score float64 `json:"score"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// CorpusStatus represents the status of the legal corpus
|
||||
type CorpusStatus struct {
|
||||
Status string `json:"status"`
|
||||
Collections []string `json:"collections"`
|
||||
Documents int `json:"documents"`
|
||||
LastUpdated string `json:"lastUpdated,omitempty"`
|
||||
}
|
||||
|
||||
// Service provides RAG functionality
|
||||
type Service struct {
|
||||
qdrantURL string
|
||||
// client *qdrant.Client // Would be actual Qdrant client in production
|
||||
}
|
||||
|
||||
// NewService creates a new RAG service
|
||||
func NewService(qdrantURL string) (*Service, error) {
|
||||
if qdrantURL == "" {
|
||||
return nil, fmt.Errorf("qdrant URL is required")
|
||||
}
|
||||
|
||||
// In production, this would initialize the Qdrant client
|
||||
// client, err := qdrant.NewClient(qdrantURL)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
qdrantURL: qdrantURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Search performs semantic search on the legal corpus
|
||||
func (s *Service) Search(ctx context.Context, query string, topK int, collection string, filter string) ([]SearchResult, error) {
|
||||
// In production, this would:
|
||||
// 1. Generate embedding for the query using an embedding model (e.g., BGE-M3)
|
||||
// 2. Search Qdrant for similar vectors
|
||||
// 3. Return the results
|
||||
|
||||
// For now, return mock results that simulate a real RAG response
|
||||
results := s.getMockSearchResults(query, topK)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetCorpusStatus returns the status of the legal corpus
|
||||
func (s *Service) GetCorpusStatus(ctx context.Context) (*CorpusStatus, error) {
|
||||
// In production, this would query Qdrant for collection info
|
||||
return &CorpusStatus{
|
||||
Status: "ready",
|
||||
Collections: []string{
|
||||
"legal_corpus",
|
||||
"dsgvo_articles",
|
||||
"ai_act_articles",
|
||||
"nis2_articles",
|
||||
},
|
||||
Documents: 1500,
|
||||
LastUpdated: "2026-02-01T00:00:00Z",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IndexDocument indexes a new document into the corpus
|
||||
func (s *Service) IndexDocument(ctx context.Context, collection string, id string, content string, metadata map[string]string) error {
|
||||
// In production, this would:
|
||||
// 1. Generate embedding for the content
|
||||
// 2. Store in Qdrant with the embedding and metadata
|
||||
return nil
|
||||
}
|
||||
|
||||
// getMockSearchResults returns mock search results for development
|
||||
func (s *Service) getMockSearchResults(query string, topK int) []SearchResult {
|
||||
// Comprehensive mock data for legal searches
|
||||
allResults := []SearchResult{
|
||||
// DSGVO Articles
|
||||
{
|
||||
ID: "dsgvo-art-5",
|
||||
Content: "Art. 5 DSGVO - Grundsätze für die Verarbeitung personenbezogener Daten\n\n(1) Personenbezogene Daten müssen:\na) auf rechtmäßige Weise, nach Treu und Glauben und in einer für die betroffene Person nachvollziehbaren Weise verarbeitet werden („Rechtmäßigkeit, Verarbeitung nach Treu und Glauben, Transparenz");\nb) für festgelegte, eindeutige und legitime Zwecke erhoben werden und dürfen nicht in einer mit diesen Zwecken nicht zu vereinbarenden Weise weiterverarbeitet werden („Zweckbindung");\nc) dem Zweck angemessen und erheblich sowie auf das für die Zwecke der Verarbeitung notwendige Maß beschränkt sein („Datenminimierung");",
|
||||
Source: "DSGVO",
|
||||
Score: 0.95,
|
||||
Metadata: map[string]string{
|
||||
"article": "5",
|
||||
"regulation": "DSGVO",
|
||||
"category": "grundsaetze",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "dsgvo-art-6",
|
||||
Content: "Art. 6 DSGVO - Rechtmäßigkeit der Verarbeitung\n\n(1) Die Verarbeitung ist nur rechtmäßig, wenn mindestens eine der nachstehenden Bedingungen erfüllt ist:\na) Die betroffene Person hat ihre Einwilligung zu der Verarbeitung der sie betreffenden personenbezogenen Daten für einen oder mehrere bestimmte Zwecke gegeben;\nb) die Verarbeitung ist für die Erfüllung eines Vertrags erforderlich;\nc) die Verarbeitung ist zur Erfüllung einer rechtlichen Verpflichtung erforderlich;",
|
||||
Source: "DSGVO",
|
||||
Score: 0.92,
|
||||
Metadata: map[string]string{
|
||||
"article": "6",
|
||||
"regulation": "DSGVO",
|
||||
"category": "rechtsgrundlage",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "dsgvo-art-30",
|
||||
Content: "Art. 30 DSGVO - Verzeichnis von Verarbeitungstätigkeiten\n\n(1) Jeder Verantwortliche und gegebenenfalls sein Vertreter führen ein Verzeichnis aller Verarbeitungstätigkeiten, die ihrer Zuständigkeit unterliegen. Dieses Verzeichnis enthält sämtliche folgenden Angaben:\na) den Namen und die Kontaktdaten des Verantwortlichen;\nb) die Zwecke der Verarbeitung;\nc) eine Beschreibung der Kategorien betroffener Personen und der Kategorien personenbezogener Daten;",
|
||||
Source: "DSGVO",
|
||||
Score: 0.89,
|
||||
Metadata: map[string]string{
|
||||
"article": "30",
|
||||
"regulation": "DSGVO",
|
||||
"category": "dokumentation",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "dsgvo-art-32",
|
||||
Content: "Art. 32 DSGVO - Sicherheit der Verarbeitung\n\n(1) Unter Berücksichtigung des Stands der Technik, der Implementierungskosten und der Art, des Umfangs, der Umstände und der Zwecke der Verarbeitung sowie der unterschiedlichen Eintrittswahrscheinlichkeit und Schwere des Risikos für die Rechte und Freiheiten natürlicher Personen treffen der Verantwortliche und der Auftragsverarbeiter geeignete technische und organisatorische Maßnahmen, um ein dem Risiko angemessenes Schutzniveau zu gewährleisten.",
|
||||
Source: "DSGVO",
|
||||
Score: 0.88,
|
||||
Metadata: map[string]string{
|
||||
"article": "32",
|
||||
"regulation": "DSGVO",
|
||||
"category": "sicherheit",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "dsgvo-art-35",
|
||||
Content: "Art. 35 DSGVO - Datenschutz-Folgenabschätzung\n\n(1) Hat eine Form der Verarbeitung, insbesondere bei Verwendung neuer Technologien, aufgrund der Art, des Umfangs, der Umstände und der Zwecke der Verarbeitung voraussichtlich ein hohes Risiko für die Rechte und Freiheiten natürlicher Personen zur Folge, so führt der Verantwortliche vorab eine Abschätzung der Folgen der vorgesehenen Verarbeitungsvorgänge für den Schutz personenbezogener Daten durch.",
|
||||
Source: "DSGVO",
|
||||
Score: 0.87,
|
||||
Metadata: map[string]string{
|
||||
"article": "35",
|
||||
"regulation": "DSGVO",
|
||||
"category": "dsfa",
|
||||
},
|
||||
},
|
||||
// AI Act Articles
|
||||
{
|
||||
ID: "ai-act-art-6",
|
||||
Content: "Art. 6 AI Act - Klassifizierungsregeln für Hochrisiko-KI-Systeme\n\n(1) Unbeschadet des Absatzes 2 gilt ein KI-System als Hochrisiko-KI-System, wenn es beide der folgenden Bedingungen erfüllt:\na) das KI-System soll als Sicherheitskomponente eines unter die in Anhang II aufgeführten Harmonisierungsrechtsvorschriften der Union fallenden Produkts verwendet werden oder ist selbst ein solches Produkt;\nb) das Produkt, dessen Sicherheitskomponente das KI-System ist, oder das KI-System selbst muss einer Konformitätsbewertung durch Dritte unterzogen werden.",
|
||||
Source: "AI Act",
|
||||
Score: 0.91,
|
||||
Metadata: map[string]string{
|
||||
"article": "6",
|
||||
"regulation": "AI_ACT",
|
||||
"category": "klassifizierung",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "ai-act-art-9",
|
||||
Content: "Art. 9 AI Act - Risikomanagement\n\n(1) Für Hochrisiko-KI-Systeme wird ein Risikomanagementsystem eingerichtet, umgesetzt, dokumentiert und aufrechterhalten. Das Risikomanagementsystem ist ein kontinuierlicher iterativer Prozess, der während des gesamten Lebenszyklus eines Hochrisiko-KI-Systems geplant und durchgeführt wird und einer regelmäßigen systematischen Aktualisierung bedarf.",
|
||||
Source: "AI Act",
|
||||
Score: 0.85,
|
||||
Metadata: map[string]string{
|
||||
"article": "9",
|
||||
"regulation": "AI_ACT",
|
||||
"category": "risikomanagement",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "ai-act-art-52",
|
||||
Content: "Art. 52 AI Act - Transparenzpflichten für bestimmte KI-Systeme\n\n(1) Die Anbieter stellen sicher, dass KI-Systeme, die für die Interaktion mit natürlichen Personen bestimmt sind, so konzipiert und entwickelt werden, dass die betreffenden natürlichen Personen darüber informiert werden, dass sie mit einem KI-System interagieren, es sei denn, dies ist aus den Umständen und dem Nutzungskontext offensichtlich.",
|
||||
Source: "AI Act",
|
||||
Score: 0.83,
|
||||
Metadata: map[string]string{
|
||||
"article": "52",
|
||||
"regulation": "AI_ACT",
|
||||
"category": "transparenz",
|
||||
},
|
||||
},
|
||||
// NIS2 Articles
|
||||
{
|
||||
ID: "nis2-art-21",
|
||||
Content: "Art. 21 NIS2 - Risikomanagementmaßnahmen im Bereich der Cybersicherheit\n\n(1) Die Mitgliedstaaten stellen sicher, dass wesentliche und wichtige Einrichtungen geeignete und verhältnismäßige technische, operative und organisatorische Maßnahmen ergreifen, um die Risiken für die Sicherheit der Netz- und Informationssysteme, die diese Einrichtungen für ihren Betrieb oder die Erbringung ihrer Dienste nutzen, zu beherrschen und die Auswirkungen von Sicherheitsvorfällen auf die Empfänger ihrer Dienste und auf andere Dienste zu verhindern oder möglichst gering zu halten.",
|
||||
Source: "NIS2",
|
||||
Score: 0.86,
|
||||
Metadata: map[string]string{
|
||||
"article": "21",
|
||||
"regulation": "NIS2",
|
||||
"category": "risikomanagement",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "nis2-art-23",
|
||||
Content: "Art. 23 NIS2 - Meldepflichten\n\n(1) Jeder Mitgliedstaat stellt sicher, dass wesentliche und wichtige Einrichtungen jeden Sicherheitsvorfall, der erhebliche Auswirkungen auf die Erbringung ihrer Dienste hat, unverzüglich dem zuständigen CSIRT oder gegebenenfalls der zuständigen Behörde melden.",
|
||||
Source: "NIS2",
|
||||
Score: 0.81,
|
||||
Metadata: map[string]string{
|
||||
"article": "23",
|
||||
"regulation": "NIS2",
|
||||
"category": "meldepflicht",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Return top K results
|
||||
if topK > len(allResults) {
|
||||
topK = len(allResults)
|
||||
}
|
||||
return allResults[:topK]
|
||||
}
|
||||
Reference in New Issue
Block a user