| """Pydantic request/response models for the API.""" |
|
|
| from typing import Literal, Optional |
|
|
| from pydantic import BaseModel, Field, field_validator |
|
|
|
|
| class PredictRequest(BaseModel): |
| text: str = Field(..., min_length=1, max_length=5000) |
| threshold: float = Field(0.5, ge=0.0, le=1.0) |
|
|
| @field_validator("text") |
| @classmethod |
| def text_not_empty(cls, v: str) -> str: |
| if not v.strip(): |
| raise ValueError("Text cannot be empty") |
| return v.strip() |
|
|
|
|
| class PredictResponse(BaseModel): |
| text: str |
| is_toxic: bool |
| probability: float = Field(..., ge=0.0, le=1.0) |
| status: Literal["Safe", "Toxic"] |
| mode: Literal["binary"] = "binary" |
| labels: list[str] |
| model_used: str |
| latency_ms: float |
|
|
|
|
| class BatchPredictRequest(BaseModel): |
| texts: list[str] = Field(..., min_length=1, max_length=100) |
| threshold: float = Field(0.5, ge=0.0, le=1.0) |
|
|
|
|
| class BatchPredictResponse(BaseModel): |
| results: list[PredictResponse] |
| total: int |
| toxic_count: int |
| latency_ms: float |
|
|
|
|
| class VideoRequest(BaseModel): |
| url: str |
| max_comments: int = Field(15, ge=1, le=200) |
| threshold: float = Field(0.5, ge=0.0, le=1.0) |
|
|
|
|
| class VideoResponse(BaseModel): |
| video_url: str |
| total_fetched: int |
| toxic_count: int |
| toxic_rate: float |
| results: list[PredictResponse] |
| source: Literal["youtube", "demo", "unavailable"] = "demo" |
| reason: Optional[str] = None |
| error: Optional[str] = None |
|
|
|
|
| class ModelStatusEntry(BaseModel): |
| name: str |
| available: bool |
| reason: Optional[str] = None |
| type: str = "unknown" |
|
|
|
|
| class ModelsStatusResponse(BaseModel): |
| models: list[ModelStatusEntry] |
| active: str |
|
|
|
|
| class SelectModelRequest(BaseModel): |
| model_name: str = Field(..., min_length=1) |
|
|
|
|
| class ModelInfo(BaseModel): |
| name: str |
| type: str |
| description: str |
| speed: str |
| accuracy: str |
| uptime_s: float |
| predictions_served: int |
| display_banner: Optional[str] = None |
| train_test_gap_pp: Optional[float] = None |
| recommended_threshold: Optional[float] = None |
|
|
|
|
| class SuggestedVideo(BaseModel): |
| id: str |
| title: str |
| channel_title: str |
| thumbnail_url: str |
| watch_url: str |
| embeddable: bool = True |
|
|
|
|
| class SuggestedVideosResponse(BaseModel): |
| videos: list[SuggestedVideo] |
| max_comments: int |
|
|