File size: 2,318 Bytes
e317d56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46cc63a
e317d56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0f0ce9b
 
 
 
e317d56
 
 
 
 
 
 
 
46cc63a
 
 
e317d56
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""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