-
Notifications
You must be signed in to change notification settings - Fork 2.2k
🧹 Decouple HTTP exceptions from VAD utility #11338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5d5b709
494e7d4
074723a
ba4e39e
da03475
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ | |
| ) | ||
| from utils.multipart import MultipartMaxPartSizeRoute, SPEECH_PROFILE_MAX_PART_SIZE, max_part_size | ||
| from utils.stt.speaker_embedding import extract_embedding | ||
| from utils.stt.vad import apply_vad_for_speech_profile | ||
| from utils.stt.vad import apply_vad_for_speech_profile, VADEmptyError | ||
| import logging | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -81,7 +81,10 @@ def upload_profile(file: UploadFile, uid: str = Depends(auth.get_current_user_ui | |
| if aseg.duration_seconds < 5 or aseg.duration_seconds > 120: | ||
| raise HTTPException(status_code=400, detail="Audio duration is invalid (must be 5-120 seconds)") | ||
|
|
||
| apply_vad_for_speech_profile(file_path) | ||
| try: | ||
| apply_vad_for_speech_profile(file_path) | ||
| except VADEmptyError: | ||
| raise HTTPException(status_code=400, detail="Audio is empty") | ||
|
Comment on lines
+86
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When VAD finds no speech, this commit changes the utility's exception contract and adds translations at the HTTP and batch-script boundaries, but it modifies no tests; an incorrect exception type or catch would therefore turn the upload's intended 400 into a 500 or let the maintenance thread fail unnoticed. Add a behavioral test that drives the zero-segment path and verifies AGENTS.md reference: AGENTS.md:L26-L28 Useful? React with 👍 / 👎.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed: the zero-segment path is now covered end-to-end — |
||
|
|
||
| # Write-ahead: Cache exact duration after VAD processing (use av for fast header-only read) | ||
| with av.open(file_path) as container: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,6 @@ | |
|
|
||
| import importlib.abc | ||
| import importlib.machinery | ||
| import importlib.util | ||
| import io | ||
| import os | ||
| import sys | ||
|
|
@@ -29,9 +28,7 @@ | |
| "database", | ||
| "utils.other.storage", | ||
| "utils.stt.speaker_embedding", | ||
| "utils.stt.vad", | ||
| "av", | ||
| "pydub", | ||
| "firebase_admin", | ||
| "google", | ||
| "pinecone", | ||
|
|
@@ -75,6 +72,8 @@ def exec_module(self, module): | |
| sys.meta_path.insert(0, _f) | ||
| try: | ||
| from routers import speech_profile as mod | ||
| from scripts.stt import j_apply_vad_to_speech_profiles as batch_mod | ||
| from utils.stt import vad as vad_mod | ||
| finally: | ||
| sys.meta_path.remove(_f) | ||
| for _n in list(sys.modules): | ||
|
|
@@ -132,3 +131,35 @@ def test_invalid_wav_does_not_run_vad_or_upload(self): | |
| assert exc_info.value.status_code == 400 | ||
| mock_vad.assert_not_called() | ||
| mock_upload.assert_not_called() | ||
|
|
||
| def test_empty_vad_returns_400_not_500(self): | ||
| fake_file = _fake_upload_file(b'silence') | ||
|
|
||
| with patch.object(mod, "os") as mock_os, patch("builtins.open", MagicMock()), patch.object( | ||
| mod, "AudioSegment" | ||
| ) as mock_aseg, patch.object(vad_mod, "vad_is_empty", return_value=[]) as mock_vad, patch.object( | ||
| mod, "upload_profile_audio" | ||
| ) as mock_upload: | ||
| mock_os.makedirs.return_value = None | ||
| mock_aseg.from_wav.return_value = MagicMock(frame_rate=16000, duration_seconds=5) | ||
|
|
||
| with pytest.raises(HTTPException) as exc_info: | ||
| mod.upload_profile(fake_file, uid="test-uid") | ||
|
|
||
| assert exc_info.value.status_code == 400 | ||
| assert exc_info.value.detail == "Audio is empty" | ||
| mock_vad.assert_called_once_with("_temp/test-uid/speech_profile.wav", return_segments=True) | ||
| mock_upload.assert_not_called() | ||
|
|
||
| def test_batch_skips_empty_vad_without_uploading(self): | ||
| with patch.object(batch_mod.os, "makedirs"), patch.object( | ||
| batch_mod, "get_users_uid", return_value=["test-uid"] | ||
| ), patch.object(batch_mod, "get_profile_audio_if_exists", return_value="/tmp/profile.wav"), patch.object( | ||
| batch_mod, "upload_profile_audio" | ||
| ) as mock_upload, patch.object( | ||
| vad_mod, "vad_is_empty", return_value=[] | ||
| ) as mock_vad: | ||
| batch_mod.execute() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fresh evidence since the earlier coverage comments is that this added test invokes the failure path through AGENTS.md reference: AGENTS.md:L43-L45 Useful? React with 👍 / 👎. |
||
|
|
||
| mock_vad.assert_called_once_with("/tmp/profile.wav", return_segments=True) | ||
| mock_upload.assert_not_called() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: The new
VADEmptyErrorhandling path is untested. The router branch that convertsVADEmptyErrorinto the400 "Audio is empty"response is the exact user-facing behavior this PR preserves, and the script'sexcept VADEmptyError: returnis the new robustness behavior that motivated the change — but neither has a regression test (existing tests only cover the WAV decode/duration failure branches, and nothing asserts the empty-audio path). Consider adding a small unit test that patchesapply_vad_for_speech_profileto raiseVADEmptyErrorand asserts the router returns 400 without uploading, and one for the script's early-return-on-empty branch.Prompt for AI agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed:
test_empty_vad_returns_400_not_500asserts status 400 + detail "Audio is empty" and no upload;test_batch_skips_empty_vad_without_uploadingcovers the script early-return branch. Both pass.