This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

246 lines
6.5 KiB
Python

"""
BreakPilot Compliance SDK - RAG Service
Retrieval-Augmented Generation service for legal document search and Q&A.
"""
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, UploadFile, File
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
import structlog
from rag.search import SearchService
from rag.assistant import AssistantService
from rag.documents import DocumentService
from config import Settings
# Configure logging
structlog.configure(
processors=[
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer()
]
)
logger = structlog.get_logger()
# Load settings
settings = Settings()
# Services
search_service: SearchService = None
assistant_service: AssistantService = None
document_service: DocumentService = None
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan handler."""
global search_service, assistant_service, document_service
logger.info("Starting RAG Service", version="0.0.1")
# Initialize services
search_service = SearchService(settings)
assistant_service = AssistantService(settings)
document_service = DocumentService(settings)
# Initialize vector store with legal corpus
await search_service.initialize()
logger.info("RAG Service ready",
regulations=len(search_service.regulations),
total_chunks=search_service.total_chunks)
yield
logger.info("Shutting down RAG Service")
app = FastAPI(
title="BreakPilot RAG Service",
description="Legal document search and Q&A service",
version="0.0.1",
lifespan=lifespan
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# =============================================================================
# Models
# =============================================================================
class SearchRequest(BaseModel):
query: str
regulation_codes: Optional[List[str]] = None
limit: int = 10
min_score: float = 0.7
class SearchResult(BaseModel):
content: str
regulation_code: str
article: Optional[str] = None
paragraph: Optional[str] = None
score: float
metadata: dict = {}
class SearchResponse(BaseModel):
query: str
results: List[SearchResult]
total: int
class AskRequest(BaseModel):
question: str
context: Optional[str] = None
regulation_codes: Optional[List[str]] = None
include_citations: bool = True
class Citation(BaseModel):
regulation_code: str
article: str
text: str
relevance: float
class AskResponse(BaseModel):
question: str
answer: str
citations: List[Citation]
confidence: float
class RegulationInfo(BaseModel):
code: str
name: str
chunks: int
last_updated: str
# =============================================================================
# Endpoints
# =============================================================================
@app.get("/health")
async def health():
"""Health check endpoint."""
return {
"status": "healthy",
"service": "rag-service",
"version": "0.0.1",
"regulations": len(search_service.regulations) if search_service else 0
}
@app.post("/api/v1/search", response_model=SearchResponse)
async def search(request: SearchRequest):
"""Perform semantic search across legal documents."""
try:
results = await search_service.search(
query=request.query,
regulation_codes=request.regulation_codes,
limit=request.limit,
min_score=request.min_score
)
return SearchResponse(
query=request.query,
results=[SearchResult(**r) for r in results],
total=len(results)
)
except Exception as e:
logger.error("Search failed", error=str(e))
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/v1/ask", response_model=AskResponse)
async def ask(request: AskRequest):
"""Ask a question about legal requirements."""
try:
response = await assistant_service.ask(
question=request.question,
context=request.context,
regulation_codes=request.regulation_codes,
include_citations=request.include_citations
)
return AskResponse(
question=request.question,
answer=response["answer"],
citations=[Citation(**c) for c in response.get("citations", [])],
confidence=response.get("confidence", 0.9)
)
except Exception as e:
logger.error("Ask failed", error=str(e))
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/v1/regulations", response_model=List[RegulationInfo])
async def get_regulations():
"""Get list of available regulations."""
return search_service.get_regulations()
@app.get("/api/v1/regulations/{code}")
async def get_regulation(code: str):
"""Get details of a specific regulation."""
regulation = search_service.get_regulation(code)
if not regulation:
raise HTTPException(status_code=404, detail="Regulation not found")
return regulation
@app.post("/api/v1/documents")
async def upload_document(
file: UploadFile = File(...),
regulation_code: Optional[str] = None
):
"""Upload a custom document for indexing."""
try:
result = await document_service.process_upload(
file=file,
regulation_code=regulation_code
)
return {
"id": result["id"],
"filename": file.filename,
"chunks": result["chunks"],
"status": "INDEXED"
}
except Exception as e:
logger.error("Document upload failed", error=str(e))
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/api/v1/documents/{document_id}")
async def delete_document(document_id: str):
"""Delete a custom document."""
try:
await document_service.delete(document_id)
return {"status": "deleted", "id": document_id}
except Exception as e:
logger.error("Document deletion failed", error=str(e))
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv("PORT", "8082")),
reload=os.getenv("ENVIRONMENT") != "production"
)