Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
330 commits
Select commit Hold shift + click to select a range
aaa9471
Merge branch 'main' into bar_raise_agent
mehtarac Nov 6, 2025
9240bad
Update src/strands/experimental/bidirectional_streaming/agent/agent.py
mehtarac Nov 6, 2025
2a2861b
Update imports
mehtarac Nov 6, 2025
30c0a5d
fix: remove turn id
Nov 6, 2025
6ad0120
fix: fix nova completion id tracking
Nov 6, 2025
774ab86
fix: remove unnecessary if condition
Nov 6, 2025
0a63829
Update implementation based on bar-raising
mehtarac Nov 7, 2025
a9784f0
temp commit message, review the changes
Nov 9, 2025
8d9a298
Updates: make ToolCaller private, minor updates based on PR comments
mehtarac Nov 9, 2025
73416d7
Update: file names, locations, and ToolCaller class name
mehtarac Nov 9, 2025
a49273b
Update method names imports for io.py and audio.py and their dependen…
mehtarac Nov 9, 2025
986fc45
use input event in method signatures and update outdated comments
Nov 10, 2025
5ace082
Merge branch 'bidi-event-types' into bidi-gemini-improvements
Nov 10, 2025
69965d2
fix(openai): Improve interruption handling
Nov 10, 2025
f7c18d4
fix: improve gemini test script to display interrupts and remove exce…
Nov 10, 2025
fd11282
Merge pull request #12 from mehtarac/bar_raise_agent
mehtarac Nov 10, 2025
843133b
Move test scripts into dedicated directory so tests directory only ha…
mehtarac Nov 10, 2025
3f0a527
Merge pull request #27 from mehtarac/move_tests
mehtarac Nov 10, 2025
f8ab2a0
refactor: rename events and files
Nov 10, 2025
b815706
Rename bidirectional components
mehtarac Nov 10, 2025
5c30596
Merge pull request #28 from mehtarac/rename_agent
mehtarac Nov 10, 2025
805aa3a
Fix main branch. Temporarily rename loop to original name
mehtarac Nov 10, 2025
5a22ad9
Merge pull request #29 from mehtarac/fix_main
mehtarac Nov 10, 2025
066227d
Merge main into bidi-event-types - apply event naming
Nov 10, 2025
873441b
Fix agent send() to convert dicts to TypedEvent instances
Nov 10, 2025
272a1fc
Update model unit tests to use new class names
Nov 10, 2025
6c2cbf5
Fix all model unit tests to use new API
Nov 10, 2025
8918757
fix: fix bidi tests
Nov 10, 2025
3a9f944
refactor: rename to bidi input and output events
Nov 10, 2025
5eca8f9
refactor: change event type prefix to bidi
Nov 10, 2025
328b5dd
add timeout to agent.receive and fix integ tests
Nov 10, 2025
f7874fe
add bidi agent input type alias
Nov 10, 2025
330831d
use bidi input event for type check
Nov 10, 2025
30e6b1e
Merge pull request #20 from mkmeral/bidi-event-types
mehtarac Nov 10, 2025
bf8c0e9
Merge branch 'main' into bidi-gemini-improvements
Nov 11, 2025
c06ec6f
remove text logging
Nov 11, 2025
ef292d3
add hooks to agent for tool execution
Nov 11, 2025
43fed3c
add event types for tool related events
Nov 11, 2025
f4f7e4d
fix test scripts
Nov 11, 2025
1100ad3
Merge branch 'main' into bidi-openai-improvements
Nov 11, 2025
2d01d2d
add user transcription to openai
Nov 11, 2025
490ce1e
fix(gemini): return multiple tool use events
Nov 11, 2025
325bae1
Merge pull request #31 from mkmeral/fix-scripts
mehtarac Nov 11, 2025
c47a355
Merge pull request #26 from mkmeral/bidi-openai-improvements
mehtarac Nov 11, 2025
2dbe7f0
Merge branch 'main' into bidi-gemini-improvements
Nov 11, 2025
3864bc9
Merge pull request #25 from mkmeral/bidi-gemini-improvements
mehtarac Nov 11, 2025
4e29b5a
agent - loop - refine
pgrayy Nov 10, 2025
2ec5e57
queue empty vs timeout
pgrayy Nov 11, 2025
8bbfd38
bidi audio io - speech
pgrayy Nov 12, 2025
cf6e41c
fix tool call run
pgrayy Nov 12, 2025
7101f4a
asyncio.sleep(0.01)
pgrayy Nov 12, 2025
76f9983
tidy
pgrayy Nov 12, 2025
566f952
preview transcript
pgrayy Nov 12, 2025
e67e90b
comment
pgrayy Nov 12, 2025
d76fefc
sleep comment
pgrayy Nov 12, 2025
f18e509
Merge pull request #33 from mehtarac/loop
mehtarac Nov 12, 2025
d225cd2
split BidiIO into BidiInput and BidiOutput
pgrayy Nov 12, 2025
205ecd7
Merge pull request #35 from mehtarac/loop-run
mehtarac Nov 12, 2025
aa289ba
Update test script
mehtarac Nov 12, 2025
5148c35
Update dependencies
mehtarac Nov 12, 2025
00b379d
Update dependencies
mehtarac Nov 12, 2025
cfe7e26
Update dependencies
mehtarac Nov 12, 2025
ba32dda
Update dependencies
mehtarac Nov 12, 2025
2fd1b44
Update dependencies
mehtarac Nov 12, 2025
06199cc
Merge pull request #36 from mehtarac/iron_out_scripts
mehtarac Nov 12, 2025
9756570
Rename directories and files to use bidi instead of bidirectional
mehtarac Nov 12, 2025
fca4a52
Update naming of directory and file
mehtarac Nov 12, 2025
24e1892
Update import path
mehtarac Nov 12, 2025
fbdcf11
Update model in test script
mehtarac Nov 12, 2025
28eae22
Merge pull request #38 from mehtarac/update_dir
mehtarac Nov 12, 2025
c1b3bf5
Update import
mehtarac Nov 12, 2025
6554242
Merge pull request #39 from mehtarac/update_name
mehtarac Nov 12, 2025
92e6822
fix(pyproject): Fix bidi-all dependency group
Nov 13, 2025
55c5c94
Merge pull request #41 from mkmeral/bidi-deps
mehtarac Nov 13, 2025
ef3d659
Merge pull request #43 from mehtarac/bidio_interface
pgrayy Nov 13, 2025
aae5cf0
Add start/stop to bidi_agent.run
Nov 13, 2025
8dcee5d
Merge pull request #44 from mkmeral/bidi-run
mehtarac Nov 13, 2025
504509e
feat: Add hook support to bidi agents
Nov 17, 2025
918b1af
bidi audio io - handle interruption (#45)
pgrayy Nov 17, 2025
5073fe4
nova sonic - remove model task and events queue (#46)
pgrayy Nov 17, 2025
b186775
openai - remove model task and event queue (#47)
pgrayy Nov 17, 2025
496c025
agent loop - bound event queue (#48)
pgrayy Nov 17, 2025
e763629
Format code
mehtarac Nov 17, 2025
55bc573
Merge pull request #51 from mehtarac/format_lint_main
mehtarac Nov 17, 2025
2f956bf
Format tests and fix linting errors - D102, D107
mehtarac Nov 17, 2025
e597ca2
fix E501 errors
mehtarac Nov 17, 2025
818072f
fix: D107 errors
mehtarac Nov 17, 2025
f343ea6
fix linting error codes - D205, G201, F841, D415
mehtarac Nov 17, 2025
7020875
fix formatting errors - B012, F821, E722
mehtarac Nov 17, 2025
397408e
fix logging and error code G004
mehtarac Nov 17, 2025
bf6236b
Merge pull request #53 from mehtarac/lint_main
mehtarac Nov 17, 2025
0e6221a
fix hatch-static-analysis in github workflow
mehtarac Nov 17, 2025
6175bce
Merge pull request #54 from mehtarac/fix_build_lint
mehtarac Nov 17, 2025
46319e5
nova - send event lock (#52)
pgrayy Nov 17, 2025
315ca24
agent - run - run input/output concurrently (#49)
pgrayy Nov 17, 2025
4f3f558
feat(bidi): Add agent state
Nov 18, 2025
4f3eecf
Merge branch 'main' into bidi-hooks
Nov 18, 2025
d31bc1b
Merge remote-tracking branch 'upstream/main'
Nov 18, 2025
77531c3
Merge branch 'merge-main-bidi' into bidi-hooks
Nov 18, 2025
c15ccf5
fix(bidi): fix tests
Nov 18, 2025
037d184
formatter
Nov 18, 2025
2fe5ddf
Merge branch 'unit-test-fix' into bidi-hooks
Nov 18, 2025
e50fb62
fix toolcaller rename
Nov 18, 2025
d5d3983
Merge branch 'merge-main-bidi' into bidi-hooks
Nov 18, 2025
3346dfa
Merge pull request #56 from mkmeral/merge-main-bidi
mehtarac Nov 18, 2025
16e539b
fix import issues and event loop hanging
Nov 18, 2025
01ee698
Merge branch 'main' into bidi-hooks
Nov 18, 2025
444fc34
remove interruptable
Nov 18, 2025
23b0385
update hooks to use async invocation
Nov 18, 2025
ae3394d
fix mypy errors - agent, loop
mehtarac Nov 18, 2025
8540fd6
Merge pull request #57 from mkmeral/unit-test-fix
mehtarac Nov 18, 2025
29114f6
Merge pull request #55 from mkmeral/bidi-agent-state
mehtarac Nov 18, 2025
f59b2de
Merge branch 'main' into bidi-hooks
Nov 18, 2025
f3f5978
fix mypy errors in agent, loop
mehtarac Nov 18, 2025
5cfbe4d
fix mypy errors in agent, loop files
mehtarac Nov 18, 2025
a20a012
fix model provider mypy errors - novasonic, openai
mehtarac Nov 18, 2025
f7a8e3e
temporarily exclude scripts since scripts will be removed
mehtarac Nov 18, 2025
6d1ebd0
fix mypy errors - audio,text, bidi_model
mehtarac Nov 18, 2025
2b30c5b
fix mypy errors - novasonic
mehtarac Nov 18, 2025
61f61ee
fix mypy errors - openai, caller, events
mehtarac Nov 18, 2025
f220711
fix mypy errors - gemini_live
mehtarac Nov 18, 2025
1a7732e
feat(bidi): Implement session manager
Nov 19, 2025
610a2dd
feat(novasonic): Implement agent.messages injection on start
Nov 19, 2025
1c26f8d
feat: Invoke message added event on tool use
Nov 19, 2025
4cdaf31
fix(openai): fix agent history init
Nov 19, 2025
4206077
Merge pull request #59 from mehtarac/mypy_fixes
mehtarac Nov 19, 2025
48576fe
Merge branch 'main' into bidi-hooks
mehtarac Nov 19, 2025
4033886
Merge pull request #50 from mkmeral/bidi-hooks
mehtarac Nov 19, 2025
c31f5a6
Merge branch 'main' of https://github.com/strands-agents/sdk-python i…
mehtarac Nov 19, 2025
7c964bc
Merge branch 'strands-agents-main'
mehtarac Nov 19, 2025
5af9c9d
lint and mypy (#65)
pgrayy Nov 19, 2025
51c5835
feat(bidi): Add tool executor
Nov 20, 2025
7a00e08
fix: Initialize model param with none on init to fix tests
Nov 20, 2025
d250c4c
feat: Add invocation state to bidi agent
Nov 20, 2025
516b431
run formatter
Nov 20, 2025
7254c28
add hook utils for testing
Nov 20, 2025
dd66699
test: Restore BidiAgent hook event tests in new location
Nov 20, 2025
8d9b7b9
refactor: Use type name check instead of runtime import for BidiAgent…
Nov 20, 2025
e188604
cancellation - nova sonic (#64)
pgrayy Nov 20, 2025
16db749
Update credentials resolver logic in NovaSonic
mehtarac Nov 20, 2025
7e61480
minor update
mehtarac Nov 20, 2025
bf06138
minor update
mehtarac Nov 20, 2025
eef755c
Merge pull request #68 from strands-agents/main
mehtarac Nov 20, 2025
96a2494
minor update
mehtarac Nov 20, 2025
f45cc65
Merge pull request #67 from mehtarac/creds_nova
mehtarac Nov 20, 2025
6596caf
Merge branch 'main' of https://github.com/strands-agents/sdk-python i…
mehtarac Nov 20, 2025
06517ea
Merge branch 'toolcaller'
mehtarac Nov 20, 2025
28a58c0
Merge branch 'main' into bidi-tool-executor
Nov 21, 2025
5d7086f
fix mypy errors
Nov 21, 2025
f652cc8
simplify code based on comments
Nov 21, 2025
8885532
remove unnecessary comment
Nov 21, 2025
2149ba0
Merge branch 'main' into bidi-session-manager
Nov 21, 2025
4532bb0
nova - transcript - set role (#74)
pgrayy Nov 21, 2025
2c421cc
fix merge mypy issuee
Nov 21, 2025
6133368
fix: fix openai sample rate output
Nov 21, 2025
2089d51
Merge pull request #66 from mkmeral/bidi-tool-executor
mehtarac Nov 21, 2025
98dc4f2
Merge branch 'main' into bidi-session-manager
Nov 21, 2025
1026cfc
add audio_config for user's to configure their audio settings
mehtarac Nov 21, 2025
a308eee
async - task pool (#71)
pgrayy Nov 21, 2025
1b5f9a6
cancellation - openai (#73)
pgrayy Nov 21, 2025
a3c7d5e
cancellation - gemini (#75)
pgrayy Nov 21, 2025
6298c86
cancellation - agent (#72)
pgrayy Nov 22, 2025
c4181ec
cancellation - agent loop (#63)
pgrayy Nov 23, 2025
c70d43c
pyproject - bidi configs (#77)
pgrayy Nov 23, 2025
8920afd
Merge branch 'strands-agents:main' into main
pgrayy Nov 23, 2025
7a690ee
fix mypy errors
Nov 24, 2025
d1b8015
Merge branch 'main' into bidi-session-manager
Nov 24, 2025
a4a7f45
rebase
mehtarac Nov 24, 2025
46e14f0
fix tool result mapping
Nov 25, 2025
a1a3fc6
update bidi hook imports
Nov 25, 2025
275f469
pass agent to io's start instead of just model audio config
mehtarac Nov 25, 2025
d18b021
updated audio config to be used from model provider
mehtarac Nov 25, 2025
802099b
remove code from bad rebase
mehtarac Nov 25, 2025
ff0b89b
pass agent to io start method
mehtarac Nov 25, 2025
7b2c30e
update imports
mehtarac Nov 25, 2025
489bba0
test_bidi.py - remove use of with context (#81)
pgrayy Nov 25, 2025
cd06d20
remove unused ToolCaller code from agent and delete caller.py
mehtarac Nov 25, 2025
b3a6e31
agent - add text input to messages (#78)
pgrayy Nov 25, 2025
f29f4f8
bidi text input (#79)
pgrayy Nov 25, 2025
923d93e
updated agent and _caller
mehtarac Nov 25, 2025
dec6c8e
udpated agent
mehtarac Nov 25, 2025
9639536
Merge branch 'main' into update_bidi_agent
mehtarac Nov 25, 2025
1e5adc6
Merge pull request #82 from mehtarac/update_bidi_agent
mehtarac Nov 25, 2025
28ef19e
tools.caller
mehtarac Nov 25, 2025
0d7406c
update configuration for audio
mehtarac Nov 25, 2025
8c2c3c9
updating doc strings
mehtarac Nov 25, 2025
a9c7129
fix indentation
mehtarac Nov 25, 2025
dfa014a
Merge pull request #76 from mehtarac/audio_config
mehtarac Nov 25, 2025
f6a811b
Merge branch 'main' into bidi-session-manager
Nov 26, 2025
fabaf59
address comments
Nov 26, 2025
9d01277
improve tool result handling
Nov 26, 2025
b5d2bb1
fix integ tests
Nov 26, 2025
be51621
make tool result content handling consistent
Nov 26, 2025
f03b73b
audio io - fix hanging on stop (#80)
pgrayy Nov 26, 2025
f7a9ffc
Merge branch 'main' into bidi-session-manager
mehtarac Nov 26, 2025
46e119f
Merge pull request #83 from strands-agents/main
mehtarac Nov 26, 2025
08ac8a5
Merge pull request #60 from mkmeral/bidi-session-manager
mehtarac Nov 26, 2025
a3bc749
model provider consistency improvmeents
mehtarac Nov 26, 2025
e7f3ed5
remove runtime import of bidi (#86)
pgrayy Nov 26, 2025
9c88f87
fix lint and type errors (#87)
pgrayy Nov 27, 2025
610a8ea
tool executor - add agent isinstance check (#88)
pgrayy Nov 27, 2025
7f16aa3
add unit test for bidi agent
mehtarac Nov 27, 2025
aa90bc3
add unit test for bidi agent
mehtarac Nov 27, 2025
18a4304
add unit test for bidi agent
mehtarac Nov 27, 2025
43e3125
remove scripts directory before merging with sdk-python/main
mehtarac Nov 27, 2025
3c812b0
remove scripts directory before merging with sdk-python/main
mehtarac Nov 27, 2025
7a93946
Merge pull request #89 from mehtarac/add_agent_tests
mehtarac Nov 27, 2025
9c5835e
bidi io text - switch to using prompt toolkit (#91)
pgrayy Nov 27, 2025
651565e
types - events - minor clean ups (#92)
pgrayy Nov 28, 2025
be4c10f
model timeout - restart connection - nova sonic (#84)
pgrayy Nov 28, 2025
2b3438a
add feature to stop connection through voice
mehtarac Nov 28, 2025
198bc91
Merge branch 'main' into stop_agent
mehtarac Nov 29, 2025
2cf2c13
update stop_connection tool to stop_coversation
mehtarac Nov 29, 2025
4e68657
update stop_connection tool to stop_coversation
mehtarac Nov 29, 2025
625b25a
updated implementation based on comments
mehtarac Nov 29, 2025
0284837
update tool
mehtarac Nov 29, 2025
b474978
loop - add tool use and tool result in sequence to history (#94)
pgrayy Nov 29, 2025
53e4b31
adjust logic based on comments
mehtarac Nov 29, 2025
e9c26ab
updated implementation
mehtarac Nov 29, 2025
ba56028
Merge branch 'main' into stop_agent
mehtarac Nov 29, 2025
16677f1
update implementation
mehtarac Nov 29, 2025
b266b4e
Merge pull request #93 from mehtarac/stop_agent
mehtarac Nov 29, 2025
4d14bb4
loop - restart connection - openai (#95)
pgrayy Nov 29, 2025
dc30c71
Merge branch 'main' into improve_mp
mehtarac Nov 29, 2025
a9f92aa
Merge branch 'main' into improve_mp
mehtarac Nov 29, 2025
d16ef14
Merge pull request #85 from mehtarac/improve_mp
mehtarac Nov 29, 2025
505de3e
run bidi:prepare (#97)
pgrayy Nov 29, 2025
40aac26
loop - restart connection - gemini (#96)
pgrayy Nov 30, 2025
642752a
rename modules
mehtarac Nov 30, 2025
0dd05fe
rename files
mehtarac Nov 30, 2025
78b3ebc
fix formatting on docstrings (#98)
pgrayy Nov 30, 2025
75d865c
Merge branch 'main' into rename_modules
mehtarac Nov 30, 2025
e472b92
addressed comments
mehtarac Nov 30, 2025
75a7775
addrsssed comments
mehtarac Nov 30, 2025
69d8f09
address comments
mehtarac Nov 30, 2025
9829494
minor update
mehtarac Nov 30, 2025
094a64e
Merge pull request #99 from mehtarac/rename_modules
mehtarac Nov 30, 2025
dadda61
Merge branch 'main' into del_scripts
mehtarac Dec 1, 2025
55fb736
Merge pull request #90 from mehtarac/del_scripts
mehtarac Dec 1, 2025
4c58c43
minor update
mehtarac Dec 1, 2025
a46828d
isolate model inference configs (#100)
pgrayy Dec 2, 2025
78423eb
fix agent send dict to event construction (#101)
pgrayy Dec 2, 2025
75b91aa
add bidi to README
mehtarac Dec 2, 2025
185db15
address comments
mehtarac Dec 2, 2025
f9f0e2d
address comments
mehtarac Dec 2, 2025
6cbac51
address comments
mehtarac Dec 2, 2025
873da65
address comments
mehtarac Dec 2, 2025
29e0989
Merge pull request #102 from mehtarac/update_rm
mehtarac Dec 2, 2025
4ce00d8
fix minor linting and integ test failure errors
mehtarac Dec 2, 2025
799fbd5
Merge pull request #103 from mehtarac/bidi_agent
mehtarac Dec 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/test-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install system audio dependencies (Linux)
if: matrix.os-name == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y portaudio19-dev libasound2-dev
- name: Install system audio dependencies (macOS)
if: matrix.os-name == 'macOS'
run: |
brew install portaudio
- name: Install system audio dependencies (Windows)
if: matrix.os-name == 'windows'
run: |
# Windows typically has audio libraries available by default
echo "Windows audio dependencies handled by PyAudio wheels"
- name: Install dependencies
run: |
pip install --no-cache-dir hatch
Expand Down Expand Up @@ -89,6 +103,11 @@ jobs:
python-version: '3.10'
cache: 'pip'

- name: Install system audio dependencies (Linux)
run: |
sudo apt-get update
sudo apt-get install -y portaudio19-dev libasound2-dev

- name: Install dependencies
run: |
pip install --no-cache-dir hatch
Expand All @@ -97,3 +116,4 @@ jobs:
id: lint
run: hatch fmt --linter --check
continue-on-error: false

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ dist
repl_state
.kiro
uv.lock
.audio_cache
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,74 @@ agent("What is the square root of 1764")

It's also available on GitHub via [strands-agents/tools](https://github.com/strands-agents/tools).

### Bidirectional Streaming

> **⚠️ Experimental Feature**: Bidirectional streaming is currently in experimental status. APIs may change in future releases as we refine the feature based on user feedback and evolving model capabilities.

Build real-time voice and audio conversations with persistent streaming connections. Unlike traditional request-response patterns, bidirectional streaming maintains long-running conversations where users can interrupt, provide continuous input, and receive real-time audio responses. Get started with your first BidiAgent by following the [Quickstart](https://strandsagents.com/latest/documentation/docs/user-guide/concepts/experimental/bidirectional-streaming/quickstart) guide.

**Supported Model Providers:**
- Amazon Nova Sonic (`amazon.nova-sonic-v1:0`)
- Google Gemini Live (`gemini-2.5-flash-native-audio-preview-09-2025`)
- OpenAI Realtime API (`gpt-realtime`)

**Quick Example:**

```python
import asyncio
from strands.experimental.bidi import BidiAgent
from strands.experimental.bidi.models import BidiNovaSonicModel
from strands.experimental.bidi.io import BidiAudioIO, BidiTextIO
from strands.experimental.bidi.tools import stop_conversation
from strands_tools import calculator

async def main():
# Create bidirectional agent with audio model
model = BidiNovaSonicModel()
agent = BidiAgent(model=model, tools=[calculator, stop_conversation])

# Setup audio and text I/O
audio_io = BidiAudioIO()
text_io = BidiTextIO()

# Run with real-time audio streaming
# Say "stop conversation" to gracefully end the conversation
await agent.run(
inputs=[audio_io.input()],
outputs=[audio_io.output(), text_io.output()]
)

if __name__ == "__main__":
asyncio.run(main())
```

**Configuration Options:**

```python
# Configure audio settings
model = BidiNovaSonicModel(
provider_config={
"audio": {
"input_rate": 16000,
"output_rate": 16000,
"voice": "matthew"
},
"inference": {
"max_tokens": 2048,
"temperature": 0.7
}
}
)

# Configure I/O devices
audio_io = BidiAudioIO(
input_device_index=0, # Specific microphone
output_device_index=1, # Specific speaker
input_buffer_size=10,
output_buffer_size=10
)
```

## Documentation

For detailed guidance & examples, explore our documentation:
Expand Down
67 changes: 65 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,18 @@ a2a = [
"fastapi>=0.115.12,<1.0.0",
"starlette>=0.46.2,<1.0.0",
]

bidi = [
"aws_sdk_bedrock_runtime; python_version>='3.12'",
"prompt_toolkit>=3.0.0,<4.0.0",
"pyaudio>=0.2.13,<1.0.0",
"smithy-aws-core>=0.0.1; python_version>='3.12'",
]
bidi-gemini = ["google-genai>=1.32.0,<2.0.0"]
bidi-openai = ["websockets>=15.0.0,<16.0.0"]

all = ["strands-agents[a2a,anthropic,docs,gemini,litellm,llamaapi,mistral,ollama,openai,writer,sagemaker,otel]"]
bidi-all = ["strands-agents[a2a,bidi,bidi-gemini,bidi-openai,docs,otel]"]

dev = [
"commitizen>=4.4.0,<5.0.0",
Expand Down Expand Up @@ -104,7 +115,7 @@ features = ["all"]
dependencies = [
"mypy>=1.15.0,<2.0.0",
"ruff>=0.13.0,<0.14.0",
# Include required pacakge dependencies for mypy
# Include required package dependencies for mypy
"strands-agents @ {root:uri}",
]

Expand All @@ -118,7 +129,7 @@ format-fix = [
]
lint-check = [
"ruff check",
"mypy -p src"
"mypy ./src"
]
lint-fix = [
"ruff check --fix"
Expand Down Expand Up @@ -192,11 +203,16 @@ warn_no_return = true
warn_unreachable = true
follow_untyped_imports = true
ignore_missing_imports = false
exclude = ["src/strands/experimental/bidi"]

[[tool.mypy.overrides]]
module = ["strands.experimental.bidi.*"]
follow_imports = "skip"

[tool.ruff]
line-length = 120
include = ["examples/**/*.py", "src/**/*.py", "tests/**/*.py", "tests_integ/**/*.py"]
exclude = ["src/strands/experimental/bidi/**/*.py", "tests/strands/experimental/bidi/**/*.py", "tests_integ/bidi/**/*.py"]

[tool.ruff.lint]
select = [
Expand All @@ -219,6 +235,7 @@ convention = "google"
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_default_fixture_loop_scope = "function"
addopts = "--ignore=tests/strands/experimental/bidi --ignore=tests_integ/bidi"


[tool.coverage.run]
Expand All @@ -227,6 +244,7 @@ source = ["src"]
context = "thread"
parallel = true
concurrency = ["thread", "multiprocessing"]
omit = ["src/strands/experimental/bidi/*"]

[tool.coverage.report]
show_missing = true
Expand Down Expand Up @@ -256,3 +274,48 @@ style = [
["text", ""],
["disabled", "fg:#858585 italic"]
]

# =========================
# Bidi development configs
# =========================

[tool.hatch.envs.bidi]
dev-mode = true
features = ["dev", "bidi-all"]
installer = "uv"

[tool.hatch.envs.bidi.scripts]
prepare = [
"hatch run bidi-lint:format-fix",
"hatch run bidi-lint:quality-fix",
"hatch run bidi-lint:type-check",
"hatch run bidi-test:test-cov",
]

[tools.hatch.envs.bidi-lint]
template = "bidi"

[tool.hatch.envs.bidi-lint.scripts]
format-check = "format-fix --check"
format-fix = "ruff format {args} --target-version py312 ./src/strands/experimental/bidi/**/*.py"
quality-check = "ruff check {args} --target-version py312 ./src/strands/experimental/bidi/**/*.py"
quality-fix = "quality-check --fix"
type-check = "mypy {args} --python-version 3.12 ./src/strands/experimental/bidi/**/*.py"

[tool.hatch.envs.bidi-test]
template = "bidi"

[tool.hatch.envs.bidi-test.scripts]
test = "pytest {args} tests/strands/experimental/bidi"
test-cov = """
test \
--cov=strands.experimental.bidi \
--cov-config= \
--cov-branch \
--cov-report=term-missing \
--cov-report=xml:build/coverage/bidi-coverage.xml \
--cov-report=html:build/coverage/bidi-html
"""

[[tool.hatch.envs.bidi-test.matrix]]
python = ["3.13", "3.12"]
78 changes: 78 additions & 0 deletions src/strands/experimental/bidi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Bidirectional streaming package."""

import sys

if sys.version_info < (3, 12):
raise ImportError("bidi only supported for >= Python 3.12")

# Main components - Primary user interface
# Re-export standard agent events for tool handling
from ...types._events import (
ToolResultEvent,
ToolStreamEvent,
ToolUseStreamEvent,
)
from .agent.agent import BidiAgent

# IO channels - Hardware abstraction
from .io.audio import BidiAudioIO

# Model interface (for custom implementations)
from .models.model import BidiModel
from .models.nova_sonic import BidiNovaSonicModel

# Built-in tools
from .tools import stop_conversation

# Event types - For type hints and event handling
from .types.events import (
BidiAudioInputEvent,
BidiAudioStreamEvent,
BidiConnectionCloseEvent,
BidiConnectionStartEvent,
BidiErrorEvent,
BidiImageInputEvent,
BidiInputEvent,
BidiInterruptionEvent,
BidiOutputEvent,
BidiResponseCompleteEvent,
BidiResponseStartEvent,
BidiTextInputEvent,
BidiTranscriptStreamEvent,
BidiUsageEvent,
ModalityUsage,
)

__all__ = [
# Main interface
"BidiAgent",
# IO channels
"BidiAudioIO",
# Model providers
"BidiNovaSonicModel",
# Built-in tools
"stop_conversation",
# Input Event types
"BidiTextInputEvent",
"BidiAudioInputEvent",
"BidiImageInputEvent",
"BidiInputEvent",
# Output Event types
"BidiConnectionStartEvent",
"BidiConnectionCloseEvent",
"BidiResponseStartEvent",
"BidiResponseCompleteEvent",
"BidiAudioStreamEvent",
"BidiTranscriptStreamEvent",
"BidiInterruptionEvent",
"BidiUsageEvent",
"ModalityUsage",
"BidiErrorEvent",
"BidiOutputEvent",
# Tool Event types (reused from standard agent)
"ToolUseStreamEvent",
"ToolResultEvent",
"ToolStreamEvent",
# Model interface
"BidiModel",
]
29 changes: 29 additions & 0 deletions src/strands/experimental/bidi/_async/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Utilities for async operations."""

from typing import Awaitable, Callable

from ._task_pool import _TaskPool

__all__ = ["_TaskPool"]


async def stop_all(*funcs: Callable[..., Awaitable[None]]) -> None:
"""Call all stops in sequence and aggregate errors.

A failure in one stop call will not block subsequent stop calls.

Args:
funcs: Stop functions to call in sequence.

Raises:
ExceptionGroup: If any stop function raises an exception.
"""
exceptions = []
for func in funcs:
try:
await func()
except Exception as exception:
exceptions.append(exception)

if exceptions:
raise ExceptionGroup("failed stop sequence", exceptions)
43 changes: 43 additions & 0 deletions src/strands/experimental/bidi/_async/_task_pool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Manage pool of active async tasks.

This is particularly useful for cancelling multiple tasks at once.
"""

import asyncio
from typing import Any, Coroutine


class _TaskPool:
"""Manage pool of active async tasks."""

def __init__(self) -> None:
"""Setup task container."""
self._tasks: set[asyncio.Task] = set()

def __len__(self) -> int:
"""Number of active tasks."""
return len(self._tasks)

def create(self, coro: Coroutine[Any, Any, Any]) -> asyncio.Task:
"""Create async task.

Adds a clean up callback to run after task completes.

Returns:
The created task.
"""
task = asyncio.create_task(coro)
task.add_done_callback(lambda task: self._tasks.remove(task))

self._tasks.add(task)
return task

async def cancel(self) -> None:
"""Cancel all active tasks in pool."""
for task in self._tasks:
task.cancel()

try:
await asyncio.gather(*self._tasks)
except asyncio.CancelledError:
pass
5 changes: 5 additions & 0 deletions src/strands/experimental/bidi/agent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Bidirectional agent for real-time streaming conversations."""

from .agent import BidiAgent

__all__ = ["BidiAgent"]
Loading
Loading