Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c0974d7

Browse files
authored
Tests for realtime runner (openai#1079)
--- [//]: # (BEGIN SAPLING FOOTER) * openai#1080 * __->__ openai#1079 * openai#1076
1 parent 3a64c6f commit c0974d7

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed

tests/realtime/test_runner.py

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
from unittest.mock import AsyncMock, Mock, patch
2+
3+
import pytest
4+
from inline_snapshot import snapshot
5+
6+
from agents.realtime.agent import RealtimeAgent
7+
from agents.realtime.config import RealtimeRunConfig, RealtimeSessionModelSettings
8+
from agents.realtime.model import RealtimeModel, RealtimeModelConfig
9+
from agents.realtime.runner import RealtimeRunner
10+
from agents.realtime.session import RealtimeSession
11+
12+
13+
class MockRealtimeModel(RealtimeModel):
14+
async def connect(self, options=None):
15+
pass
16+
17+
def add_listener(self, listener):
18+
pass
19+
20+
def remove_listener(self, listener):
21+
pass
22+
23+
async def send_event(self, event):
24+
pass
25+
26+
async def send_message(self, message, other_event_data=None):
27+
pass
28+
29+
async def send_audio(self, audio, commit=False):
30+
pass
31+
32+
async def send_tool_output(self, tool_call, output, start_response=True):
33+
pass
34+
35+
async def interrupt(self):
36+
pass
37+
38+
async def close(self):
39+
pass
40+
41+
42+
@pytest.fixture
43+
def mock_agent():
44+
agent = Mock(spec=RealtimeAgent)
45+
agent.get_system_prompt = AsyncMock(return_value="Test instructions")
46+
agent.get_all_tools = AsyncMock(return_value=[{"type": "function", "name": "test_tool"}])
47+
return agent
48+
49+
50+
@pytest.fixture
51+
def mock_model():
52+
return MockRealtimeModel()
53+
54+
55+
@pytest.mark.asyncio
56+
async def test_run_creates_session_with_no_settings(mock_agent, mock_model):
57+
"""Test that run() creates a session correctly if no settings are provided"""
58+
runner = RealtimeRunner(mock_agent, model=mock_model)
59+
60+
with patch("agents.realtime.runner.RealtimeSession") as mock_session_class:
61+
mock_session = Mock(spec=RealtimeSession)
62+
mock_session_class.return_value = mock_session
63+
64+
session = await runner.run()
65+
66+
# Verify session was created with correct parameters
67+
mock_session_class.assert_called_once()
68+
call_args = mock_session_class.call_args
69+
70+
assert call_args[1]["model"] == mock_model
71+
assert call_args[1]["agent"] == mock_agent
72+
assert call_args[1]["context"] is None
73+
74+
# Verify model_config contains expected settings from agent
75+
model_config = call_args[1]["model_config"]
76+
assert model_config == snapshot(
77+
{
78+
"initial_model_settings": {
79+
"instructions": "Test instructions",
80+
"tools": [{"type": "function", "name": "test_tool"}],
81+
}
82+
}
83+
)
84+
85+
assert session == mock_session
86+
87+
88+
@pytest.mark.asyncio
89+
async def test_run_creates_session_with_settings_only_in_init(mock_agent, mock_model):
90+
"""Test that it creates a session with the right settings if they are provided only in init"""
91+
config = RealtimeRunConfig(
92+
model_settings=RealtimeSessionModelSettings(model_name="gpt-4o-realtime", voice="nova")
93+
)
94+
runner = RealtimeRunner(mock_agent, model=mock_model, config=config)
95+
96+
with patch("agents.realtime.runner.RealtimeSession") as mock_session_class:
97+
mock_session = Mock(spec=RealtimeSession)
98+
mock_session_class.return_value = mock_session
99+
100+
_ = await runner.run()
101+
102+
# Verify session was created with config overrides
103+
call_args = mock_session_class.call_args
104+
model_config = call_args[1]["model_config"]
105+
106+
# Should have agent settings plus config overrides
107+
assert model_config == snapshot(
108+
{
109+
"initial_model_settings": {
110+
"instructions": "Test instructions",
111+
"tools": [{"type": "function", "name": "test_tool"}],
112+
"model_name": "gpt-4o-realtime",
113+
"voice": "nova",
114+
}
115+
}
116+
)
117+
118+
119+
@pytest.mark.asyncio
120+
async def test_run_creates_session_with_settings_in_both_init_and_run_overrides(
121+
mock_agent, mock_model
122+
):
123+
"""Test settings in both init and run() - init should override run()"""
124+
init_config = RealtimeRunConfig(
125+
model_settings=RealtimeSessionModelSettings(model_name="gpt-4o-realtime", voice="nova")
126+
)
127+
runner = RealtimeRunner(mock_agent, model=mock_model, config=init_config)
128+
129+
run_model_config: RealtimeModelConfig = {
130+
"initial_model_settings": RealtimeSessionModelSettings(
131+
voice="alloy", input_audio_format="pcm16"
132+
)
133+
}
134+
135+
with patch("agents.realtime.runner.RealtimeSession") as mock_session_class:
136+
mock_session = Mock(spec=RealtimeSession)
137+
mock_session_class.return_value = mock_session
138+
139+
_ = await runner.run(model_config=run_model_config)
140+
141+
# Verify run() settings override init settings
142+
call_args = mock_session_class.call_args
143+
model_config = call_args[1]["model_config"]
144+
145+
# Should have agent settings, then init config, then run config overrides
146+
assert model_config == snapshot(
147+
{
148+
"initial_model_settings": {
149+
"voice": "nova",
150+
"input_audio_format": "pcm16",
151+
"instructions": "Test instructions",
152+
"tools": [{"type": "function", "name": "test_tool"}],
153+
"model_name": "gpt-4o-realtime",
154+
}
155+
}
156+
)
157+
158+
159+
@pytest.mark.asyncio
160+
async def test_run_creates_session_with_settings_only_in_run(mock_agent, mock_model):
161+
"""Test settings provided only in run()"""
162+
runner = RealtimeRunner(mock_agent, model=mock_model)
163+
164+
run_model_config: RealtimeModelConfig = {
165+
"initial_model_settings": RealtimeSessionModelSettings(
166+
model_name="gpt-4o-realtime-preview", voice="shimmer", modalities=["text", "audio"]
167+
)
168+
}
169+
170+
with patch("agents.realtime.runner.RealtimeSession") as mock_session_class:
171+
mock_session = Mock(spec=RealtimeSession)
172+
mock_session_class.return_value = mock_session
173+
174+
_ = await runner.run(model_config=run_model_config)
175+
176+
# Verify run() settings are applied
177+
call_args = mock_session_class.call_args
178+
model_config = call_args[1]["model_config"]
179+
180+
# Should have agent settings plus run() settings
181+
assert model_config == snapshot(
182+
{
183+
"initial_model_settings": {
184+
"model_name": "gpt-4o-realtime-preview",
185+
"voice": "shimmer",
186+
"modalities": ["text", "audio"],
187+
"instructions": "Test instructions",
188+
"tools": [{"type": "function", "name": "test_tool"}],
189+
}
190+
}
191+
)
192+
193+
194+
@pytest.mark.asyncio
195+
async def test_run_with_context_parameter(mock_agent, mock_model):
196+
"""Test that context parameter is passed through to session"""
197+
runner = RealtimeRunner(mock_agent, model=mock_model)
198+
test_context = {"user_id": "test123"}
199+
200+
with patch("agents.realtime.runner.RealtimeSession") as mock_session_class:
201+
mock_session = Mock(spec=RealtimeSession)
202+
mock_session_class.return_value = mock_session
203+
204+
await runner.run(context=test_context)
205+
206+
call_args = mock_session_class.call_args
207+
assert call_args[1]["context"] == test_context
208+
209+
210+
@pytest.mark.asyncio
211+
async def test_get_model_settings_with_none_values(mock_model):
212+
"""Test _get_model_settings handles None values from agent properly"""
213+
agent = Mock(spec=RealtimeAgent)
214+
agent.get_system_prompt = AsyncMock(return_value=None)
215+
agent.get_all_tools = AsyncMock(return_value=None)
216+
217+
runner = RealtimeRunner(agent, model=mock_model)
218+
219+
with patch("agents.realtime.runner.RealtimeSession"):
220+
await runner.run()
221+
222+
# Should not crash and agent methods should be called
223+
agent.get_system_prompt.assert_called_once()
224+
agent.get_all_tools.assert_called_once()

0 commit comments

Comments
 (0)