mirror of
https://github.com/strands-agents/harness-sdk.git
synced 2026-10-02 02:44:48 +08:00
feat(bidi): graduate bidirectional streaming API (#4707)
Co-authored-by: Patrick Gray <pgrayy@amazon.com> Co-authored-by: Murat Kaan Meral <muratkaanmeral@gmail.com>
This commit is contained in:
co-authored by
Patrick Gray
Murat Kaan Meral
parent
ff114a9bd1
commit
8dbc128eae
@@ -421,7 +421,7 @@ The index page (`src/content/docs/api/python/index.mdx`) is a permanent file (no
|
||||
```
|
||||
strands.agent.agent → Agent > Agent
|
||||
strands.agent.base → Agent > Base
|
||||
strands.experimental.bidi.types → Experimental > Bidi > Types
|
||||
strands.bidi.types → Bidi > Types
|
||||
```
|
||||
|
||||
### Index Page Component (`src/components/PythonApiList.astro`)
|
||||
|
||||
@@ -27,7 +27,9 @@ from pydoc_markdown.contrib.processors.smart import SmartProcessor
|
||||
from pydoc_markdown.contrib.renderers.markdown import MarkdownRenderer
|
||||
from pydoc_markdown.contrib.source_linkers.git import GitSourceLinker
|
||||
|
||||
BIDI_PACKAGE = "strands.experimental.bidi"
|
||||
BIDI_PACKAGE = "strands.bidi"
|
||||
BIDI_PUBLIC_MODULES = ("agent", "hooks", "io", "models", "types")
|
||||
DEPRECATED_BIDI_PACKAGE = "strands.experimental.bidi"
|
||||
|
||||
|
||||
def _read_module_tree(source_root: Path, module_name: str) -> ast.Module:
|
||||
@@ -113,11 +115,9 @@ def _read_public_sources(source_root: Path, module_name: str) -> dict[str, set[s
|
||||
|
||||
|
||||
def _read_bidi_public_api(source_root: Path) -> dict[str, dict[str, set[str]]]:
|
||||
package_tree = _read_module_tree(source_root, BIDI_PACKAGE)
|
||||
owner_names = _read_all_exports(package_tree, BIDI_PACKAGE)
|
||||
return {
|
||||
f"{BIDI_PACKAGE}.{owner_name}": _read_public_sources(source_root, f"{BIDI_PACKAGE}.{owner_name}")
|
||||
for owner_name in owner_names
|
||||
for owner_name in BIDI_PUBLIC_MODULES
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,9 @@ def generate_docs():
|
||||
for module in modules:
|
||||
module_name = module.name
|
||||
|
||||
if module_name == DEPRECATED_BIDI_PACKAGE or module_name.startswith(f"{DEPRECATED_BIDI_PACKAGE}."):
|
||||
continue
|
||||
|
||||
if module_name in bidi_public_api:
|
||||
continue
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
---
|
||||
title: BidiAgent
|
||||
description: 'Build real-time voice conversations with BidiAgent. Stream audio and text over persistent connections with barge-ins and concurrent tool calling.'
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/agent/agent.py
|
||||
- path: strands-py/src/strands/experimental/bidi/agent/loop.py
|
||||
- path: strands-py/src/strands/experimental/bidi/types/content.py
|
||||
- path: strands-py/src/strands/bidi/agent/agent.py
|
||||
- path: strands-py/src/strands/bidi/agent/loop.py
|
||||
- path: strands-py/src/strands/bidi/types/content.py
|
||||
---
|
||||
|
||||
|
||||
@@ -69,9 +68,9 @@ print(result.message)
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
agent = BidiAgent(model=model, tools=[notebook])
|
||||
@@ -202,8 +201,8 @@ Configure a `BidiAgent` with a model, tools, a system prompt, and optional conve
|
||||
### Basic Configuration
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
|
||||
@@ -223,7 +222,7 @@ agent = BidiAgent(
|
||||
Each model provider has specific configuration options:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
model = BedrockNovaSonicModel(
|
||||
model_id="amazon.nova-2-sonic-v1:0",
|
||||
@@ -356,7 +355,7 @@ Send a list to group text and images into one user message, preserving block ord
|
||||
With a running OpenAI or Gemini agent:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.types.media import ImageBlock
|
||||
|
||||
async def describe_image(agent: BidiAgent, image_bytes: bytes) -> None:
|
||||
@@ -400,7 +399,7 @@ Each provider declares its reconnect timing as a `ConnectionConfig`. Override it
|
||||
opt out of automatic reconnect with the model's `connection` argument:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
model = BedrockNovaSonicModel(
|
||||
model_id="amazon.nova-2-sonic-v1:0",
|
||||
@@ -486,5 +485,5 @@ and event queues, then invokes cleanup hooks.
|
||||
- [I/O Streams](io.md) - Building custom input and output streams
|
||||
- [Model Providers](models/bedrock.md) - Provider-specific configuration
|
||||
- [Quickstart](quickstart.md) - Getting started guide
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.agent) -
|
||||
- [Python API Reference](@api/python/strands.bidi.agent) -
|
||||
Complete API documentation
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
title: Barge-in
|
||||
description: 'Handle real-time voice barge-ins in BidiAgent. Voice Activity Detection stops responses mid-stream for natural, human-like conversations.'
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
redirectFrom:
|
||||
- docs/user-guide/concepts/bidirectional-streaming/interruption
|
||||
- docs/user-guide/sdk/bidirectional-streaming/interruption
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/types/events.py
|
||||
- path: strands-py/src/strands/experimental/bidi/io/audio.py
|
||||
- path: strands-py/src/strands/bidi/types/events.py
|
||||
- path: strands-py/src/strands/bidi/io/audio.py
|
||||
---
|
||||
|
||||
|
||||
@@ -41,9 +40,9 @@ When using `AudioIO`, barge-ins are handled automatically:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
agent = BidiAgent(model=model)
|
||||
@@ -68,9 +67,9 @@ For custom behavior, process barge-in events manually:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.experimental.bidi.types import BidiBargeInEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.types import BidiBargeInEvent
|
||||
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
agent = BidiAgent(model=model)
|
||||
@@ -104,8 +103,8 @@ asyncio.run(main())
|
||||
Use hooks to track barge-ins across your application:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.hooks import (
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.hooks import (
|
||||
BidiBargeInEvent as BidiBargeInHookEvent,
|
||||
)
|
||||
|
||||
@@ -135,7 +134,7 @@ agent = BidiAgent(
|
||||
If barge-ins aren't being detected:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import OpenAIRealtimeModel
|
||||
from strands.bidi.models import OpenAIRealtimeModel
|
||||
|
||||
# Check VAD configuration (OpenAI)
|
||||
model = OpenAIRealtimeModel(
|
||||
@@ -178,7 +177,7 @@ async def __call__(self, event: BidiOutputEvent):
|
||||
If barge-in is detected too easily:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import OpenAIRealtimeModel
|
||||
from strands.bidi.models import OpenAIRealtimeModel
|
||||
|
||||
# Increase VAD threshold (OpenAI)
|
||||
model = OpenAIRealtimeModel(
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
---
|
||||
title: Events
|
||||
description: 'Send and receive bidirectional streaming events in Strands agents. Process audio, text, and tool activity in real time over persistent connections.'
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/types/agent.py
|
||||
- path: strands-py/src/strands/experimental/bidi/types/content.py
|
||||
- path: strands-py/src/strands/experimental/bidi/types/media.py
|
||||
- path: strands-py/src/strands/bidi/types/agent.py
|
||||
- path: strands-py/src/strands/bidi/types/content.py
|
||||
- path: strands-py/src/strands/bidi/types/media.py
|
||||
- path: strands-py/src/strands/types/content.py
|
||||
- path: strands-py/src/strands/experimental/bidi/types/events.py
|
||||
- path: strands-py/src/strands/experimental/bidi/types/io.py
|
||||
- path: strands-py/src/strands/bidi/types/events.py
|
||||
- path: strands-py/src/strands/bidi/types/io.py
|
||||
- path: strands-py/src/strands/types/media.py
|
||||
---
|
||||
|
||||
@@ -35,8 +34,8 @@ Bidirectional streaming uses a different event model than [standard streaming](.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -84,7 +83,7 @@ determines the sample rate and channel count for real-time PCM audio.
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
from strands.experimental.bidi.types import AudioDelta
|
||||
from strands.bidi.types import AudioDelta
|
||||
|
||||
audio_bytes = Path("audio-chunk.pcm").read_bytes()
|
||||
|
||||
@@ -427,8 +426,8 @@ Use deltas for live updates and block events for completed text, reasoning, and
|
||||
transcripts. This function reads completed content from a started agent:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.types import (
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.types import (
|
||||
BidiReasoningBlockEvent,
|
||||
BidiTextBlockEvent,
|
||||
BidiTranscriptBlockEvent,
|
||||
@@ -558,9 +557,9 @@ Emitted periodically to report token usage with modality breakdown.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -595,8 +594,8 @@ asyncio.run(main())
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -615,8 +614,8 @@ asyncio.run(main())
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.vended_tools import notebook
|
||||
|
||||
async def main():
|
||||
@@ -644,8 +643,8 @@ asyncio.run(main())
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -670,8 +669,8 @@ asyncio.run(main())
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0") # 8-minute timeout
|
||||
|
||||
@@ -3,12 +3,11 @@ title: Bidirectional Streaming Hooks
|
||||
description: 'Extend BidiAgent with hooks for bidirectional streaming events: connection lifecycle, barge-ins, restarts, and real-time conversation logging.'
|
||||
sidebar:
|
||||
label: "Hooks"
|
||||
experimental: true
|
||||
tags: [bidi-streaming, hooks]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/hooks/events.py
|
||||
- path: strands-py/src/strands/experimental/bidi/agent/agent.py
|
||||
- path: strands-py/src/strands/experimental/bidi/agent/loop.py
|
||||
- path: strands-py/src/strands/bidi/hooks/events.py
|
||||
- path: strands-py/src/strands/bidi/agent/agent.py
|
||||
- path: strands-py/src/strands/bidi/agent/loop.py
|
||||
- path: strands-py/src/strands/hooks/events.py
|
||||
- path: strands-py/src/strands/types/agent.py
|
||||
---
|
||||
@@ -42,8 +41,8 @@ Register related hooks together by implementing `register_hooks()`:
|
||||
|
||||
```python
|
||||
from strands import LocalAgent
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.hooks import BidiAgentStopEvent, BidiResponseStopEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.hooks import BidiAgentStopEvent, BidiResponseStopEvent
|
||||
from strands.hooks import AgentInitializedEvent, HookRegistry, MessageAddedEvent
|
||||
|
||||
|
||||
@@ -79,7 +78,7 @@ Register a single hook with `add_hook()`, which infers the event type:
|
||||
|
||||
```python
|
||||
from strands import LocalAgent
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.hooks import MessageAddedEvent
|
||||
|
||||
|
||||
@@ -100,7 +99,7 @@ their default agent type.
|
||||
To observe completed transcripts, subscribe to `MessageUpdatedEvent`. A transcript first appears as an empty message through `MessageAddedEvent` when the transcript starts. Its completion replaces that message at the reserved position. `event.tracking_id` identifies the message and `event.message` contains the replacement.
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.hooks import MessageUpdatedEvent
|
||||
|
||||
|
||||
@@ -120,7 +119,7 @@ for their agent type:
|
||||
|
||||
```python
|
||||
from strands import LocalAgent, ToolContext, tool
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.hooks import AfterToolCallEvent, BeforeToolCallEvent
|
||||
|
||||
|
||||
@@ -196,8 +195,8 @@ The hook mirrors model-reported completion: shutdown or a connection failure wit
|
||||
a stop event does not emit it.
|
||||
|
||||
The hook and streaming event share a name but are separate classes. Import the hook
|
||||
from `strands.experimental.bidi.hooks`. Import the streaming event from
|
||||
`strands.experimental.bidi.types` when handling `agent.receive()` output.
|
||||
from `strands.bidi.hooks`. Import the streaming event from
|
||||
`strands.bidi.types` when handling `agent.receive()` output.
|
||||
|
||||
## Cookbook
|
||||
|
||||
@@ -208,8 +207,8 @@ This section contains practical hook implementations for common use cases.
|
||||
Count barge-ins and record their reasons:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.hooks import BidiBargeInEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.hooks import BidiBargeInEvent
|
||||
from strands.hooks import HookRegistry
|
||||
|
||||
|
||||
@@ -234,8 +233,8 @@ agent = BidiAgent(hooks=[tracker])
|
||||
Track connection restart attempts and their outcomes:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.hooks import (
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.hooks import (
|
||||
BidiAfterConnectionRestartEvent,
|
||||
BidiBeforeConnectionRestartEvent,
|
||||
)
|
||||
@@ -269,8 +268,8 @@ agent = BidiAgent(hooks=[ConnectionMonitor()])
|
||||
Count model-reported response completions and report the total when the agent stops:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.hooks import BidiAgentStopEvent, BidiResponseStopEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.hooks import BidiAgentStopEvent, BidiResponseStopEvent
|
||||
from strands.hooks import HookRegistry
|
||||
|
||||
|
||||
@@ -301,7 +300,7 @@ across connection restarts. Changes made by any of them are visible to the other
|
||||
|
||||
```python
|
||||
from strands import LocalAgent, tool
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.hooks import BeforeToolCallEvent
|
||||
|
||||
|
||||
@@ -338,4 +337,4 @@ For more guidance on performance, errors, and composition, see the
|
||||
|
||||
- [Agent](agent.md) - Learn about BidiAgent configuration and lifecycle
|
||||
- [Events](events.md) - Complete guide to bidirectional streaming events
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.agent) - Complete API documentation
|
||||
- [Python API Reference](@api/python/strands.bidi.agent) - Complete API documentation
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
title: Build a realtime voice agent
|
||||
description: "Build a realtime agent that streams audio in and out: a persistent BidiAgent connection, a realtime model provider, and the events, I/O, and barge-ins a live conversation needs."
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
redirectFrom:
|
||||
- docs/user-guide/concepts/bidirectional-streaming/session-management
|
||||
@@ -85,9 +84,9 @@ loop streams audio both ways until you interrupt it.
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
agent = BidiAgent(
|
||||
@@ -105,7 +104,7 @@ async def main():
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
Bidirectional streaming is a Python-only experimental feature; install it with
|
||||
Bidirectional streaming is available in the Python SDK. Install it with
|
||||
`pip install "strands-agents[bidi-all]"`. The [quickstart](quickstart/) covers
|
||||
per-provider installs and credentials.
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@ description: >-
|
||||
with input and output streams for audio and text.
|
||||
sidebar:
|
||||
label: "IO"
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/io/console/_io.py
|
||||
- path: strands-py/src/strands/experimental/bidi/io/console/_display.py
|
||||
- path: strands-py/src/strands/experimental/bidi/io/console/_keyboard.py
|
||||
- path: strands-py/src/strands/experimental/bidi/io/audio.py
|
||||
- path: strands-py/src/strands/bidi/io/console/_io.py
|
||||
- path: strands-py/src/strands/bidi/io/console/_display.py
|
||||
- path: strands-py/src/strands/bidi/io/console/_keyboard.py
|
||||
- path: strands-py/src/strands/bidi/io/audio.py
|
||||
---
|
||||
|
||||
|
||||
@@ -46,10 +45,10 @@ management.
|
||||
Implementation of these protocols will look as follows:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.types import BidiAgentInput
|
||||
from strands.experimental.bidi.types import BidiOutputEvent
|
||||
from strands.experimental.bidi.types import InputStream, OutputStream
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.types import BidiAgentInput
|
||||
from strands.bidi.types import BidiOutputEvent
|
||||
from strands.bidi.types import InputStream, OutputStream
|
||||
|
||||
|
||||
class MyInputStream(InputStream):
|
||||
@@ -87,7 +86,7 @@ Pass your I/O streams to the agent's `run()` method to connect them to the agent
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
|
||||
|
||||
async def main():
|
||||
@@ -124,8 +123,8 @@ PyAudio is excluded from the aggregate `bidi-all` extra because of its PortAudio
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
|
||||
|
||||
async def main():
|
||||
@@ -174,7 +173,7 @@ pip install "strands-agents[bidi,bidi-pyaudio,bidi-aec]"
|
||||
Pass `audio_processor=True` for the defaults, or an `AudioProcessorConfig` to tune it:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.io import AudioIO, AudioProcessorConfig
|
||||
from strands.bidi.io import AudioIO, AudioProcessorConfig
|
||||
|
||||
# Echo cancellation, noise suppression, and auto gain control with defaults:
|
||||
audio_io = AudioIO(audio_processor=True)
|
||||
@@ -224,9 +223,9 @@ pip install "strands-agents[bidi-io,bidi-openai]"
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import ConsoleIO
|
||||
from strands.experimental.bidi.models import OpenAIRealtimeModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import ConsoleIO
|
||||
from strands.bidi.models import OpenAIRealtimeModel
|
||||
|
||||
|
||||
async def main():
|
||||
@@ -273,8 +272,8 @@ This example uses Amazon Nova Sonic. Configure
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO, ConsoleIO
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO, ConsoleIO
|
||||
|
||||
|
||||
async def main():
|
||||
@@ -307,8 +306,8 @@ shows how to use WebSockets with `run()`:
|
||||
# server.py
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import OpenAIRealtimeModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import OpenAIRealtimeModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: Bedrock Nova Sonic
|
||||
description: 'Build real-time speech-to-speech agents with Amazon Bedrock Nova Sonic and Strands. Bidirectional audio streaming with barge-in handling and tool calling.'
|
||||
experimental: true
|
||||
tags: [bidi-streaming, aws, bedrock]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/models/bedrock.py
|
||||
- path: strands-py/src/strands/bidi/models/bedrock.py
|
||||
redirectFrom:
|
||||
- docs/user-guide/concepts/bidirectional-streaming/models/nova_sonic
|
||||
---
|
||||
@@ -46,9 +45,9 @@ After installing the Bedrock Nova Sonic and local audio extras, create a voice a
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.vended_tools import notebook
|
||||
|
||||
|
||||
@@ -101,7 +100,7 @@ export AWS_REGION=your_region_name
|
||||
|
||||
```python
|
||||
import boto3
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
|
||||
boto_session = boto3.Session(
|
||||
@@ -133,10 +132,10 @@ For more details on this approach, please refer to the [boto3 session docs](http
|
||||
| Parameter | Description | Example | Options |
|
||||
| --------- | ----------- | ------- | ------- |
|
||||
| `model_id` | Nova Sonic model identifier. | `"amazon.nova-2-sonic-v1:0"` | Nova Sonic model IDs |
|
||||
| `audio` | Input and output stream options. | `{"output": {"sample_rate": 24000}}` | [reference](@api/python/strands.experimental.bidi.models#BedrockNovaSonicAudioConfig) |
|
||||
| `audio` | Input and output stream options. | `{"output": {"sample_rate": 24000}}` | [reference](@api/python/strands.bidi.models#BedrockNovaSonicAudioConfig) |
|
||||
| `voice` | Output voice identifier. Defaults to `"matthew"`. | `"tiffany"` | Nova Sonic voices |
|
||||
| `params` | Provider-specific session parameters, such as inference and turn detection configuration. | `{"inferenceConfiguration": {"temperature": 0.7}}` | [`sessionStart` fields](https://docs.aws.amazon.com/nova/latest/nova2-userguide/sonic-input-events.html) |
|
||||
| `connection` | Reconnect timing overrides. | `{"auto_reconnect": False}` | [reference](@api/python/strands.experimental.bidi.models#ConnectionConfig) |
|
||||
| `connection` | Reconnect timing overrides. | `{"auto_reconnect": False}` | [reference](@api/python/strands.bidi.models#ConnectionConfig) |
|
||||
|
||||
:::caution[Conversation History Limits]
|
||||
Nova Sonic caps conversation history passed via `messages` at 50KB per message and 200KB total. Messages exceeding the per-message limit are truncated; if the total exceeds 200KB, the oldest messages are dropped until it fits. This happens silently (only visible via debug-level logs) and only applies to history provided at connection start, not to turns generated during the live conversation.
|
||||
@@ -168,4 +167,4 @@ As a reminder, Nova Sonic is only available in us-east-1, us-west-2, eu-north-1,
|
||||
|
||||
- [Nova Sonic](https://docs.aws.amazon.com/nova/latest/nova2-userguide/using-conversational-speech.html)
|
||||
- [Experimental Bedrock Client](https://github.com/aws/aws-sdk-python/tree/develop/clients/aws-sdk-bedrock-runtime)
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.models#BedrockNovaSonicModel)
|
||||
- [Python API Reference](@api/python/strands.bidi.models#BedrockNovaSonicModel)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: Google Gemini Live
|
||||
description: "Build real-time voice agents with Google's Gemini Live API and Strands. Stream audio and text over WebSocket with barge-ins and tool calling."
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/models/google.py
|
||||
- path: strands-py/src/strands/bidi/models/google.py
|
||||
redirectFrom:
|
||||
- docs/user-guide/concepts/bidirectional-streaming/models/gemini_live
|
||||
---
|
||||
@@ -42,9 +41,9 @@ After installing the Gemini Live and local audio extras, create a voice agent:
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import GoogleGeminiLiveModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import GoogleGeminiLiveModel
|
||||
from strands.vended_tools import notebook
|
||||
|
||||
|
||||
@@ -76,10 +75,10 @@ the [Google GenAI client reference](https://googleapis.github.io/python-genai/ge
|
||||
| Parameter | Description | Example | Options |
|
||||
| --------- | ----------- | ------- | ------- |
|
||||
| `model_id` | Gemini Live model identifier. | `"gemini-3.8-live"` | [Gemini models](https://ai.google.dev/gemini-api/docs/models) |
|
||||
| `audio` | Input audio options. | `{"input": {"sample_rate": 48000}}` | [reference](@api/python/strands.experimental.bidi.models#GoogleGeminiLiveAudioConfig) |
|
||||
| `audio` | Input audio options. | `{"input": {"sample_rate": 48000}}` | [reference](@api/python/strands.bidi.models#GoogleGeminiLiveAudioConfig) |
|
||||
| `voice` | Prebuilt output voice name. Uses the provider default when omitted. | `"Kore"` | [Voices and languages](https://docs.cloud.google.com/text-to-speech/docs/list-voices-and-types) |
|
||||
| `params` | Gemini Live session parameters. | `{"temperature": 0.7}` | [`LiveConnectConfig`](https://googleapis.github.io/python-genai/genai.html#genai.types.LiveConnectConfig) |
|
||||
| `connection` | Reconnect timing overrides. | `{"auto_reconnect": False}` | [reference](@api/python/strands.experimental.bidi.models#ConnectionConfig) |
|
||||
| `connection` | Reconnect timing overrides. | `{"auto_reconnect": False}` | [reference](@api/python/strands.bidi.models#ConnectionConfig) |
|
||||
|
||||
### Additional Provider Options
|
||||
|
||||
@@ -87,7 +86,7 @@ Use direct options such as `voice` for common settings. For additional Google Ge
|
||||
options, pass `params` using snake_case field names.
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import GoogleGeminiLiveModel
|
||||
from strands.bidi.models import GoogleGeminiLiveModel
|
||||
|
||||
model = GoogleGeminiLiveModel(
|
||||
model_id="gemini-3.8-live",
|
||||
@@ -124,4 +123,4 @@ environment variable. You can obtain an API key from
|
||||
|
||||
- [Gemini Live API](https://ai.google.dev/gemini-api/docs/live)
|
||||
- [Gemini API Reference](https://googleapis.github.io/python-genai/genai.html#)
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.models#GoogleGeminiLiveModel)
|
||||
- [Python API Reference](@api/python/strands.bidi.models#GoogleGeminiLiveModel)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: OpenAI Realtime
|
||||
description: 'Build low-latency voice agents with the OpenAI Realtime API and Strands. Configure speech-to-speech streaming, barge-ins, and tool calling.'
|
||||
experimental: true
|
||||
tags: [bidi-streaming]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/models/openai.py
|
||||
- path: strands-py/src/strands/bidi/models/openai.py
|
||||
redirectFrom:
|
||||
- docs/user-guide/concepts/bidirectional-streaming/models/openai_realtime
|
||||
---
|
||||
@@ -41,9 +40,9 @@ After installing the OpenAI Realtime and local audio extras, create a voice agen
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import OpenAIRealtimeModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import OpenAIRealtimeModel
|
||||
from strands.vended_tools import notebook
|
||||
|
||||
|
||||
@@ -83,14 +82,14 @@ if __name__ == "__main__":
|
||||
| `transcription_model_id` | Required input transcription model identifier. Pass `None` to disable user transcription. | `"gpt-transcribe"` | [GPT-Transcribe](https://developers.openai.com/api/docs/models/gpt-transcribe) |
|
||||
| `voice` | Output voice identifier. Defaults to `"alloy"`. | `"coral"` | [Voice options](https://platform.openai.com/docs/guides/realtime-conversations#voice-options) |
|
||||
| `params` | OpenAI Realtime session parameters. Audio must remain mono PCM at 24000 Hz. | `{"max_output_tokens": 4096}` | [`session.update`](https://platform.openai.com/docs/api-reference/realtime-client-events/session/update) |
|
||||
| `connection` | Reconnect timing overrides. | `{"auto_reconnect": False}` | [reference](@api/python/strands.experimental.bidi.models#ConnectionConfig) |
|
||||
| `connection` | Reconnect timing overrides. | `{"auto_reconnect": False}` | [reference](@api/python/strands.bidi.models#ConnectionConfig) |
|
||||
|
||||
### Additional Provider Options
|
||||
|
||||
Use direct options such as `voice` and `transcription_model_id` for common settings, and pass additional OpenAI Realtime options through `params`.
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import OpenAIRealtimeModel
|
||||
from strands.bidi.models import OpenAIRealtimeModel
|
||||
|
||||
model = OpenAIRealtimeModel(
|
||||
model_id="gpt-realtime-2.1",
|
||||
@@ -128,4 +127,4 @@ Set the `OPENAI_API_KEY` environment variable or pass the key through `api_key`.
|
||||
|
||||
- [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime)
|
||||
- [OpenAI API Reference](https://platform.openai.com/docs/api-reference/realtime)
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.models#OpenAIRealtimeModel)
|
||||
- [Python API Reference](@api/python/strands.bidi.models#OpenAIRealtimeModel)
|
||||
|
||||
@@ -4,11 +4,10 @@ description: 'Trace, measure, and log BidiAgent sessions: connection lifecycle,
|
||||
sidebar:
|
||||
label: "Observability"
|
||||
languages: Python
|
||||
experimental: true
|
||||
tags: [bidi-streaming, observability]
|
||||
sourceLinks:
|
||||
- path: strands-py/src/strands/experimental/bidi/_telemetry.py
|
||||
- path: strands-py/src/strands/experimental/bidi/agent/loop.py
|
||||
- path: strands-py/src/strands/bidi/_telemetry.py
|
||||
- path: strands-py/src/strands/bidi/agent/loop.py
|
||||
---
|
||||
|
||||
|
||||
@@ -31,9 +30,9 @@ pip install 'strands-agents[bidi,otel]'
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.types import BidiConnectionStartEvent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.types import BidiConnectionStartEvent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.telemetry import StrandsTelemetry
|
||||
|
||||
strands_telemetry = StrandsTelemetry()
|
||||
@@ -278,7 +277,7 @@ Register a hook provider to accumulate session health counters:
|
||||
```python
|
||||
import logging
|
||||
|
||||
from strands.experimental.bidi.hooks import (
|
||||
from strands.bidi.hooks import (
|
||||
BidiAfterConnectionRestartEvent,
|
||||
BidiBargeInEvent,
|
||||
)
|
||||
@@ -315,8 +314,8 @@ Traces record aggregate session tokens. The per-modality breakdown is only avail
|
||||
the event stream:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.types import BidiResponseStopEvent, BidiUsageEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.types import BidiResponseStopEvent, BidiUsageEvent
|
||||
|
||||
|
||||
async def track_session(agent: BidiAgent) -> None:
|
||||
@@ -357,7 +356,7 @@ behavior the trace does not explain, such as a tool call that never appears to e
|
||||
```python
|
||||
import logging
|
||||
|
||||
logging.getLogger("strands.experimental.bidi").setLevel(logging.DEBUG)
|
||||
logging.getLogger("strands.bidi").setLevel(logging.DEBUG)
|
||||
logging.basicConfig(
|
||||
format="%(levelname)s | %(name)s | %(message)s",
|
||||
handlers=[logging.StreamHandler()],
|
||||
@@ -367,13 +366,13 @@ logging.basicConfig(
|
||||
A session that starts, calls a tool, and stops logs this:
|
||||
|
||||
```
|
||||
DEBUG | strands.experimental.bidi.agent.agent | context_manager=<enter> | starting agent
|
||||
DEBUG | strands.experimental.bidi.agent.agent | agent starting
|
||||
DEBUG | strands.experimental.bidi.agent.loop | agent loop starting
|
||||
DEBUG | strands.experimental.bidi.agent.loop | model task starting
|
||||
DEBUG | strands.experimental.bidi.agent.loop | tool_name=<get_weather> | tool execution starting
|
||||
DEBUG | strands.experimental.bidi.agent.agent | context_manager=<exit> | stopping agent
|
||||
DEBUG | strands.experimental.bidi.agent.loop | agent loop stopping
|
||||
DEBUG | strands.bidi.agent.agent | context_manager=<enter> | starting agent
|
||||
DEBUG | strands.bidi.agent.agent | agent starting
|
||||
DEBUG | strands.bidi.agent.loop | agent loop starting
|
||||
DEBUG | strands.bidi.agent.loop | model task starting
|
||||
DEBUG | strands.bidi.agent.loop | tool_name=<get_weather> | tool execution starting
|
||||
DEBUG | strands.bidi.agent.agent | context_manager=<exit> | stopping agent
|
||||
DEBUG | strands.bidi.agent.loop | agent loop stopping
|
||||
```
|
||||
|
||||
Audio and transcript chunks are not logged, since a minute of conversation produces
|
||||
@@ -465,4 +464,4 @@ Each provider emits one span per response. Usage details vary by provider:
|
||||
- [Events](events.md) - Complete guide to bidirectional streaming events
|
||||
- [Hooks](hooks.md) - Extend agent functionality with hooks
|
||||
- [Barge-in](barge-in.md) - How barge-in detection works
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.agent) - Complete API documentation
|
||||
- [Python API Reference](@api/python/strands.bidi.agent) - Complete API documentation
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
title: Build a voice agent
|
||||
tags: [bidi-streaming, quickstart]
|
||||
description: "Build voice-enabled AI agents with real-time audio streaming. Works with Amazon Nova Sonic, Gemini Live, and OpenAI Realtime."
|
||||
experimental: true
|
||||
---
|
||||
|
||||
|
||||
@@ -22,7 +21,7 @@ Before starting, ensure you have:
|
||||
|
||||
## Install the SDK
|
||||
|
||||
Bidirectional streaming is included in the Strands Agents SDK as an experimental feature. Install the SDK with bidirectional streaming support:
|
||||
Install the SDK with bidirectional streaming support:
|
||||
|
||||
### For All Providers
|
||||
|
||||
@@ -153,9 +152,9 @@ Now let's create a simple voice-enabled agent that can have real-time conversati
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
# Create a bidirectional streaming model
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -203,9 +202,9 @@ The `run()` method runs indefinitely by default. The simplest way to stop conver
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -238,9 +237,9 @@ Just like standard Strands agents, bidirectional agents can use tools during con
|
||||
```python
|
||||
import asyncio
|
||||
from strands import tool
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.vended_tools import notebook
|
||||
|
||||
# Define a custom tool
|
||||
@@ -302,9 +301,9 @@ Choose supported audio settings on the model and device buffering on the I/O str
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import GoogleGeminiLiveModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import GoogleGeminiLiveModel
|
||||
|
||||
# Configure model audio settings
|
||||
model = GoogleGeminiLiveModel(
|
||||
@@ -342,10 +341,10 @@ Bidirectional agents automatically handle barge-ins when users start speaking:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.experimental.bidi.types import BidiBargeInEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.types import BidiBargeInEvent
|
||||
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
agent = BidiAgent(model=model)
|
||||
@@ -378,9 +377,9 @@ If you need more control over the agent lifecycle, you can manually call `start(
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.experimental.bidi.types import BidiResponseStopEvent
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.types import BidiResponseStopEvent
|
||||
|
||||
async def main():
|
||||
model = BedrockNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0")
|
||||
@@ -411,9 +410,9 @@ To let users end a conversation by voice, define a tool that calls `agent.cancel
|
||||
```python
|
||||
import asyncio
|
||||
from strands import LocalAgent, ToolContext, tool
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
|
||||
@tool(context=True)
|
||||
@@ -451,9 +450,9 @@ To enable debug logs in your agent, configure the `strands` logger:
|
||||
```python
|
||||
import asyncio
|
||||
import logging
|
||||
from strands.experimental.bidi.agent import BidiAgent
|
||||
from strands.experimental.bidi.io import AudioIO
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.agent import BidiAgent
|
||||
from strands.bidi.io import AudioIO
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
# Enable debug logs
|
||||
logging.getLogger("strands").setLevel(logging.DEBUG)
|
||||
@@ -531,7 +530,7 @@ audio_io = AudioIO(input_device_index=1)
|
||||
Each provider caps how long a single connection stays open. Rather than wait for that limit, `BidiAgent` reconnects proactively: a timer fires ahead of the cap, the agent replays the conversation history into a fresh connection, and it emits a `BidiConnectionRestartEvent` with `reason="scheduled"`. If a connection times out first, the agent reconnects reactively and emits the same event with `reason="timeout"`. Treat both as informational, not errors:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.types import BidiConnectionRestartEvent
|
||||
from strands.bidi.types import BidiConnectionRestartEvent
|
||||
|
||||
async for event in agent.receive():
|
||||
if isinstance(event, BidiConnectionRestartEvent):
|
||||
@@ -545,7 +544,7 @@ Providers declare reconnect timing through `ConnectionConfig`. Tune it, or opt o
|
||||
automatic reconnect with the model's `connection` argument:
|
||||
|
||||
```python
|
||||
from strands.experimental.bidi.models import BedrockNovaSonicModel
|
||||
from strands.bidi.models import BedrockNovaSonicModel
|
||||
|
||||
# Reconnect 60s earlier than the provider default
|
||||
model = BedrockNovaSonicModel(
|
||||
@@ -565,4 +564,4 @@ For longer sessions on a single connection, OpenAI Realtime allows a larger conn
|
||||
- [Nova Sonic](models/bedrock.md) - Amazon Bedrock's bidirectional streaming model
|
||||
- [OpenAI Realtime](models/openai.md) - OpenAI's Realtime API
|
||||
- [Gemini Live](models/google.md) - Google's Gemini Live API
|
||||
- [Python API Reference](@api/python/strands.experimental.bidi.agent) - Complete API documentation
|
||||
- [Python API Reference](@api/python/strands.bidi.agent) - Complete API documentation
|
||||
|
||||
@@ -46,7 +46,7 @@ export interface DocInfo {
|
||||
*
|
||||
* Example:
|
||||
* - strands.agent.agent -> Agent > Agent
|
||||
* - strands.experimental.bidi.types -> Experimental > Bidi > Types
|
||||
* - strands.bidi.types -> Bidi > Types
|
||||
*/
|
||||
export function buildPythonApiSidebar(docs: DocInfo[], currentSlug: string): SidebarEntry[] {
|
||||
const pythonApiDocs = docs.filter(
|
||||
|
||||
@@ -105,10 +105,9 @@ export function buildApiCounterpartMap(entries: readonly ApiDocEntry[]): Map<str
|
||||
for (const symbol of tsSymbols) {
|
||||
const pages = symbolToPyPages.get(symbol)
|
||||
if (!pages || pages.length === 0) continue
|
||||
// Symbols shared by several modules (e.g. Role in types.content and the
|
||||
// experimental bidi types): prefer the stable module, then the shortest
|
||||
// path, then alphabetical — deterministic and biased toward the page a
|
||||
// reader most likely wants.
|
||||
// Symbols shared by several modules: prefer the stable module, then the
|
||||
// shortest path, then alphabetical — deterministic and biased toward the
|
||||
// page a reader most likely wants.
|
||||
const best = [...pages].sort(
|
||||
(a, b) =>
|
||||
Number(a.includes('.experimental.')) - Number(b.includes('.experimental.')) ||
|
||||
|
||||
@@ -22,6 +22,13 @@ const PYTHON_API_PATTERN = /^(\.\.\/)*api-reference\/python\/([^#]+)\.md(#(.+))?
|
||||
*/
|
||||
const TS_API_PATTERN = /^(\.\.\/)*api-reference\/typescript\/(?:classes|interfaces)\/([^.]+)\.html(#(.+))?$/
|
||||
|
||||
/**
|
||||
* Map a strands.experimental.bidi module path to its strands.bidi equivalent.
|
||||
*/
|
||||
function resolveExperimentalBidiModule(modulePath: string): string {
|
||||
return modulePath.replace(/^strands\.experimental\.bidi(?=\.|$)/, 'strands.bidi')
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a link is an old-style API reference link that needs conversion.
|
||||
*/
|
||||
@@ -65,10 +72,10 @@ export function convertPythonApiLink(link: string): string | null {
|
||||
const symbolPart = hashContent.slice(modulePrefix.length)
|
||||
if (symbolPart.startsWith('.')) {
|
||||
// There's a symbol after the module path
|
||||
return `@api/python/${modulePrefix}#${symbolPart.slice(1)}`
|
||||
return `@api/python/${resolveExperimentalBidiModule(modulePrefix)}#${symbolPart.slice(1)}`
|
||||
} else if (symbolPart === '') {
|
||||
// Hash points to the module itself
|
||||
return `@api/python/${modulePrefix}`
|
||||
return `@api/python/${resolveExperimentalBidiModule(modulePrefix)}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +92,7 @@ export function convertPythonApiLink(link: string): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
const modulePath = hashParts.slice(0, moduleEndIndex).join('.')
|
||||
const modulePath = resolveExperimentalBidiModule(hashParts.slice(0, moduleEndIndex).join('.'))
|
||||
const symbol = hashParts.slice(moduleEndIndex).join('.')
|
||||
|
||||
if (symbol) {
|
||||
@@ -95,7 +102,7 @@ export function convertPythonApiLink(link: string): string | null {
|
||||
}
|
||||
} else {
|
||||
// No hash - convert path to dotted module notation
|
||||
const modulePath = 'strands.' + pathPart.split('/').join('.')
|
||||
const modulePath = resolveExperimentalBidiModule('strands.' + pathPart.split('/').join('.'))
|
||||
return `@api/python/${modulePath}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +223,13 @@ const PREFIX_SLUG_RULES: SlugRule[] = [
|
||||
// and only genuinely-missing old SDK deep links fall through to /sdk/*.
|
||||
{ match: startsWith('docs/user-guide/harness'), to: (m) => `docs/user-guide/sdk/${m[1]}` },
|
||||
|
||||
// bidi graduated from strands.experimental.bidi to strands.bidi. API pages are generated, so
|
||||
// STATIC_SLUG_REDIRECTS can't validate them as targets.
|
||||
{
|
||||
match: /^docs\/api\/python\/strands\.experimental\.bidi(\..+)?$/,
|
||||
to: (m) => `docs/api/python/strands.bidi${m[1] ?? ''}`,
|
||||
},
|
||||
|
||||
// Changelog stream rename: the former "harness" stream is now the "sdk" stream.
|
||||
{ match: startsWith('changelog/harness'), to: (m) => `changelog/sdk/${m[1]}` },
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ const BROKEN_LINKS_TEST_DATA: [string, string][] = [
|
||||
['../../../api-reference/python/agent/agent_result.md#strands.agent.agent_result', '@api/python/strands.agent.agent_result'],
|
||||
|
||||
// user-guide/concepts/bidirectional-streaming/*.mdx
|
||||
['../../../api-reference/python/experimental/bidi/agent.md', '@api/python/strands.experimental.bidi.agent'],
|
||||
['../../../api-reference/python/experimental/bidi/agent.md', '@api/python/strands.bidi.agent'],
|
||||
|
||||
// user-guide/concepts/multi-agent/graph.mdx
|
||||
['../../../api-reference/python/multiagent/graph.md#strands.multiagent.graph.GraphNode', '@api/python/strands.multiagent.graph#GraphNode'],
|
||||
@@ -167,16 +167,16 @@ const BROKEN_LINKS_TEST_DATA: [string, string][] = [
|
||||
['../../../api-reference/python/types/tools.md#strands.types.tools.ToolUse', '@api/python/strands.types.tools#ToolUse'],
|
||||
|
||||
// user-guide/concepts/bidirectional-streaming/models/google.mdx
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig', '@api/python/strands.experimental.bidi.models#AudioConfig'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.GoogleGeminiLiveModel', '@api/python/strands.experimental.bidi.models#GoogleGeminiLiveModel'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig', '@api/python/strands.bidi.models#AudioConfig'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.GoogleGeminiLiveModel', '@api/python/strands.bidi.models#GoogleGeminiLiveModel'],
|
||||
|
||||
// user-guide/concepts/bidirectional-streaming/models/bedrock.mdx
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig', '@api/python/strands.experimental.bidi.models#AudioConfig'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.BedrockNovaSonicModel', '@api/python/strands.experimental.bidi.models#BedrockNovaSonicModel'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig', '@api/python/strands.bidi.models#AudioConfig'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.BedrockNovaSonicModel', '@api/python/strands.bidi.models#BedrockNovaSonicModel'],
|
||||
|
||||
// user-guide/concepts/bidirectional-streaming/models/openai.mdx
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig', '@api/python/strands.experimental.bidi.models#AudioConfig'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.OpenAIRealtimeModel', '@api/python/strands.experimental.bidi.models#OpenAIRealtimeModel'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig', '@api/python/strands.bidi.models#AudioConfig'],
|
||||
['../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.OpenAIRealtimeModel', '@api/python/strands.bidi.models#OpenAIRealtimeModel'],
|
||||
]
|
||||
|
||||
describe('API Link Converter', () => {
|
||||
@@ -238,7 +238,7 @@ describe('API Link Converter', () => {
|
||||
|
||||
it('should convert bidi package paths', () => {
|
||||
expect(convertPythonApiLink('../api-reference/python/experimental/bidi/agent.md')).toBe(
|
||||
'@api/python/strands.experimental.bidi.agent'
|
||||
'@api/python/strands.bidi.agent'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -370,13 +370,13 @@ describe('API Link Converter', () => {
|
||||
convertApiLink(
|
||||
'../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig'
|
||||
)
|
||||
).toBe('@api/python/strands.experimental.bidi.models#AudioConfig')
|
||||
).toBe('@api/python/strands.bidi.models#AudioConfig')
|
||||
|
||||
expect(
|
||||
convertApiLink(
|
||||
'../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.GoogleGeminiLiveModel'
|
||||
)
|
||||
).toBe('@api/python/strands.experimental.bidi.models#GoogleGeminiLiveModel')
|
||||
).toBe('@api/python/strands.bidi.models#GoogleGeminiLiveModel')
|
||||
})
|
||||
|
||||
// user-guide/concepts/tools/custom-tools.mdx
|
||||
@@ -397,7 +397,7 @@ describe('API Link Converter', () => {
|
||||
expect(convertApiLink('../../../api-reference/python/types/content.md')).toBe('@api/python/strands.types.content')
|
||||
|
||||
expect(convertApiLink('../../../api-reference/python/experimental/bidi/agent.md')).toBe(
|
||||
'@api/python/strands.experimental.bidi.agent'
|
||||
'@api/python/strands.bidi.agent'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -74,7 +74,7 @@ Check out [GitHub](https://github.com/strands-agents/harness-sdk).
|
||||
|
||||
it('should handle deeply nested relative paths', () => {
|
||||
const input = `See [AudioConfig](../../../../api-reference/python/experimental/bidi/models.md#strands.experimental.bidi.models.AudioConfig).`
|
||||
const expected = `See [AudioConfig](@api/python/strands.experimental.bidi.models#AudioConfig).`
|
||||
const expected = `See [AudioConfig](@api/python/strands.bidi.models#AudioConfig).`
|
||||
expect(convertApiLinks(input)).toBe(expected)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user