SentinelAI / app /api /schemas /responses.py
sajith-0701's picture
initial deployment for HF Spaces
71c1ad2
# app/api/schemas/responses.py
# Pydantic response models for API endpoints
from pydantic import BaseModel, Field
from typing import Optional, Literal, Any
from datetime import datetime
class DecisionDetail(BaseModel):
"""Details of the moderation decision."""
action: Literal["ALLOWED", "WARNING", "BLOCKED"]
reason: str
severity: str
should_alert_parent: bool = False
escalation_notes: Optional[str] = None
class DeepAnalysisDetail(BaseModel):
"""Details from the deep analysis stage (only present for HIGH risk)."""
is_confirmed: bool
severity: str
reasoning: str
categories: list[str] = []
recommended_action: str
confidence: float
clip_scores: dict = {}
class RiskDetail(BaseModel):
"""Breakdown of risk scoring."""
score: float
level: Literal["LOW", "MEDIUM", "HIGH"]
components: dict = {}
repeat_offender: bool = False
class FilterDetail(BaseModel):
"""Fast filter stage output."""
is_flagged: bool
scores: dict[str, float] = {}
max_score: float
max_label: str
categories: list[str] = []
class AnalysisResponse(BaseModel):
"""
Unified response for all /analyze/* endpoints.
This is the primary contract between the AI engine
and the Node.js backend (and any external consumers).
"""
request_id: str = Field(..., description="Unique request identifier")
input_type: Literal["text", "image", "video"]
status: Literal["ALLOWED", "WARNING", "BLOCKED"]
risk_level: Literal["LOW", "MEDIUM", "HIGH"]
risk_score: float = Field(..., ge=0, le=100, description="Composite risk score 0-100")
categories: list[str] = Field(default_factory=list, description="Detected abuse categories")
confidence: float = Field(..., ge=0, le=1, description="Overall confidence 0-1")
# Detailed breakdowns
decision: DecisionDetail
risk_detail: RiskDetail
filter_detail: FilterDetail
deep_analysis: Optional[DeepAnalysisDetail] = None
# Metadata
processing_time_ms: int = Field(..., description="Total processing time in milliseconds")
trace_id: Optional[str] = Field(None, description="LangSmith trace ID for observability")
cached: bool = Field(False, description="Whether this result was served from cache")
model_config = {
"json_schema_extra": {
"examples": [
{
"request_id": "req_abc123",
"input_type": "text",
"status": "WARNING",
"risk_level": "MEDIUM",
"risk_score": 45.2,
"categories": ["insult", "toxic"],
"confidence": 0.82,
"decision": {
"action": "WARNING",
"reason": "Content flagged as potentially harmful",
"severity": "medium",
"should_alert_parent": False,
},
"risk_detail": {
"score": 45.2,
"level": "MEDIUM",
"components": {
"base_score": 42.0,
"multi_category_penalty": 3.2,
"repeat_offender_boost": 0.0,
},
"repeat_offender": False,
},
"filter_detail": {
"is_flagged": True,
"scores": {"toxic": 0.78, "insult": 0.65},
"max_score": 0.78,
"max_label": "toxic",
"categories": ["toxic", "insult"],
},
"deep_analysis": None,
"processing_time_ms": 156,
"trace_id": None,
"cached": False,
}
]
}
}
class HealthResponse(BaseModel):
"""Health check response."""
status: str
version: str
models: dict[str, bool]
services: dict[str, bool]
uptime_seconds: float
class HistoryResponse(BaseModel):
"""Moderation history response."""
user_id: str
total: int
results: list[dict[str, Any]]
class ErrorResponse(BaseModel):
"""Standard error response."""
error: str
detail: str
request_id: Optional[str] = None