Skip to content

Commit 5421b56

Browse files
committed
bidi - remove python 3.11+ features
1 parent 911a1c7 commit 5421b56

File tree

5 files changed

+36
-19
lines changed

5 files changed

+36
-19
lines changed

src/strands/experimental/bidi/_async/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async def stop_all(*funcs: Callable[..., Awaitable[None]]) -> None:
1616
funcs: Stop functions to call in sequence.
1717
1818
Raises:
19-
ExceptionGroup: If any stop function raises an exception.
19+
RuntimeError: If any stop function raises an exception.
2020
"""
2121
exceptions = []
2222
for func in funcs:
@@ -26,4 +26,8 @@ async def stop_all(*funcs: Callable[..., Awaitable[None]]) -> None:
2626
exceptions.append(exception)
2727

2828
if exceptions:
29-
raise ExceptionGroup("failed stop sequence", exceptions)
29+
exceptions.append(RuntimeError("failed stop sequence"))
30+
for i in range(1, len(exceptions)):
31+
exceptions[i].__cause__ = exceptions[i - 1]
32+
33+
raise exceptions[-1]

src/strands/experimental/bidi/agent/agent.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,16 @@ async def run_outputs(inputs_task: asyncio.Task) -> None:
387387
for start in [*input_starts, *output_starts]:
388388
await start(self)
389389

390-
async with asyncio.TaskGroup() as task_group:
391-
inputs_task = task_group.create_task(run_inputs())
392-
task_group.create_task(run_outputs(inputs_task))
390+
inputs_task = asyncio.create_task(run_inputs())
391+
outputs_task = asyncio.create_task(run_outputs(inputs_task))
392+
393+
try:
394+
await asyncio.gather(inputs_task, outputs_task)
395+
except (Exception, asyncio.CancelledError):
396+
inputs_task.cancel()
397+
outputs_task.cancel()
398+
await asyncio.gather(inputs_task, outputs_task, return_exceptions=True)
399+
raise
393400

394401
finally:
395402
input_stops = [input_.stop for input_ in inputs if isinstance(input_, BidiInput)]

tests/strands/experimental/bidi/_async/test__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import traceback
12
from unittest.mock import AsyncMock
23

34
import pytest
@@ -10,17 +11,19 @@ async def test_stop_exception():
1011
func1 = AsyncMock()
1112
func2 = AsyncMock(side_effect=ValueError("stop 2 failed"))
1213
func3 = AsyncMock()
14+
func4 = AsyncMock(side_effect=ValueError("stop 4 failed"))
1315

14-
with pytest.raises(ExceptionGroup) as exc_info:
15-
await stop_all(func1, func2, func3)
16+
with pytest.raises(RuntimeError, match=r"failed stop sequence") as exc_info:
17+
await stop_all(func1, func2, func3, func4)
1618

1719
func1.assert_called_once()
1820
func2.assert_called_once()
1921
func3.assert_called_once()
22+
func4.assert_called_once()
2023

21-
assert len(exc_info.value.exceptions) == 1
22-
with pytest.raises(ValueError, match=r"stop 2 failed"):
23-
raise exc_info.value.exceptions[0]
24+
tru_tb = "".join(traceback.format_exception(RuntimeError, exc_info.value, exc_info.tb))
25+
assert "ValueError: stop 2 failed" in tru_tb
26+
assert "ValueError: stop 4 failed" in tru_tb
2427

2528

2629
@pytest.mark.asyncio

tests/strands/experimental/bidi/models/test_gemini_live.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
import pytest
1414
from google.genai import types as genai_types
1515

16-
from strands.experimental.bidi.models.model import BidiModelTimeoutError
1716
from strands.experimental.bidi.models.gemini_live import BidiGeminiLiveModel
17+
from strands.experimental.bidi.models.model import BidiModelTimeoutError
1818
from strands.experimental.bidi.types.events import (
1919
BidiAudioInputEvent,
2020
BidiAudioStreamEvent,
@@ -185,7 +185,7 @@ async def test_connection_edge_cases(mock_genai_client, api_key, model_id):
185185
model4 = BidiGeminiLiveModel(model_id=model_id, client_config={"api_key": api_key})
186186
await model4.start()
187187
mock_live_session_cm.__aexit__.side_effect = Exception("Close failed")
188-
with pytest.raises(ExceptionGroup):
188+
with pytest.raises(RuntimeError, match=r"failed stop sequence"):
189189
await model4.stop()
190190

191191

@@ -572,7 +572,6 @@ def test_tool_formatting(model, tool_spec):
572572
assert formatted_empty == []
573573

574574

575-
576575
# Tool Result Content Tests
577576

578577

@@ -601,7 +600,7 @@ async def test_custom_audio_rates_in_events(mock_genai_client, model_id, api_key
601600
assert isinstance(audio_event, BidiAudioStreamEvent)
602601
# Should use configured rates, not constants
603602
assert audio_event.sample_rate == 48000 # Custom config
604-
assert audio_event.channels == 2 # Custom config
603+
assert audio_event.channels == 2 # Custom config
605604
assert audio_event.format == "pcm"
606605

607606
await model.stop()
@@ -631,7 +630,7 @@ async def test_default_audio_rates_in_events(mock_genai_client, model_id, api_ke
631630
assert isinstance(audio_event, BidiAudioStreamEvent)
632631
# Should use default rates
633632
assert audio_event.sample_rate == 24000 # Default output rate
634-
assert audio_event.channels == 1 # Default channels
633+
assert audio_event.channels == 1 # Default channels
635634
assert audio_event.format == "pcm"
636635

637636
await model.stop()

tests/strands/experimental/bidi/models/test_openai_realtime.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ def test_audio_config_defaults(api_key, model_name):
131131
def test_audio_config_partial_override(api_key, model_name):
132132
"""Test partial audio configuration override."""
133133
provider_config = {"audio": {"output_rate": 48000, "voice": "echo"}}
134-
model = BidiOpenAIRealtimeModel(model_id=model_name, client_config={"api_key": api_key}, provider_config=provider_config)
134+
model = BidiOpenAIRealtimeModel(
135+
model_id=model_name, client_config={"api_key": api_key}, provider_config=provider_config
136+
)
135137

136138
# Overridden values
137139
assert model.config["audio"]["output_rate"] == 48000
@@ -154,7 +156,9 @@ def test_audio_config_full_override(api_key, model_name):
154156
"voice": "shimmer",
155157
}
156158
}
157-
model = BidiOpenAIRealtimeModel(model_id=model_name, client_config={"api_key": api_key}, provider_config=provider_config)
159+
model = BidiOpenAIRealtimeModel(
160+
model_id=model_name, client_config={"api_key": api_key}, provider_config=provider_config
161+
)
158162

159163
assert model.config["audio"]["input_rate"] == 48000
160164
assert model.config["audio"]["output_rate"] == 48000
@@ -349,7 +353,7 @@ async def async_connect(*args, **kwargs):
349353
model4 = BidiOpenAIRealtimeModel(model_id=model_name, client_config={"api_key": api_key})
350354
await model4.start()
351355
mock_ws.close.side_effect = Exception("Close failed")
352-
with pytest.raises(ExceptionGroup):
356+
with pytest.raises(RuntimeError, match=r"failed stop sequence"):
353357
await model4.stop()
354358

355359

@@ -510,7 +514,7 @@ async def test_receive_lifecycle_events(mock_websocket, model):
510514
format="pcm",
511515
sample_rate=24000,
512516
channels=1,
513-
)
517+
),
514518
]
515519
assert tru_events == exp_events
516520

0 commit comments

Comments
 (0)