chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
# Tests for the OpenAI text-to-speech model (OpenAITTSModel).
|
||||
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
try:
|
||||
from agents.voice import OpenAITTSModel, TTSModelSettings
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class _FakeStreamResponse:
|
||||
"""A minimal async context manager to simulate streaming audio bytes."""
|
||||
|
||||
def __init__(self, chunks: list[bytes]):
|
||||
self._chunks = chunks
|
||||
|
||||
async def __aenter__(self) -> "_FakeStreamResponse":
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||
return None
|
||||
|
||||
async def iter_bytes(self, chunk_size: int = 1024):
|
||||
for chunk in self._chunks:
|
||||
yield chunk
|
||||
|
||||
|
||||
def _make_fake_openai_client(fake_create) -> SimpleNamespace:
|
||||
"""Construct an object with nested audio.speech.with_streaming_response.create."""
|
||||
return SimpleNamespace(
|
||||
audio=SimpleNamespace(
|
||||
speech=SimpleNamespace(with_streaming_response=SimpleNamespace(create=fake_create))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_tts_default_voice_and_instructions() -> None:
|
||||
"""If no voice is specified, OpenAITTSModel uses its default voice and passes instructions."""
|
||||
chunks = [b"abc", b"def"]
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fake_create(
|
||||
*, model: str, voice: str, input: str, response_format: str, extra_body: dict[str, Any]
|
||||
) -> _FakeStreamResponse:
|
||||
captured["model"] = model
|
||||
captured["voice"] = voice
|
||||
captured["input"] = input
|
||||
captured["response_format"] = response_format
|
||||
captured["extra_body"] = extra_body
|
||||
return _FakeStreamResponse(chunks)
|
||||
|
||||
client = _make_fake_openai_client(fake_create)
|
||||
tts_model = OpenAITTSModel(model="test-model", openai_client=client) # type: ignore[arg-type]
|
||||
settings = TTSModelSettings()
|
||||
out: list[bytes] = []
|
||||
async for b in tts_model.run("hello world", settings):
|
||||
out.append(b)
|
||||
assert out == chunks
|
||||
assert captured["model"] == "test-model"
|
||||
assert captured["voice"] == "ash"
|
||||
assert captured["input"] == "hello world"
|
||||
assert captured["response_format"] == "pcm"
|
||||
assert captured["extra_body"] == {"instructions": settings.instructions}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_tts_custom_voice_and_instructions() -> None:
|
||||
"""Specifying voice and instructions are forwarded to the API."""
|
||||
chunks = [b"x"]
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fake_create(
|
||||
*, model: str, voice: str, input: str, response_format: str, extra_body: dict[str, Any]
|
||||
) -> _FakeStreamResponse:
|
||||
captured["model"] = model
|
||||
captured["voice"] = voice
|
||||
captured["input"] = input
|
||||
captured["response_format"] = response_format
|
||||
captured["extra_body"] = extra_body
|
||||
return _FakeStreamResponse(chunks)
|
||||
|
||||
client = _make_fake_openai_client(fake_create)
|
||||
tts_model = OpenAITTSModel(model="my-model", openai_client=client) # type: ignore[arg-type]
|
||||
settings = TTSModelSettings(voice="fable", instructions="Custom instructions")
|
||||
out: list[bytes] = []
|
||||
async for b in tts_model.run("hi", settings):
|
||||
out.append(b)
|
||||
assert out == chunks
|
||||
assert captured["voice"] == "fable"
|
||||
assert captured["extra_body"] == {"instructions": "Custom instructions"}
|
||||
Reference in New Issue
Block a user