| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import sys |
| import time |
| import asyncio |
| from pathlib import Path |
|
|
| PROJECT_ROOT = Path(__file__).resolve().parents[1] |
| if str(PROJECT_ROOT) not in sys.path: |
| sys.path.insert(0, str(PROJECT_ROOT)) |
|
|
| from src.api.server import analyze_text_payload |
| from src.aspect_sentiment.audio import WhisperTranscriber |
| from src.aspect_sentiment.behavioral_signals import detect_signals |
| from src.aspect_sentiment.diarization import diarize_audio_segments |
|
|
|
|
| REQUIRED_TERMS = ["laptop", "60000", "dell", "lenovo"] |
|
|
|
|
| def validate_audio(audio_path: Path, min_confidence: float, min_turns: int) -> tuple[dict, list[str]]: |
| transcriber = WhisperTranscriber() |
| transcription = transcriber.transcribe(audio_path) |
| diarization = diarize_audio_segments(audio_path, transcription.segments) |
| result = asyncio.run(analyze_text_payload( |
| transcription.text, |
| source_name=audio_path.name, |
| source_type="audio", |
| language=transcription.language, |
| transcription_confidence=transcription.confidence, |
| whisper_model=transcriber.model_size, |
| diarization=diarization, |
| start_time=time.perf_counter(), |
| )) |
|
|
| customer_text = result.get("customerTranscript", "") |
| agent_text = result.get("agentTranscript", "") |
| transcript_lower = transcription.text.lower().replace(",", "") |
| signals = detect_signals(customer_text, result.get("rawFeatures", [])) |
|
|
| failures: list[str] = [] |
| if transcription.confidence is None or transcription.confidence < min_confidence: |
| failures.append(f"transcription confidence below threshold: {transcription.confidence}") |
| if len(diarization.turns) < min_turns: |
| failures.append(f"too few diarized turns: {len(diarization.turns)}") |
| if not customer_text.strip(): |
| failures.append("missing customer transcript") |
| if not agent_text.strip(): |
| failures.append("missing agent transcript") |
| for term in REQUIRED_TERMS: |
| if term not in transcript_lower: |
| failures.append(f"required transcript term missing: {term}") |
| if "asked_for_whatsapp" in signals.get("detected_positive", []): |
| failures.append("false WhatsApp behavioral signal detected") |
| if result.get("conversionScore", {}).get("label") not in {"warm", "hot", "cold"}: |
| failures.append("conversion label is invalid") |
|
|
| summary = { |
| "audio": str(audio_path), |
| "whisperModel": transcriber.model_size, |
| "language": transcription.language, |
| "confidence": transcription.confidence, |
| "durationSeconds": transcription.duration_seconds, |
| "segmentCount": len(transcription.segments), |
| "diarizationProvider": diarization.provider, |
| "turnCount": len(diarization.turns), |
| "customerWordCount": len(customer_text.split()), |
| "agentWordCount": len(agent_text.split()), |
| "conversion": result.get("conversionScore", {}), |
| "behavioralSignals": signals, |
| "failures": failures, |
| } |
| return summary, failures |
|
|
|
|
| def main() -> int: |
| parser = argparse.ArgumentParser(description="Run a production smoke test for the audio pipeline.") |
| parser.add_argument("--audio", type=Path, default=Path("audio/conv_001.wav")) |
| parser.add_argument("--min-confidence", type=float, default=0.60) |
| parser.add_argument("--min-turns", type=int, default=8) |
| args = parser.parse_args() |
|
|
| if not args.audio.exists(): |
| print(json.dumps({"failures": [f"audio file not found: {args.audio}"]}, indent=2)) |
| return 2 |
|
|
| summary, failures = validate_audio(args.audio, args.min_confidence, args.min_turns) |
| print(json.dumps(summary, indent=2, ensure_ascii=True)) |
| return 1 if failures else 0 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|