Krish1440's picture
Upload 4 files
119d4e9 verified
import base64
import os
import contextlib
import logging
from typing import Optional
import uvicorn
from fastapi import FastAPI, Header, HTTPException, Request, BackgroundTasks
from fastapi.responses import JSONResponse
from fastapi.concurrency import run_in_threadpool
from pydantic import BaseModel, Field
# Setup Logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - [AudioShield] - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Import our detection engine
from detect import detector
# load_dotenv() - REMOVED per user request for Render deployment
# LIFESPAN MANAGER (Resolves Cold Start)
@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
# Startup Logic
logger.info("--- Warming up the AI engine... ---")
try:
# Trigger model loading in a threadpool so it doesn't block startup completely if async
# But here we want to block until ready.
# Run a dummy inference to ensure weights are on device
dummy_audio = b'\x00' * 16000 # 1 sec silent
await run_in_threadpool(detector.analyze_audio, dummy_audio, "English")
logger.info("--- AI Engine Ready & Warmed Up! ---")
except Exception as e:
logger.error(f"Warmup failed: {e}")
yield
# Shutdown Logic
logger.info("--- Shutting down AudioShield ---")
app = FastAPI(
title="AudioShield AI: Voice Fraud Detector",
version="2.0",
docs_url="/docs",
lifespan=lifespan
)
# CONFIGURATION
# Default key from problem statement example: sk_test_123456789
VALID_API_KEY = os.getenv("API_KEY", "sk_test_123456789")
# MODELS (Strict Adherence to Spec)
class VoiceDetectionRequest(BaseModel):
language: str = Field(..., description="Language: Tamil, English, Hindi, Malayalam, Telugu")
audioFormat: str = Field(..., pattern="^(?i)mp3$", description="Must be 'mp3'")
audioBase64: str = Field(..., description="Base64 encoded MP3 audio")
class VoiceDetectionResponse(BaseModel):
status: str
language: str
classification: str # AI_GENERATED or HUMAN
confidenceScore: float
explanation: str
# ROUTES
@app.post("/api/voice-detection", response_model=VoiceDetectionResponse)
async def detect_voice(
request: VoiceDetectionRequest
):
# 1. Security Check - REMOVED for Public Access per user request
# logger.info(f"Public Access: Processing request for {request.language}")
try:
# 2. Basic Validation (Logic)
if request.audioFormat.lower() != "mp3":
# Just to be perfectly safe, though Pydantic regex handles it
raise ValueError("Only MP3 format is supported.")
# 3. Decode Base64
try:
audio_data = base64.b64decode(request.audioBase64)
except Exception:
raise ValueError("Invalid Base64 encoding.")
if not audio_data:
raise ValueError("Empty audio data.")
# 4. Perform Detection (Non-Blocking)
# We run the synchronous detector.analyze_audio in a threadpool
# so the API remains responsive to other requests.
logger.info(f"Processing request for language: {request.language}")
result = await run_in_threadpool(detector.analyze_audio, audio_data, request.language)
if "error" in result:
# If internal analysis failed, we still want to return a strict error format if possible,
# or map it to the error response.
raise ValueError(result["error"])
# 5. Return formatted response (Strict JSON)
return VoiceDetectionResponse(
status="success",
language=request.language,
classification=result["classification"],
confidenceScore=result["confidenceScore"],
explanation=result["explanation"]
)
except ValueError as ve:
logger.error(f"Validation Error: {ve}")
return JSONResponse(
status_code=400,
content={"status": "error", "message": str(ve)}
)
except Exception as e:
logger.error(f"Internal Error: {e}")
return JSONResponse(
status_code=500,
content={"status": "error", "message": "Internal server error processing audio."}
)
@app.get("/")
def health_check():
return {
"status": "online",
"service": "AudioShield AI (Hackathon Edition)",
"models_loaded": len(detector.pipelines) if hasattr(detector, 'pipelines') else 0
}
# Standard execution for HF Spaces (uvicorn launched via Docker CMD)