diff --git a/tests/tools/test_tts_streaming.py b/tests/tools/test_tts_streaming.py index dd5b921d169..962814642ad 100644 --- a/tests/tools/test_tts_streaming.py +++ b/tests/tools/test_tts_streaming.py @@ -130,9 +130,62 @@ def test_elevenlabs_available_reflects_key(monkeypatch): assert ts.ElevenLabsStreamer.available() is False -def test_openai_available_reflects_key(monkeypatch): - monkeypatch.setattr(ts, "get_env_value", lambda k, *a: "key" if k == "OPENAI_API_KEY" else None) +def test_openai_available_reflects_audio_key_resolution(monkeypatch): + monkeypatch.setattr(ts, "resolve_openai_audio_api_key", lambda: "voice-key") assert ts.OpenAIStreamer.available() is True + monkeypatch.setattr(ts, "resolve_openai_audio_api_key", lambda: "") + assert ts.OpenAIStreamer.available() is False + + +def test_openai_streamer_prefers_configured_base_url(monkeypatch): + captured = {} + + class _Response: + def __enter__(self): + return self + + def __exit__(self, *_args): + return False + + def iter_bytes(self): + yield b"\x01\x00" + + class _StreamingCreate: + @staticmethod + def create(**kwargs): + captured["request"] = kwargs + return _Response() + + class _OpenAI: + def __init__(self, **kwargs): + captured["client"] = kwargs + self.audio = MagicMock() + self.audio.speech.with_streaming_response = _StreamingCreate() + + monkeypatch.setattr(ts, "resolve_openai_audio_api_key", lambda: "voice-key") + monkeypatch.setattr( + ts, + "get_env_value", + lambda key, *args: "https://env.example/v1" if key == "OPENAI_BASE_URL" else None, + ) + monkeypatch.setattr("openai.OpenAI", _OpenAI) + + config = { + "provider": "openai", + "openai": { + "base_url": "http://local-tts.example/v1", + "model": "tts-1", + "voice": "local-voice", + }, + } + streamer = ts.resolve_streaming_provider(config) + + assert streamer is not None + assert list(streamer.stream("Streaming test.")) == [b"\x01\x00"] + assert captured["client"] == { + "api_key": "voice-key", + "base_url": "http://local-tts.example/v1", + } # ── Dispatch: chunked streamer path ────────────────────────────────────── diff --git a/tools/tts_streaming.py b/tools/tts_streaming.py index 746886ccce3..2f15c518a3e 100644 --- a/tools/tts_streaming.py +++ b/tools/tts_streaming.py @@ -27,6 +27,7 @@ import time from abc import ABC, abstractmethod from typing import Callable, Dict, Iterator, List, Optional +from tools.tool_backend_helpers import resolve_openai_audio_api_key from tools.tts_tool import _get_provider, get_env_value logger = logging.getLogger(__name__) @@ -202,14 +203,18 @@ class OpenAIStreamer(StreamingTTSProvider): @staticmethod def available() -> bool: - return bool(get_env_value("OPENAI_API_KEY")) + return bool(resolve_openai_audio_api_key()) def stream(self, text: str) -> Iterator[bytes]: from openai import OpenAI client = OpenAI( - api_key=get_env_value("OPENAI_API_KEY"), - base_url=get_env_value("OPENAI_BASE_URL") or None, + api_key=resolve_openai_audio_api_key(), + base_url=( + self.section.get("base_url") + or get_env_value("OPENAI_BASE_URL") + or None + ), ) model = self.section.get("model", "gpt-4o-mini-tts") voice = self.section.get("voice", "alloy")