Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions examples/strands/01_basic_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# /// script
# requires-python = ">=3.11"
# dependencies = ["band-sdk[strands]"]
#
# [tool.uv.sources]
# band-sdk = { git = "https://github.com/band-ai/band-sdk-python.git" }
# ///
"""
Basic Strands Agents example.

This is the simplest way to create a Band agent with AWS Strands Agents.
The adapter handles tool registration automatically.

Strands has no provider-prefix model string (a bare string means a Bedrock
model id), so the OpenAI provider is constructed explicitly; it reads
OPENAI_API_KEY from the environment.

Run with:
uv run examples/strands/01_basic_agent.py
"""

from __future__ import annotations

import asyncio
import logging
import os

from dotenv import load_dotenv
from strands.models.openai import OpenAIModel

from setup_logging import setup_logging
from band import Agent
from band.adapters import StrandsAdapter

setup_logging()
logger = logging.getLogger(__name__)


async def main() -> None:
load_dotenv()

ws_url = os.getenv("BAND_WS_URL")
rest_url = os.getenv("BAND_REST_URL")

if not ws_url:
raise ValueError("BAND_WS_URL environment variable is required")
if not rest_url:
raise ValueError("BAND_REST_URL environment variable is required")

# Create adapter with framework-specific settings
adapter = StrandsAdapter(
model=OpenAIModel(model_id="gpt-5.4-mini"),
custom_section="You are a helpful assistant. Be concise and friendly.",
)

# Create and start agent
agent = Agent.from_config(
"strands_agent",
adapter=adapter,
ws_url=ws_url,
rest_url=rest_url,
)

logger.info("Starting Strands agent...")
await agent.run()


if __name__ == "__main__":
asyncio.run(main())
81 changes: 81 additions & 0 deletions examples/strands/02_custom_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# /// script
# requires-python = ">=3.11"
# dependencies = ["band-sdk[strands]"]
#
# [tool.uv.sources]
# band-sdk = { git = "https://github.com/band-ai/band-sdk-python.git" }
# ///
"""
Strands agent with custom tools and capabilities.

Shows the portable ``CustomToolDef`` (InputModel, handler) form shared across
adapters, plus enabling memory/contact tools and execution/usage event
emission via AdapterFeatures.

Run with:
uv run examples/strands/02_custom_tools.py
"""

from __future__ import annotations

import asyncio
import logging
import os

from dotenv import load_dotenv
from pydantic import BaseModel
from strands.models.openai import OpenAIModel

from setup_logging import setup_logging
from band import Agent
from band.adapters import StrandsAdapter
from band.core.types import AdapterFeatures, Capability, Emit

setup_logging()
logger = logging.getLogger(__name__)


class WeatherInput(BaseModel):
"""Get the weather for a city."""

city: str


async def get_weather(args: WeatherInput) -> str:
return f"{args.city}: sunny, 22°C"


async def main() -> None:
load_dotenv()

ws_url = os.getenv("BAND_WS_URL")
rest_url = os.getenv("BAND_REST_URL")

if not ws_url:
raise ValueError("BAND_WS_URL environment variable is required")
if not rest_url:
raise ValueError("BAND_REST_URL environment variable is required")

adapter = StrandsAdapter(
model=OpenAIModel(model_id="gpt-5.4-mini"),
custom_section="You can check the weather with the weather tool.",
additional_tools=[(WeatherInput, get_weather)], # CustomToolDef tuple
features=AdapterFeatures(
emit=frozenset({Emit.EXECUTION, Emit.USAGE}),
capabilities=frozenset({Capability.MEMORY, Capability.CONTACTS}),
),
)

agent = Agent.from_config(
"strands_agent",
adapter=adapter,
ws_url=ws_url,
rest_url=rest_url,
)

logger.info("Starting Strands agent with custom tools...")
await agent.run()


if __name__ == "__main__":
asyncio.run(main())
12 changes: 12 additions & 0 deletions examples/strands/setup_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Shared logging configuration for examples."""

from __future__ import annotations

import logging

from band import configure_logging


def setup_logging(level: int = logging.INFO) -> None:
"""Configure logging to show only band logs, hiding noisy dependencies."""
configure_logging(level)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ google_adk = [
agno = [
"agno>=2.6.0",
]
strands = [
"strands-agents[openai]>=1.40,<2",
]

# dev extra includes ALL framework deps for testing EXCEPT crewai,
# which conflicts with both parlant and pydantic-ai (see tool.uv.conflicts).
Expand Down Expand Up @@ -184,6 +187,8 @@ dev = [
"google-adk>=1.0.0,<2",
# Include Agno for testing
"agno>=2.6.0",
# Include Strands Agents for testing
"strands-agents[openai]>=1.40,<2",
# Include letta-client for testing
"letta-client>=0.1.0",
# Include bridge deps for testing
Expand Down
7 changes: 7 additions & 0 deletions src/band/adapters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
uv add band-sdk[google_adk]
uv add band-sdk[opencode]
uv add band-sdk[slack]
uv add band-sdk[strands]
"""

from __future__ import annotations
Expand Down Expand Up @@ -52,6 +53,7 @@
from band.adapters.slack import SlackAdapter as SlackAdapter
from band.adapters.slack import SlackApp as SlackApp
from band.adapters.slack import SlackSessionState as SlackSessionState
from band.adapters.strands import StrandsAdapter as StrandsAdapter

__all__ = [
"LangGraphAdapter",
Expand All @@ -78,6 +80,7 @@
"SlackAdapter",
"SlackApp",
"SlackSessionState",
"StrandsAdapter",
]


Expand Down Expand Up @@ -171,6 +174,10 @@ def __getattr__(name: str) -> type:
from band.adapters.letta import LettaAdapterConfig

return LettaAdapterConfig
elif name == "StrandsAdapter":
from band.adapters.strands import StrandsAdapter

return StrandsAdapter
elif name in ("SlackAdapter", "SlackApp", "SlackSessionState"):
from band.adapters.slack import SlackAdapter, SlackApp, SlackSessionState

Expand Down
Loading