Compare commits

...

1 Commits

Author SHA1 Message Date
Jacques ROUSSEL 25b82a9662 ✨(summary) make ASR response format configurable
Add WHISPERX_RESPONSE_FORMAT (default: diarized_json) so OpenAI-compatible
STT endpoints that do not support diarized_json, such as OVHcloud AI
Endpoints, can be used with verbose_json.
2026-11-11 17:17:37 +01:00
5 changed files with 42 additions and 3 deletions
+1
View File
@@ -10,6 +10,7 @@ and this project adheres to
### Added
- ✨(summary) make ASR response format configurable
- ✨(frontend) add jittered exponential backoff to LiveKit reconnections
- ✨(frontend) tell participants when a recording is interrupted
+1
View File
@@ -81,6 +81,7 @@ sequenceDiagram
| whisperx_api_key | Secret | — | API key for accessing WhisperX. |
| whisperx_base_url | String | `"https://api.whisperx.com/v1"` | Base URL for the WhisperX API. |
| whisperx_asr_model | String | `"whisper-1"` | ASR model used for transcription. |
| whisperx_response_format | String | `"diarized_json"` | Response format requested from the transcription API. Use `verbose_json` for endpoints without diarization support (e.g. OVHcloud AI Endpoints). |
| webhook_max_retries | Integer | `2` | Maximum retries for webhook requests. |
| webhook_status_forcelist | List[Int] | `[502, 503, 504]` | HTTP status codes triggering webhook retry. |
| webhook_backoff_factor | Float | `0.1` | Exponential backoff factor for webhook retries. |
+4 -3
View File
@@ -173,8 +173,9 @@ def transcribe_audio(
# This is in particular true for word_segments and words.
# WhisperX response is slightly different from OpenAI STT endpoints
# response.
# At the same time "diarized_json" should be the value
# provided to STT endpoints in our context.
# At the same time "diarized_json" is the default value
# provided to STT endpoints in our context. It can be overridden
# for endpoints that do not support it (e.g. "verbose_json").
url = urljoin(base_url.rstrip("/") + "/", "audio/transcriptions")
res = requests.post(
url,
@@ -182,7 +183,7 @@ def transcribe_audio(
"model": settings.whisperx_asr_model,
"language": language,
"timestamp_granularities": ["word", "segment"],
"response_format": "diarized_json",
"response_format": settings.whisperx_response_format,
},
files={"file": audio_file},
headers={"Authorization": f"Bearer {api_key}"},
+3
View File
@@ -93,6 +93,9 @@ class Settings(BaseSettings):
whisperx_api_key: SecretStr
whisperx_base_url: Url = "https://api.openai.com/v1"
whisperx_asr_model: str = "whisper-1"
# Use "verbose_json" for OpenAI-compatible STT endpoints that do not support
# "diarized_json" (e.g. OVHcloud AI Endpoints).
whisperx_response_format: str = "diarized_json"
# ISO 639-1 language code (e.g., "en", "fr", "es")
whisperx_default_language: Optional[str] = None
whisperx_allowed_languages: Set[str] = {"en", "fr", "de", "nl"}
@@ -221,3 +221,36 @@ def test_summary_task_retries_with_the_cleaned_exception(monkeypatch):
error = retry.call_args.kwargs["exc"]
assert isinstance(error, CleanedValidationError)
assert CANARY.encode() not in pickle.dumps(error)
@pytest.mark.parametrize(
("configured", "expected"),
[(None, "diarized_json"), ("verbose_json", "verbose_json")],
)
def test_transcription_requests_configured_response_format(
monkeypatch, configured, expected
):
"""The ASR response format defaults to diarized_json and can be overridden."""
storage = MagicMock()
storage.prepare_audio_file.return_value.__enter__.return_value = (
Mock(),
{"duration": 1},
)
monkeypatch.setattr(celery_worker, "file_service", storage)
monkeypatch.setattr(celery_worker, "metadata_manager", Mock())
if configured is not None:
monkeypatch.setattr(
celery_worker,
"settings",
celery_worker.settings.model_copy(
update={"whisperx_response_format": configured}
),
)
response = Mock(status_code=200)
response.json.return_value = {"text": "hello", "segments": []}
post = Mock(return_value=response)
monkeypatch.setattr(celery_worker.requests, "post", post)
celery_worker.transcribe_audio(
task_id="job", language="en", cloud_storage_url="https://example.com/audio"
)
assert post.call_args.kwargs["data"]["response_format"] == expected