File size: 3,981 Bytes
b3f89f5
08f0d4c
 
 
35456ff
08f0d4c
e53bdb6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3f89f5
 
 
 
2a4d245
 
b3f89f5
2a4d245
 
b3f89f5
08f0d4c
35456ff
 
08f0d4c
 
 
d72a730
 
 
 
 
 
 
 
 
 
08f0d4c
2a4d245
b3f89f5
2a4d245
b3f89f5
2a4d245
b3f89f5
 
 
2a4d245
 
 
 
b3f89f5
 
2a4d245
 
 
 
b3f89f5
 
2a4d245
 
 
b3f89f5
2a4d245
 
 
b3f89f5
 
 
 
 
 
2a4d245
 
b3f89f5
 
 
 
 
 
 
 
2a4d245
 
 
b3f89f5
 
2a4d245
 
 
b3f89f5
 
 
 
 
2a4d245
 
 
 
b3f89f5
 
2a4d245
 
b3f89f5
2a4d245
 
b3f89f5
 
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import os
import sys

# Add project root to sys.path to allow importing 'src'
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

# CRITICAL: Apply compatibility patches BEFORE any speechbrain imports
import torchaudio
if not hasattr(torchaudio, "list_audio_backends"):
    def _list_audio_backends():
        return ["soundfile"]
    torchaudio.list_audio_backends = _list_audio_backends

import huggingface_hub
_original_hf_hub_download = huggingface_hub.hf_hub_download
def _patched_hf_hub_download(*args, **kwargs):
    if "use_auth_token" in kwargs:
        token_val = kwargs.pop("use_auth_token")
        if "token" not in kwargs:
            kwargs["token"] = token_val
    return _original_hf_hub_download(*args, **kwargs)
huggingface_hub.hf_hub_download = _patched_hf_hub_download

import time
import base64
import traceback
from fastapi import FastAPI, HTTPException, Header, Body
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from pydantic import BaseModel
from dotenv import load_dotenv

# Import the new pipeline
try:
    import src
    print(f"DEBUG: src module found at {src.__file__}")
    from src.pipeline.detector import VoicePipeline
except ImportError as e:
    # Fallback or detailed error logging
    root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
    print(f"Failed to import src.pipeline.detector.")
    print(f"CWD: {os.getcwd()}")
    print(f"Path: {sys.path}")
    print(f"Root dir: {root_path}")
    if os.path.exists(root_path):
        print(f"Contents of root: {os.listdir(root_path)}")
        src_path = os.path.join(root_path, "src")
        if os.path.exists(src_path):
             print(f"Contents of src: {os.listdir(src_path)}")
    raise e

load_dotenv()

app = FastAPI(title="Voice Detector API (Refactored)")

# Initialize Pipeline (Single instance)
# Config path relative to execution root or use absolute
pipeline = VoicePipeline("config/hparams.yaml")

API_KEY = os.getenv("API_KEY", "your-secret-api-key")

class VoiceDetectionRequest(BaseModel):
    language: str = "en"
    audioFormat: str = "mp3"
    audioBase64: str

@app.on_event("startup")
async def startup_event():
    # Warmup if needed
    pass

@app.post("/api/voice-detection")
async def detect_voice(
    x_api_key: str = Header(None),
    request_data: VoiceDetectionRequest = Body(...)
):
    # 1. API Key Validation
    # Allow fallback key for testing if needed
    expected_key = os.getenv("API_KEY", "test_key_123")
    if x_api_key and x_api_key != expected_key and x_api_key != API_KEY:
         raise HTTPException(status_code=403, detail="Invalid API key")

    start_time = time.time()

    try:
        # 2. Decode Audio
        try:
            audio_bytes = base64.b64decode(request_data.audioBase64, validate=True)
        except Exception:
            raise HTTPException(status_code=400, detail="Invalid Base64 string")
            
        # 3. Process via Pipeline
        result = pipeline.process(audio_bytes)
        
        if "error" in result:
             raise HTTPException(status_code=500, detail=result["error"])
        
        # 4. Construct Response
        response_payload = {
            "status": "success",
            "language": request_data.language,
            "classification": result["classification"],
            "confidenceScore": result["confidenceScore"],
            "explanation": result["explanation"],
            "processingTime": f"{time.time() - start_time:.2f}s",
            "details": result.get("details", {})
        }
        
        return JSONResponse(content=response_payload)

    except HTTPException as he:
        raise he
    except Exception as e:
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=f"Internal Error: {str(e)}")

@app.get("/")
def health_check():
    return {"status": "ok", "message": "VoiceGuard API Running (Refactored Structure)"}