import tempfile import os from fastapi import APIRouter, UploadFile, File, Depends, HTTPException, status from core.security import verify_api_key from ml_services.audio_ai import audio_service from ml_services.nlp_graph_ai import nlp_graph_service from models.schemas import IncidentReport from datetime import datetime import uuid from core.db import db_handler router = APIRouter(prefix="/api/v1/scam", tags=["Scam Detection"]) @router.post("/analyze", response_model=IncidentReport) async def analyze_scam_audio( file: UploadFile = File(...), api_key: str = Depends(verify_api_key) ): if not file.filename.endswith(('.wav', '.mp3', '.ogg', '.flac')): raise HTTPException(status_code=400, detail="Unsupported audio format.") temp_file_path = "" try: # Save upload to a temporary file asynchronously fd, temp_file_path = tempfile.mkstemp(suffix=f".{file.filename.split('.')[-1]}") os.close(fd) # Close the synchronous fd immediately import aiofiles async with aiofiles.open(temp_file_path, 'wb') as f: while chunk := await file.read(1024 * 1024): # 1MB chunks await f.write(chunk) # 1. Process Audio (STT + Deepfake check) transcription, fake_prob = await audio_service.process_audio(temp_file_path) # 2. Translate to English (assuming Hindi or other regional language as source for robust pipeline, # but Whisper might output English directly if translated, or source language. # Let's assume whisper gives text, and we pass to translator if we want to ensure English, # but for now we'll pass to intent directly to save overhead unless explicitly requested). # We'll use the LLM to classify intent directly as mistral handles multilingual. intent_and_risk = await nlp_graph_service.classify_intent(transcription) # 3. Extract Entities entities = await nlp_graph_service.extract_entities(transcription) # 4. Agentic AI Fusion (Threat Dossier) from ml_services.agentic_fusion import agent dossier = await agent.generate_threat_dossier(transcription, entities) # 5. DPDP Act Compliance (PII Redaction) redacted_transcription = nlp_graph_service.redact_pii(transcription, entities) # 6. Construct Report report = IncidentReport( incident_id=str(uuid.uuid4()), type="audio", timestamp=datetime.utcnow(), extracted_entities=entities, risk_score=fake_prob if fake_prob > 0.5 else (0.8 if intent_and_risk.get("risk") == "High" else 0.2), threat_dossier=dossier, explainability_report=intent_and_risk.get("explainability_report"), details={ "transcription": redacted_transcription, "intent": intent_and_risk.get("intent", ""), "scam_stage": intent_and_risk.get("scam_stage", ""), "deepfake_probability": fake_prob } ) # 7. Generate Immutable Evidence Hash db_payload = report.model_dump() sig = await db_handler.generate_evidence_hash(db_payload, file_path=temp_file_path) report.digital_signature = sig db_payload["digital_signature"] = sig # 7. Save to MongoDB atomically if db_handler.db is not None: await db_handler.db["incidents"].insert_one(db_payload) return report except Exception as e: raise HTTPException(status_code=500, detail=f"Internal Server Error: {str(e)}") finally: # Cleanup temp file if temp_file_path and os.path.exists(temp_file_path): os.remove(temp_file_path) from models.schemas import VideoAnalysisResponse from ml_services.vision_ai import vision_service @router.post("/video-analysis", response_model=VideoAnalysisResponse) async def analyze_scam_video( file: UploadFile = File(...), api_key: str = Depends(verify_api_key) ): if not file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')): raise HTTPException(status_code=400, detail="Unsupported image format.") temp_file_path = "" try: fd, temp_file_path = tempfile.mkstemp(suffix=f".{file.filename.split('.')[-1]}") os.close(fd) import aiofiles async with aiofiles.open(temp_file_path, 'wb') as f: while chunk := await file.read(1024 * 1024): await f.write(chunk) result = await vision_service.analyze_video_call(temp_file_path) return VideoAnalysisResponse(**result) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) finally: if temp_file_path and os.path.exists(temp_file_path): os.remove(temp_file_path)