File size: 4,093 Bytes
96fd859
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
102
103
"""Pydantic models for API request/response schemas."""

from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Union
from enum import Enum


class PredictionLabel(str, Enum):
    """Possible prediction outcomes."""
    AI_GENERATED = "AI Generated"
    HUMAN = "Human"
    UNCERTAIN = "Uncertain"

class MetricDetail(BaseModel):
    score: int
    confidence: str
    reason: str

class ForensicMetrics(BaseModel):
    voice_naturalness: Union[int, MetricDetail]
    audio_quality: Union[int, MetricDetail]
    characteristics: List[str]
    advanced: Dict[str, str]

class TimelineSegment(BaseModel):
    start: float
    end: float
    label: str
    human_probability: float
    ai_probability: float
class AnalysisResponse(BaseModel):
    """Response schema for the /analyze endpoint."""
    id: str = Field(..., description="Unique analysis ID")
    filename: str = Field(..., description="Original uploaded filename")
    duration_seconds: float = Field(..., description="Audio duration in seconds")
    file_size_bytes: int = Field(..., description="File size in bytes")
    
    # DL Preprocessing Data
    sample_rate: int = Field(..., description="Audio sample rate in Hz")
    channels: int = Field(..., description="Number of audio channels (1 for mono)")
    peak_amplitude: float = Field(..., description="Peak amplitude after normalization")
    waveform: list[float] = Field(..., description="Downsampled waveform array (max 500 points)")
    spectrogram_image: str = Field(..., description="Base64 encoded PNG of the Mel Spectrogram")
    processed_audio_path: str = Field(..., description="Path to the cached processed WAV file")

    # Prediction
    prediction: str = Field(..., description="Prediction label: 'AI Generated', 'Human', or 'Uncertain'")
    confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score between 0 and 1")
    human_probability: float = Field(..., description="Human probability")
    ai_probability: float = Field(..., description="AI probability")
    
    # Forensics
    forensics: ForensicMetrics = Field(..., description="Simplified forensics metrics")
    timeline: List[TimelineSegment] = Field(..., description="1-second chunk analysis")

    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "filename": "suspicious_call.wav",
                    "duration_seconds": 12.5,
                    "file_size_bytes": 1024000,
                    "sample_rate": 16000,
                    "channels": 1,
                    "peak_amplitude": 0.85,
                    "waveform": [0.12, -0.23, 0.45],
                    "spectrogram_image": "data:image/png;base64,iVBORw0KGgo...",
                    "processed_audio_path": "backend/cache/a1b2c3d4_processed.wav",
                    "prediction": "AI Generated",
                    "confidence": 0.92,
                    "human_probability": 0.08,
                    "ai_probability": 0.92,
                    "forensics": {
                        "voice_naturalness": 40,
                        "audio_quality": 85,
                        "speech_stability": 92,
                        "characteristics": ["⚠ Limited voice variation detected"],
                        "advanced": {"Mean Pitch (Hz)": "120.5"}
                    },
                    "timeline": [
                        {"start": 0.0, "end": 1.0, "label": "Suspicious", "human_probability": 0.1, "ai_probability": 0.9}
                    ]
                }
            ]
        }
    }


class HealthResponse(BaseModel):
    """Response schema for the /health endpoint."""
    status: str
    service: str
    version: str
    uptime_seconds: float


class ErrorResponse(BaseModel):
    """Standard error response schema."""
    error: str = Field(..., description="Error type identifier")
    detail: str = Field(..., description="Human-readable error description")
    status_code: int = Field(..., description="HTTP status code")