-
Notifications
You must be signed in to change notification settings - Fork 636
Description
Overview
Add support for passing invocation_state to edge condition functions in Graph execution, enabling conditional edge traversal based on runtime context provided during graph invocation.
Problem Statement
Currently, edge condition functions only have access to GraphState, limiting their ability to make decisions based on runtime context passed during graph invocation. This makes it difficult to conditionally enable nodes based on the invocation_state received in the graph invocation.
Implementation Requirements
Based on discussion and repository analysis, the implementation should use a Protocol-based Union type for backwards compatibility and future extensibility.
Technical Approach
1. Define Edge Condition Protocol
Create a new Protocol in src/strands/multiagent/graph.py that allows future expansion with kwargs:
from typing import Protocol, runtime_checkable
@runtime_checkable
class EdgeConditionWithContext(Protocol):
"""Protocol for edge conditions that receive invocation context.
This protocol allows conditions to access both GraphState and invocation_state,
and is designed to be expanded with additional kwargs in the future.
"""
def __call__(
self,
state: GraphState,
*,
invocation_state: dict[str, Any] | None = None,
**kwargs: Any
) -> bool: ...
# Legacy condition type (backwards compatible)
LegacyEdgeCondition = Callable[[GraphState], bool]
# Union type supporting both old and new signatures
EdgeCondition = LegacyEdgeCondition | EdgeConditionWithContext2. Update GraphEdge Dataclass
@dataclass
class GraphEdge:
...
if new style:
return self.condition(state, invocation_state=invocation_state)
# Legacy: single positional parameter only
return self.condition(state)3. Update Call Sites
Update all locations that call should_traverse() to pass invocation_state:
Graph._is_node_ready_with_conditions()(line ~837)Graph._compute_ready_nodes_for_resume()(line ~1190)Graph._build_node_input()(line ~1080)
4. Update GraphBuilder Type Hint
def add_edge(
self,
from_node: str | GraphNode,
to_node: str | GraphNode,
condition: EdgeCondition | None = None,
) -> GraphEdge:Files to Modify
| File | Changes |
|---|---|
src/strands/multiagent/graph.py |
Add Protocol, update types, update should_traverse(), update all call sites |
tests/strands/multiagent/test_graph.py |
Add tests for new-style conditions with invocation_state |
tests_integ/test_multiagent_graph.py |
Add integration tests for conditions with invocation_state |
Backwards Compatibility
- Old-style conditions (
def condition(state: GraphState) -> bool) continue to work unchanged - New-style conditions (
def condition(state: GraphState, *, invocation_state=None, **kwargs) -> bool) receive invocation context - Runtime signature inspection ensures correct calling convention
- No deprecation warnings - both styles supported indefinitely
Example Usage
# Old-style (still works)
def check_completion(state: GraphState) -> bool:
return "data_processor" in {n.node_id for n in state.completed_nodes}
# New-style (with invocation context)
def should_run_premium_node(
state: GraphState,
*,
invocation_state: dict[str, Any] | None = None,
**kwargs: Any
) -> bool:
if invocation_state is None:
return False
return invocation_state.get("user_tier") == "premium"
# Graph setup
builder = GraphBuilder()
builder.add_node(start_agent, "start")
builder.add_node(premium_agent, "premium_processor")
builder.add_edge("start", "premium_processor", condition=should_run_premium_node)
graph = builder.build()
# Invocation with context
result = graph(task="process data", invocation_state={"user_tier": "premium"})Acceptance Criteria
-
EdgeConditionWithContextProtocol defined with kwargs support for future expansion -
EdgeConditionUnion type supports both legacy and new condition signatures -
GraphEdge.should_traverse()accepts and passesinvocation_state - All call sites updated to pass
invocation_statethrough the execution flow - Existing tests pass without modification (backwards compatibility)
- New unit tests for conditions that use
invocation_state - New integration test demonstrating the use case
- Type hints pass mypy strict mode
Implementation Notes
- Use
inspect.signature()for runtime detection of condition signature - The Protocol approach allows adding more context parameters in the future without breaking changes
- Consider caching signature inspection results for performance if needed