Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ A bug report comes in. You have the exact YAML config. Load it, replay it, debug
|---------|-------------|
| **YAML-first config** | Models, agents, tools, hooks, MCP, orchestrations — all in one file |
| **Full YAML power** | Variables (`${VAR:-default}`), anchors (`&ref` / `*ref`), `x-` scratch pads, multi-file merge |
| **Multi-model support** | Bedrock, OpenAI, Ollama, Gemini — swap with one line |
| **Multi-model support** | Bedrock, Anthropic, OpenAI, Ollama, Gemini — swap with one line |
| **MCP servers & clients** | Launch local servers from Python files, connect to remote HTTP endpoints, or spawn stdio subprocesses |
| **MCP lifecycle management** | Startup ordering, readiness polling, graceful shutdown — servers before clients, always |
| **Orchestration modes** | Delegate (agent-as-tool), Swarm (peer handoffs), Graph (DAG pipelines) — arbitrarily nestable |
Expand Down
25 changes: 21 additions & 4 deletions docs/configuration/Chapter_02.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ models:

creative:
provider: openai
model_id: gpt-4o
model_id: gpt-5.5
params:
temperature: 0.9

Expand All @@ -30,9 +30,26 @@ models:
| Provider | Package Required | Example `model_id` |
|----------|-----------------|---------------------|
| `bedrock` | *(included)* | `us.anthropic.claude-sonnet-4-6-v1:0` |
| `openai` | `pip install strands-compose[openai]` | `gpt-4o` |
| `openai` | `pip install strands-compose[openai]` | `gpt-5.5` |
| `ollama` | `pip install strands-compose[ollama]` | `llama3.2` |
| `gemini` | `pip install strands-compose[gemini]` | `gemini-2.0-flash` |
| `gemini` | `pip install strands-compose[gemini]` | `gemini-3.6-flash` |
| `anthropic` | `pip install strands-compose[anthropic]` | `claude-sonnet-4-6` |

> **Anthropic note:** the `anthropic` provider talks to the Anthropic API directly
> (not through Bedrock) and **requires `max_tokens` in `params`** — the API mandates
> it, unlike the other providers:
>
> ```yaml
> models:
> claude:
> provider: anthropic
> model_id: claude-sonnet-4-6
> params:
> max_tokens: 4096 # required by the Anthropic API
> ```
>
> Set the API key via the `ANTHROPIC_API_KEY` environment variable, or pass
> `params.client_args.api_key`.

## How Agents Reference Models

Expand Down Expand Up @@ -97,7 +114,7 @@ models:

> **Tips & Tricks**
>
> - When combined with `vars`, you can swap models at runtime: `MODEL=gpt-4o python main.py`. See [Chapter 3](Chapter_03.md).
> - When combined with `vars`, you can swap models at runtime: `MODEL=gpt-5.5 python main.py`. See [Chapter 3](Chapter_03.md).
> - If you omit `model` on an agent entirely, strands picks its built-in default (Bedrock). This is fine for quick tests but explicit is better for production.
> - The `params` dict preserves types from YAML — integers stay integers, floats stay floats. This matters for parameters like `max_tokens` that must be an int.

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/Chapter_18.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ log_level: "WARNING" # Optional: DEBUG, INFO, WARNING, ERROR
```yaml
models:
name:
provider: bedrock | openai | ollama | gemini | module.path:CustomModel
provider: bedrock | anthropic | openai | ollama | gemini | module.path:CustomModel
model_id: "model-identifier"
params: {} # Provider-specific kwargs
```
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ openai = [
gemini = [
"strands-agents[gemini]>=1.35.0,<2.0.0",
]
anthropic = [
"strands-agents[anthropic]>=1.35.0,<2.0.0",
]

[project.urls]
Homepage = "https://github.com/strands-compose/sdk-python"
Expand Down
15 changes: 6 additions & 9 deletions src/strands_compose/config/loaders/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,12 @@ def normalize(raw: dict) -> dict:
"""
raw = dict(raw) # do not mutate the input
version = str(raw.get("version", "1"))
match version:
case "1":
pass # current canonical schema
case _:
raise ValueError(
f"This config declares schema version '{version}', but this "
f"strands-compose version only supports version '1'.\n"
f"Upgrade: pip install --upgrade strands-compose"
)
if version != "1":
raise ValueError(
f"This config declares schema version '{version}', but this "
f"strands-compose version only supports version '1'.\n"
f"Upgrade: pip install --upgrade strands-compose"
)
raw["version"] = "1"
return raw

Expand Down
5 changes: 3 additions & 2 deletions src/strands_compose/config/resolvers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
def resolve_model(model_def: ModelDef) -> Model:
"""Resolve a ModelDef to a strands model instance.

Built-in providers (``"ollama"``, ``"bedrock"``, ``"openai"``,
``"gemini"``) are dispatched via :func:`~strands_compose.models.create_model`.
Built-in providers (``"bedrock"``, ``"anthropic"``, ``"ollama"``,
``"openai"``, ``"gemini"``) are dispatched via
:func:`~strands_compose.models.create_model`.
Any other ``provider`` value is treated as an import spec
(``module.path:ClassName``) for a custom :class:`~strands.models.Model`
subclass.
Expand Down
88 changes: 48 additions & 40 deletions src/strands_compose/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

from strands.models import Model

PROVIDERS = ("bedrock", "ollama", "openai", "gemini")
PROVIDERS = ("bedrock", "ollama", "openai", "gemini", "anthropic")


def create_model(provider: str, model_id: str, **params: Any) -> Model:
"""Dispatch to the appropriate model factory by provider name.

Args:
provider: ``"ollama"`` or ``"bedrock"`` or ``"openai"`` or ``"gemini"``.
provider: ``"bedrock"``, ``"anthropic"``, ``"ollama"``, ``"openai"``, ``"gemini"``.
model_id: Model identifier.
**params: Provider-specific keyword arguments.

Expand All @@ -24,46 +24,54 @@ def create_model(provider: str, model_id: str, **params: Any) -> Model:
ValueError: If the provider is unknown.
ImportError: If a required optional provider package is not installed.
"""
match provider.lower():
case "bedrock":
from strands.models.bedrock import BedrockModel
provider_name = provider.lower()
if provider_name == "bedrock":
from strands.models.bedrock import BedrockModel

return BedrockModel(model_id=model_id, **params)
return BedrockModel(model_id=model_id, **params)

case "ollama":
try:
from strands.models.ollama import OllamaModel
except ImportError:
raise ImportError(
"The 'ollama' provider requires the ollama extra:\n"
" pip install strands-compose[ollama]\n"
"Or install directly: pip install strands-agents[ollama]"
) from None
return OllamaModel(model_id=model_id, **params)
if provider_name == "ollama":
try:
from strands.models.ollama import OllamaModel
except ImportError:
raise ImportError(
"The 'ollama' provider requires the ollama extra:\n"
" pip install strands-compose[ollama]\n"
"Or install directly: pip install strands-agents[ollama]"
) from None
return OllamaModel(model_id=model_id, **params)

case "openai":
try:
from strands.models.openai import OpenAIModel
except ImportError:
raise ImportError(
"The 'openai' provider requires the openai extra:\n"
" pip install strands-compose[openai]\n"
"Or install directly: pip install strands-agents[openai]"
) from None
return OpenAIModel(model_id=model_id, **params)
if provider_name == "openai":
try:
from strands.models.openai import OpenAIModel
except ImportError:
raise ImportError(
"The 'openai' provider requires the openai extra:\n"
" pip install strands-compose[openai]\n"
"Or install directly: pip install strands-agents[openai]"
) from None
return OpenAIModel(model_id=model_id, **params)

case "gemini":
try:
from strands.models.gemini import GeminiModel
except ImportError:
raise ImportError(
"The 'gemini' provider requires the gemini extra:\n"
" pip install strands-compose[gemini]\n"
"Or install directly: pip install strands-agents[gemini]"
) from None
return GeminiModel(model_id=model_id, **params)
if provider_name == "gemini":
try:
from strands.models.gemini import GeminiModel
except ImportError:
raise ImportError(
"The 'gemini' provider requires the gemini extra:\n"
" pip install strands-compose[gemini]\n"
"Or install directly: pip install strands-agents[gemini]"
) from None
return GeminiModel(model_id=model_id, **params)

case _:
raise ValueError(
f"Unknown model provider '{provider}'.\nAvailable: {', '.join(PROVIDERS)}."
)
if provider_name == "anthropic":
try:
from strands.models.anthropic import AnthropicModel
except ImportError:
raise ImportError(
"The 'anthropic' provider requires the anthropic extra:\n"
" pip install strands-compose[anthropic]\n"
"Or install directly: pip install strands-agents[anthropic]"
) from None
return AnthropicModel(model_id=model_id, **params)

raise ValueError(f"Unknown model provider '{provider}'.\nAvailable: {', '.join(PROVIDERS)}.")
2 changes: 1 addition & 1 deletion tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def graph_orchestration(
entry_name: str, edges: list[tuple[str, str]], **overrides: Any
) -> GraphOrchestrationDef:
"""Build a graph orchestration from ``(from, to)`` edge tuples."""
edge_defs = [GraphEdgeDef(from_agent=a, to_agent=b) for a, b in edges] # ty: ignore[unknown-argument, missing-argument]
edge_defs = [GraphEdgeDef(from_agent=a, to_agent=b) for a, b in edges]
return GraphOrchestrationDef(entry_name=entry_name, edges=edge_defs, **overrides)


Expand Down
Loading