A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
183 lines
5.8 KiB
Go
183 lines
5.8 KiB
Go
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
|
|
}
|