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:
mehtarac
2026-09-29 16:41:47 -04:00
committed by GitHub
co-authored by Patrick Gray Murat Kaan Meral
parent ff114a9bd1
commit 8dbc128eae
107 changed files with 998 additions and 417 deletions
+1 -1
View File
@@ -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`)
+7 -4
View File
@@ -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
+1 -1
View File
@@ -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(
+3 -4
View File
@@ -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.')) ||
+11 -4
View File
@@ -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}`
}
}
+7
View File
@@ -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]}` },
+11 -11
View File
@@ -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'
)
})
})
+1 -1
View File
@@ -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)
})