feat(voice): add elevenlabs stt and transcript truth fixes
This commit is contained in:
@@ -2,6 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import time
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from services.ai_voice_runtime_service.audiosocket import pcm16le_to_wav_bytes
|
||||
from services.ai_voice_runtime_service.providers import asr as asr_module
|
||||
|
||||
@@ -78,6 +81,99 @@ def test_yandex_asr_provider_posts_lpcm_with_tts_credential_fallback(monkeypatch
|
||||
assert calls[0]["timeout"] == 5.0
|
||||
|
||||
|
||||
def test_elevenlabs_asr_provider_posts_wav_with_tts_credential_fallback(monkeypatch):
|
||||
calls: list[dict] = []
|
||||
pcm = b"\x01\x00" * 160
|
||||
wav_bytes = pcm16le_to_wav_bytes(pcm, sample_rate_hz=8000)
|
||||
|
||||
class _DummyClient:
|
||||
def __init__(self, *, timeout: float) -> None:
|
||||
self.timeout = timeout
|
||||
|
||||
def __enter__(self) -> "_DummyClient":
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb) -> None:
|
||||
return None
|
||||
|
||||
def post(
|
||||
self,
|
||||
url: str,
|
||||
*,
|
||||
headers: dict[str, str],
|
||||
data: dict[str, str],
|
||||
files: dict[str, tuple[str, bytes, str]],
|
||||
) -> _DummyResponse:
|
||||
calls.append(
|
||||
{
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"data": data,
|
||||
"files": files,
|
||||
"timeout": self.timeout,
|
||||
}
|
||||
)
|
||||
return _DummyResponse({"text": "schedule", "language_code": "rus"})
|
||||
|
||||
monkeypatch.delenv("AI_VOICE_ASR_ELEVENLABS_API_KEY", raising=False)
|
||||
monkeypatch.setenv("AI_VOICE_TTS_ELEVENLABS_API_KEY", "tts-elevenlabs-key")
|
||||
monkeypatch.setenv("AI_VOICE_ASR_ELEVENLABS_API_BASE", "https://api.elevenlabs.example")
|
||||
monkeypatch.setenv("AI_VOICE_ASR_ELEVENLABS_MODEL_ID", "scribe_v1")
|
||||
monkeypatch.setattr(asr_module.httpx, "Client", _DummyClient)
|
||||
|
||||
provider = asr_module.ElevenLabsASRProvider()
|
||||
result = provider.transcribe(wav_bytes, language_hint="ru")
|
||||
|
||||
assert result.text == "schedule"
|
||||
assert result.language == "ru"
|
||||
assert len(calls) == 1
|
||||
assert calls[0]["url"] == "https://api.elevenlabs.example/v1/speech-to-text"
|
||||
assert calls[0]["headers"]["xi-api-key"] == "tts-elevenlabs-key"
|
||||
assert calls[0]["data"]["model_id"] == "scribe_v1"
|
||||
assert calls[0]["data"]["language_code"] == "rus"
|
||||
assert calls[0]["files"]["file"][0] == "turn.wav"
|
||||
assert calls[0]["files"]["file"][2] == "audio/wav"
|
||||
assert calls[0]["timeout"] == 20.0
|
||||
|
||||
|
||||
def test_elevenlabs_asr_provider_surfaces_http_auth_errors(monkeypatch):
|
||||
request = httpx.Request("POST", "https://api.elevenlabs.example/v1/speech-to-text")
|
||||
response = httpx.Response(401, request=request)
|
||||
|
||||
class _DummyClient:
|
||||
def __init__(self, *, timeout: float) -> None:
|
||||
self.timeout = timeout
|
||||
|
||||
def __enter__(self) -> "_DummyClient":
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb) -> None:
|
||||
return None
|
||||
|
||||
def post(
|
||||
self,
|
||||
url: str,
|
||||
*,
|
||||
headers: dict[str, str],
|
||||
data: dict[str, str],
|
||||
files: dict[str, tuple[str, bytes, str]],
|
||||
) -> _DummyResponse:
|
||||
del url, headers, data, files
|
||||
class _FailingResponse(_DummyResponse):
|
||||
def raise_for_status(self_nonlocal) -> None:
|
||||
raise httpx.HTTPStatusError("401 Unauthorized", request=request, response=response)
|
||||
|
||||
return _FailingResponse()
|
||||
|
||||
monkeypatch.setenv("AI_VOICE_ASR_ELEVENLABS_API_KEY", "asr-elevenlabs-key")
|
||||
monkeypatch.setattr(asr_module.httpx, "Client", _DummyClient)
|
||||
|
||||
provider = asr_module.ElevenLabsASRProvider()
|
||||
|
||||
with pytest.raises(httpx.HTTPStatusError):
|
||||
provider.transcribe(b"\x01\x00" * 160, language_hint="ru")
|
||||
|
||||
|
||||
def test_yandex_asr_provider_uses_iam_token(monkeypatch):
|
||||
calls: list[dict] = []
|
||||
|
||||
@@ -325,6 +421,7 @@ def test_yandex_grpc_streaming_provider_returns_partial_and_final():
|
||||
|
||||
|
||||
def test_yandex_asr_builders():
|
||||
assert isinstance(asr_module.build_asr_provider("elevenlabs"), asr_module.ElevenLabsASRProvider)
|
||||
assert isinstance(asr_module.build_asr_provider("yandex"), asr_module.YandexSpeechKitASRProvider)
|
||||
assert isinstance(asr_module.build_asr_provider("speechkit"), asr_module.YandexSpeechKitASRProvider)
|
||||
assert isinstance(
|
||||
|
||||
Reference in New Issue
Block a user