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
10 changes: 5 additions & 5 deletions strands-py/src/strands/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,11 @@ def __init__(
self.hooks = HookRegistry()

self._middleware_registry = MiddlewareRegistry()
self._plugin_registry = _PluginRegistry(self)

# Input handlers preserve registration order, so initialize routing before capability middleware.
if self._model_router is not None:
self._plugin_registry.add_and_init(self._model_router)

# In agentic mode, surface live token usage to the model so it can decide when to compress.
if context_manager == "agentic":
Expand All @@ -424,8 +429,6 @@ def __init__(

self._middleware_registry.add_middleware(InvokeModelStage.Input, create_token_usage_middleware())

self._plugin_registry = _PluginRegistry(self)

self._interrupt_state = _InterruptState()

# Checkpointing: pause at cycle boundaries when enabled.
Expand Down Expand Up @@ -489,9 +492,6 @@ def __init__(
# Register built-in plugins
self._plugin_registry.add_and_init(_ModelPlugin())

if self._model_router is not None:
self._plugin_registry.add_and_init(self._model_router)

plugins_to_register = resolved_plugins if resolved_plugins is not None else plugins
if plugins_to_register:
for plugin in plugins_to_register:
Expand Down
8 changes: 4 additions & 4 deletions strands-py/src/strands/event_loop/_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _calculate_delay(self, attempt: int) -> int:
delay: int = self._initial_delay * (2**attempt)
return min(delay, self._max_delay)

def _reset_retry_state(self) -> None:
def reset_retry_state(self) -> None:
"""Reset retry state to initial values."""
self._current_attempt = 0

Expand All @@ -101,7 +101,7 @@ async def _handle_after_invocation(self, event: AfterInvocationEvent) -> None:
Args:
event: The AfterInvocationEvent signaling invocation completion.
"""
self._reset_retry_state()
self.reset_retry_state()

async def _handle_after_model_call(self, event: AfterModelCallEvent) -> None:
"""Handle model call completion and determine if retry is needed.
Expand Down Expand Up @@ -129,12 +129,12 @@ async def _handle_after_model_call(self, event: AfterModelCallEvent) -> None:
"stop_reason=<%s> | model call succeeded, resetting retry state",
event.stop_response.stop_reason,
)
self._reset_retry_state()
self.reset_retry_state()
return

# Check if we have an exception and reset state if no exception
if event.exception is None:
self._reset_retry_state()
self.reset_retry_state()
return

if not self.is_retryable(event.exception):
Expand Down
15 changes: 12 additions & 3 deletions strands-py/src/strands/models/routing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
"""Model routing primitives.

``ModelRouter`` holds an ordered set of candidate models and resolves a concrete default. The
API is provisional and may change before it is finalized.
``ModelRouter`` holds an ordered set of candidate models and, per model call, selects one via a
``RoutingStrategy`` (default: ``FallbackStrategy``). When the selected model's retries are
exhausted, the router advances to the next candidate in declaration order. ``ContextFitStrategy``
selects by context-window capacity. The API is provisional and may change before it is finalized.
"""

from .router import CandidateInput, ModelRouter
from .router import CandidateInput, FallbackStrategy, ModelRouter, RoutingCandidate
from .strategies import ContextFitStrategy
from .strategy import RoutingContext, RoutingStrategy

__all__ = [
"CandidateInput",
"ContextFitStrategy",
"FallbackStrategy",
"ModelRouter",
"RoutingCandidate",
"RoutingContext",
"RoutingStrategy",
]
Loading