Skip to content

Commit e7d3eb9

Browse files
feat(steering): move steering from experimental to production (#1853)
1 parent 4a26f4a commit e7d3eb9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1211
-764
lines changed

AGENTS.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,18 @@ strands-agents/
130130
│ ├── plugins/ # Plugin system
131131
│ │ ├── plugin.py # Plugin base class
132132
│ │ ├── decorator.py # @hook decorator
133-
│ │ ├── registry.py # PluginRegistry for tracking plugins
134-
│ │ └── skills/ # Agent Skills integration
135-
│ │ ├── __init__.py # Skills package exports
136-
│ │ ├── skill.py # Skill dataclass
137-
│ │ └── agent_skills.py # AgentSkills plugin implementation
133+
│ │ └── registry.py # PluginRegistry for tracking plugins
138134
│ │
139135
│ ├── handlers/ # Event handlers
140136
│ │ └── callback_handler.py # Callback handling
141137
│ │
138+
│ ├── vended_plugins/ # Production plugin implementations
139+
│ │ ├── steering/ # Agent steering system
140+
│ │ │ ├── context_providers/ # Context data providers (e.g., ledger)
141+
│ │ │ ├── core/ # Base classes, actions, context
142+
│ │ │ └── handlers/ # Handler implementations (e.g., LLM)
143+
│ │ └── skills/ # AgentSkills.io integration (Skill, AgentSkills)
144+
│ │
142145
│ ├── experimental/ # Experimental features (API may change)
143146
│ │ ├── agent_config.py # Experimental agent config
144147
│ │ ├── bidi/ # Bidirectional streaming
@@ -151,11 +154,8 @@ strands-agents/
151154
│ │ ├── hooks/ # Experimental hooks
152155
│ │ │ ├── events.py
153156
│ │ │ └── multiagent/
154-
│ │ ├── steering/ # Agent steering
155-
│ │ │ ├── context_providers/
156-
│ │ │ ├── core/
157-
│ │ │ └── handlers/
158-
│ │ └── tools/ # Experimental tools (deprecation shims)
157+
│ │ ├── steering/ # Deprecated aliases for vended_plugins/steering
158+
│ │ └── tools/ # Deprecated aliases for strands.tools
159159
│ │
160160
│ ├── __init__.py # Public API exports
161161
│ ├── interrupt.py # Interrupt handling

src/strands/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from .agent.agent import Agent
55
from .agent.base import AgentBase
66
from .event_loop._retry import ModelRetryStrategy
7-
from .plugins import AgentSkills, Plugin, Skill
7+
from .plugins import Plugin
88
from .tools.decorator import tool
99
from .types.tools import ToolContext
10+
from .vended_plugins.skills import AgentSkills, Skill
1011

1112
__all__ = [
1213
"Agent",
Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
1-
"""Steering system for Strands agents.
1+
"""Deprecated: Steering has moved to strands.vended_plugins.steering.
22
3-
Provides contextual guidance for agents through modular prompting with progressive disclosure.
4-
Instead of front-loading all instructions, steering handlers provide just-in-time feedback
5-
based on local context data populated by context callbacks.
6-
7-
Core components:
8-
9-
- SteeringHandler: Base class for guidance logic with local context
10-
- SteeringContextCallback: Protocol for context update functions
11-
- SteeringContextProvider: Protocol for multi-event context providers
12-
- ToolSteeringAction/ModelSteeringAction: Proceed/Guide/Interrupt decisions
13-
14-
Usage:
15-
handler = LLMSteeringHandler(system_prompt="...")
16-
agent = Agent(tools=[...], plugins=[handler])
3+
This module provides backwards-compatible aliases that emit deprecation warnings.
174
"""
185

19-
# Core primitives
20-
# Context providers
21-
from .context_providers.ledger_provider import (
22-
LedgerAfterToolCall,
23-
LedgerBeforeToolCall,
24-
LedgerProvider,
25-
)
26-
from .core.action import Guide, Interrupt, ModelSteeringAction, Proceed, ToolSteeringAction
27-
from .core.context import SteeringContextCallback, SteeringContextProvider
28-
from .core.handler import SteeringHandler
29-
30-
# Handler implementations
31-
from .handlers.llm import LLMPromptMapper, LLMSteeringHandler
32-
33-
__all__ = [
6+
import warnings
7+
from typing import Any
8+
9+
_DEPRECATED_NAMES = {
3410
"ToolSteeringAction",
3511
"ModelSteeringAction",
3612
"Proceed",
@@ -44,4 +20,20 @@
4420
"LedgerProvider",
4521
"LLMSteeringHandler",
4622
"LLMPromptMapper",
47-
]
23+
}
24+
25+
26+
def __getattr__(name: str) -> Any:
27+
if name in _DEPRECATED_NAMES:
28+
from strands.vended_plugins import steering
29+
30+
warnings.warn(
31+
f"{name} has been moved to production. Use {name} from strands.vended_plugins.steering instead.",
32+
DeprecationWarning,
33+
stacklevel=2,
34+
)
35+
return getattr(steering, name)
36+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
37+
38+
39+
__all__: list[str] = []
Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
"""Context providers for steering evaluation."""
2-
3-
from .ledger_provider import (
4-
LedgerAfterToolCall,
5-
LedgerBeforeToolCall,
6-
LedgerProvider,
7-
)
8-
9-
__all__ = [
10-
"LedgerAfterToolCall",
11-
"LedgerBeforeToolCall",
12-
"LedgerProvider",
13-
]
1+
"""Deprecated: Use strands.vended_plugins.steering.context_providers instead."""
2+
3+
import warnings
4+
from typing import Any
5+
6+
_TARGET_MODULE = "strands.vended_plugins.steering.context_providers"
7+
8+
9+
def __getattr__(name: str) -> Any:
10+
from strands.vended_plugins.steering import context_providers
11+
12+
obj = getattr(context_providers, name, None)
13+
if obj is not None:
14+
warnings.warn(
15+
f"{name} has been moved to production. Use {name} from {_TARGET_MODULE} instead.",
16+
DeprecationWarning,
17+
stacklevel=2,
18+
)
19+
return obj
20+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
21+
22+
23+
__all__: list[str] = []
Lines changed: 15 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,23 @@
1-
"""Ledger context provider for comprehensive agent activity tracking.
1+
"""Deprecated: Use strands.vended_plugins.steering.context_providers.ledger_provider instead."""
22

3-
Tracks complete agent activity ledger including tool calls, conversation history,
4-
and timing information. This comprehensive audit trail enables steering handlers
5-
to make informed guidance decisions based on agent behavior patterns and history.
6-
7-
Data captured:
8-
9-
- Tool call history with inputs, outputs, timing, success/failure
10-
- Conversation messages and agent responses
11-
- Session metadata and timing information
12-
- Error patterns and recovery attempts
13-
14-
Usage:
15-
Use as context provider functions or mix into steering handlers.
16-
"""
17-
18-
import logging
19-
from datetime import datetime
3+
import warnings
204
from typing import Any
215

22-
from ....hooks.events import AfterToolCallEvent, BeforeToolCallEvent
23-
from ..core.context import SteeringContext, SteeringContextCallback, SteeringContextProvider
24-
25-
logger = logging.getLogger(__name__)
26-
27-
28-
class LedgerBeforeToolCall(SteeringContextCallback[BeforeToolCallEvent]):
29-
"""Context provider for ledger tracking before tool calls."""
30-
31-
def __init__(self) -> None:
32-
"""Initialize the ledger provider."""
33-
self.session_start = datetime.now().isoformat()
34-
35-
def __call__(self, event: BeforeToolCallEvent, steering_context: SteeringContext, **kwargs: Any) -> None:
36-
"""Update ledger before tool call."""
37-
ledger = steering_context.data.get("ledger") or {}
38-
39-
if not ledger:
40-
ledger = {
41-
"session_start": self.session_start,
42-
"tool_calls": [],
43-
"conversation_history": [],
44-
"session_metadata": {},
45-
}
46-
47-
tool_call_entry = {
48-
"timestamp": datetime.now().isoformat(),
49-
"tool_use_id": event.tool_use.get("toolUseId"),
50-
"tool_name": event.tool_use.get("name"),
51-
"tool_args": event.tool_use.get("input", {}),
52-
"status": "pending",
53-
}
54-
ledger["tool_calls"].append(tool_call_entry)
55-
steering_context.data.set("ledger", ledger)
56-
57-
58-
class LedgerAfterToolCall(SteeringContextCallback[AfterToolCallEvent]):
59-
"""Context provider for ledger tracking after tool calls."""
60-
61-
def __call__(self, event: AfterToolCallEvent, steering_context: SteeringContext, **kwargs: Any) -> None:
62-
"""Update ledger after tool call."""
63-
ledger = steering_context.data.get("ledger") or {}
6+
_TARGET_MODULE = "strands.vended_plugins.steering.context_providers.ledger_provider"
647

65-
if ledger.get("tool_calls"):
66-
tool_use_id = event.tool_use.get("toolUseId")
678

68-
# Search for the matching tool call in the ledger to update it
69-
for call in reversed(ledger["tool_calls"]):
70-
if call.get("tool_use_id") == tool_use_id and call.get("status") == "pending":
71-
call.update(
72-
{
73-
"completion_timestamp": datetime.now().isoformat(),
74-
"status": event.result["status"],
75-
"result": event.result["content"],
76-
"error": str(event.exception) if event.exception else None,
77-
}
78-
)
79-
steering_context.data.set("ledger", ledger)
80-
break
9+
def __getattr__(name: str) -> Any:
10+
from strands.vended_plugins.steering.context_providers import ledger_provider
8111

12+
obj = getattr(ledger_provider, name, None)
13+
if obj is not None:
14+
warnings.warn(
15+
f"{name} has been moved to production. Use {name} from {_TARGET_MODULE} instead.",
16+
DeprecationWarning,
17+
stacklevel=2,
18+
)
19+
return obj
20+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
8221

83-
class LedgerProvider(SteeringContextProvider):
84-
"""Combined ledger context provider for both before and after tool calls."""
8522

86-
def context_providers(self, **kwargs: Any) -> list[SteeringContextCallback]:
87-
"""Return ledger context providers with shared state."""
88-
return [
89-
LedgerBeforeToolCall(),
90-
LedgerAfterToolCall(),
91-
]
23+
__all__: list[str] = []
Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1-
"""Core steering system interfaces and base classes."""
1+
"""Deprecated: Use strands.vended_plugins.steering.core instead."""
22

3-
from .action import Guide, Interrupt, ModelSteeringAction, Proceed, ToolSteeringAction
4-
from .handler import SteeringHandler
3+
import warnings
4+
from typing import Any
55

6-
__all__ = ["ToolSteeringAction", "ModelSteeringAction", "Proceed", "Guide", "Interrupt", "SteeringHandler"]
6+
_TARGET_MODULE = "strands.vended_plugins.steering.core"
7+
8+
9+
def __getattr__(name: str) -> Any:
10+
from strands.vended_plugins.steering import core
11+
12+
obj = getattr(core, name, None)
13+
if obj is not None:
14+
warnings.warn(
15+
f"{name} has been moved to production. Use {name} from {_TARGET_MODULE} instead.",
16+
DeprecationWarning,
17+
stacklevel=2,
18+
)
19+
return obj
20+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
21+
22+
23+
__all__: list[str] = []
Lines changed: 16 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,23 @@
1-
"""SteeringAction types for steering evaluation results.
1+
"""Deprecated: Use strands.vended_plugins.steering.core.action instead."""
22

3-
Defines structured outcomes from steering handlers that determine how agent actions
4-
should be handled. SteeringActions enable modular prompting by providing just-in-time
5-
feedback rather than front-loading all instructions in monolithic prompts.
3+
import warnings
4+
from typing import Any
65

7-
Flow:
8-
SteeringHandler.steer_*() → SteeringAction → Event handling
9-
↓ ↓ ↓
10-
Evaluate context Action type Execution modified
6+
_TARGET_MODULE = "strands.vended_plugins.steering.core.action"
117

12-
SteeringAction types:
13-
Proceed: Allow execution to continue without intervention
14-
Guide: Provide contextual guidance to redirect the agent
15-
Interrupt: Pause execution for human input
168

17-
Extensibility:
18-
New action types can be added to the union. Always handle the default
19-
case in pattern matching to maintain backward compatibility.
20-
"""
9+
def __getattr__(name: str) -> Any:
10+
from strands.vended_plugins.steering.core import action
2111

22-
from typing import Annotated, Literal
12+
obj = getattr(action, name, None)
13+
if obj is not None:
14+
warnings.warn(
15+
f"{name} has been moved to production. Use {name} from {_TARGET_MODULE} instead.",
16+
DeprecationWarning,
17+
stacklevel=2,
18+
)
19+
return obj
20+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
2321

24-
from pydantic import BaseModel, Field
2522

26-
27-
class Proceed(BaseModel):
28-
"""Allow execution to continue without intervention.
29-
30-
The action proceeds as planned. The reason provides context
31-
for logging and debugging purposes.
32-
"""
33-
34-
type: Literal["proceed"] = "proceed"
35-
reason: str
36-
37-
38-
class Guide(BaseModel):
39-
"""Provide contextual guidance to redirect the agent.
40-
41-
The agent receives the reason as contextual feedback to help guide
42-
its behavior. The specific handling depends on the steering context
43-
(e.g., tool call vs. model response).
44-
"""
45-
46-
type: Literal["guide"] = "guide"
47-
reason: str
48-
49-
50-
class Interrupt(BaseModel):
51-
"""Pause execution for human input via interrupt system.
52-
53-
Execution is paused and human input is requested through Strands'
54-
interrupt system. The human can approve or deny the operation, and their
55-
decision determines whether execution continues or is cancelled.
56-
"""
57-
58-
type: Literal["interrupt"] = "interrupt"
59-
reason: str
60-
61-
62-
# Context-specific steering action types
63-
ToolSteeringAction = Annotated[Proceed | Guide | Interrupt, Field(discriminator="type")]
64-
"""Steering actions valid for tool steering (steer_before_tool).
65-
66-
- Proceed: Allow tool execution to continue
67-
- Guide: Cancel tool and provide feedback for alternative approaches
68-
- Interrupt: Pause for human input before tool execution
69-
"""
70-
71-
ModelSteeringAction = Annotated[Proceed | Guide, Field(discriminator="type")]
72-
"""Steering actions valid for model steering (steer_after_model).
73-
74-
- Proceed: Accept model response without modification
75-
- Guide: Discard model response and retry with guidance
76-
"""
23+
__all__: list[str] = []

0 commit comments

Comments
 (0)