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 | EdgeConditionWithContext
2. 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
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
Overview
Add support for passing
invocation_stateto 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 theinvocation_statereceived 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.pythat allows future expansion with kwargs:2. Update GraphEdge Dataclass
3. Update Call Sites
Update all locations that call
should_traverse()to passinvocation_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
Files to Modify
src/strands/multiagent/graph.pyshould_traverse(), update all call sitestests/strands/multiagent/test_graph.pytests_integ/test_multiagent_graph.pyBackwards Compatibility
def condition(state: GraphState) -> bool) continue to work unchangeddef condition(state: GraphState, *, invocation_state=None, **kwargs) -> bool) receive invocation contextExample Usage
Acceptance Criteria
EdgeConditionWithContextProtocol defined with kwargs support for future expansionEdgeConditionUnion type supports both legacy and new condition signaturesGraphEdge.should_traverse()accepts and passesinvocation_stateinvocation_statethrough the execution flowinvocation_stateImplementation Notes
inspect.signature()for runtime detection of condition signature