mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-10 11:12:13 +02:00
* fix: fix org scoped access for resources * Fix auth and config validation regressions * fix: track org config validation timestamp * fix: backfill org model configuration v2 from legacy user rows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: align config tests with org-level v2 resolution Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: helm example values tweaks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
161 lines
5.2 KiB
Python
161 lines
5.2 KiB
Python
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from pipecat.transcriptions.language import Language
|
|
|
|
from api.services.configuration.check_validity import UserConfigurationValidator
|
|
from api.services.configuration.registry import (
|
|
XAI_TTS_VOICES,
|
|
ServiceProviders,
|
|
XAITTSConfiguration,
|
|
)
|
|
from api.services.pipecat.service_factory import create_tts_service
|
|
|
|
|
|
def test_xai_tts_configuration_defaults():
|
|
config = XAITTSConfiguration(api_key="test-key")
|
|
|
|
assert config.provider == ServiceProviders.XAI
|
|
assert config.voice == "eve"
|
|
assert config.language == "en"
|
|
# xAI TTS has no model selector; a constant satisfies the shared contract.
|
|
assert config.model == "xai-tts"
|
|
assert XAI_TTS_VOICES == ["eve", "ara", "leo", "rex", "sal"]
|
|
|
|
|
|
@pytest.mark.parametrize("transport_out_sample_rate", [8000, 16000])
|
|
def test_create_xai_tts_service_uses_pipeline_compatible_audio_format(
|
|
transport_out_sample_rate,
|
|
):
|
|
user_config = SimpleNamespace(
|
|
tts=SimpleNamespace(
|
|
provider=ServiceProviders.XAI.value,
|
|
api_key="test-key",
|
|
model="xai-tts",
|
|
voice="rex",
|
|
language="en",
|
|
)
|
|
)
|
|
audio_config = SimpleNamespace(
|
|
transport_out_sample_rate=transport_out_sample_rate,
|
|
transport_in_sample_rate=16000,
|
|
)
|
|
|
|
with patch(
|
|
"api.services.pipecat.service_factory.XAIHttpTTSService"
|
|
) as mock_service:
|
|
create_tts_service(user_config, audio_config)
|
|
|
|
assert mock_service.call_count == 1
|
|
kwargs = mock_service.call_args.kwargs
|
|
assert kwargs["api_key"] == "test-key"
|
|
assert kwargs["sample_rate"] == transport_out_sample_rate
|
|
assert kwargs["encoding"] == "pcm"
|
|
assert kwargs["settings"].voice == "rex"
|
|
assert kwargs["settings"].language == Language.EN
|
|
|
|
|
|
def test_create_xai_tts_service_converts_language():
|
|
user_config = SimpleNamespace(
|
|
tts=SimpleNamespace(
|
|
provider=ServiceProviders.XAI.value,
|
|
api_key="test-key",
|
|
model="xai-tts",
|
|
voice="eve",
|
|
language="fr",
|
|
)
|
|
)
|
|
audio_config = SimpleNamespace(
|
|
transport_out_sample_rate=24000,
|
|
transport_in_sample_rate=16000,
|
|
)
|
|
|
|
with patch(
|
|
"api.services.pipecat.service_factory.XAIHttpTTSService"
|
|
) as mock_service:
|
|
create_tts_service(user_config, audio_config)
|
|
|
|
kwargs = mock_service.call_args.kwargs
|
|
assert kwargs["settings"].language == Language.FR
|
|
|
|
|
|
def test_create_xai_tts_service_falls_back_to_english_for_unknown_language():
|
|
user_config = SimpleNamespace(
|
|
tts=SimpleNamespace(
|
|
provider=ServiceProviders.XAI.value,
|
|
api_key="test-key",
|
|
model="xai-tts",
|
|
voice="eve",
|
|
language="not-a-language",
|
|
)
|
|
)
|
|
audio_config = SimpleNamespace(
|
|
transport_out_sample_rate=24000,
|
|
transport_in_sample_rate=16000,
|
|
)
|
|
|
|
with patch(
|
|
"api.services.pipecat.service_factory.XAIHttpTTSService"
|
|
) as mock_service:
|
|
create_tts_service(user_config, audio_config)
|
|
|
|
kwargs = mock_service.call_args.kwargs
|
|
assert kwargs["settings"].language == Language.EN
|
|
|
|
|
|
def test_create_xai_tts_service_preserves_auto_language():
|
|
user_config = SimpleNamespace(
|
|
tts=SimpleNamespace(
|
|
provider=ServiceProviders.XAI.value,
|
|
api_key="test-key",
|
|
model="xai-tts",
|
|
voice="eve",
|
|
language="auto",
|
|
)
|
|
)
|
|
audio_config = SimpleNamespace(
|
|
transport_out_sample_rate=24000,
|
|
transport_in_sample_rate=16000,
|
|
)
|
|
|
|
with patch(
|
|
"api.services.pipecat.service_factory.XAIHttpTTSService"
|
|
) as mock_service:
|
|
create_tts_service(user_config, audio_config)
|
|
|
|
kwargs = mock_service.call_args.kwargs
|
|
assert kwargs["settings"].language == "auto"
|
|
|
|
|
|
def test_xai_is_registered_for_key_validation():
|
|
validator = UserConfigurationValidator()
|
|
assert ServiceProviders.XAI.value in validator._validator_map
|
|
|
|
|
|
def test_xai_key_validation_accepts_valid_key():
|
|
validator = UserConfigurationValidator()
|
|
with patch("api.services.configuration.check_validity.httpx.get") as mock_get:
|
|
mock_get.return_value.status_code = 200
|
|
assert validator._check_xai_api_key("xai", "xai-valid-key") is True
|
|
# Validates against the TTS-scoped voices endpoint, not /v1/models.
|
|
called_url = mock_get.call_args.args[0]
|
|
assert called_url == "https://api.x.ai/v1/tts/voices"
|
|
assert (
|
|
mock_get.call_args.kwargs["headers"]["Authorization"] == "Bearer xai-valid-key"
|
|
)
|
|
|
|
|
|
def test_xai_key_validation_rejects_bad_key():
|
|
validator = UserConfigurationValidator()
|
|
with patch("api.services.configuration.check_validity.httpx.get") as mock_get:
|
|
mock_get.return_value.status_code = 401
|
|
with pytest.raises(ValueError):
|
|
validator._check_xai_api_key("xai", "bad-key")
|
|
|
|
|
|
def test_xai_key_validation_allows_scoped_key_without_voice_list_access():
|
|
validator = UserConfigurationValidator()
|
|
with patch("api.services.configuration.check_validity.httpx.get") as mock_get:
|
|
mock_get.return_value.status_code = 403
|
|
assert validator._check_xai_api_key("xai", "tts-scoped-key") is True
|