Skip to content

Commit f47d3b9

Browse files
committed
feat: enable multiagent session persistence
1 parent d17fc60 commit f47d3b9

24 files changed

+1289
-579
lines changed

src/strands/experimental/multiagent_session/__init__.py renamed to src/strands/experimental/multiagent_hooks/__init__.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,19 @@
55
"""
66

77
from .multiagent_events import (
8-
AfterGraphInvocationEvent,
8+
AfterMultiAgentInvocationEvent,
99
AfterNodeInvocationEvent,
10-
BeforeGraphInvocationEvent,
10+
BeforeMultiAgentInvocationEvent,
1111
BeforeNodeInvocationEvent,
1212
MultiAgentInitializationEvent,
1313
)
14-
from .multiagent_state import MultiAgentState, MultiAgentType
15-
from .multiagent_state_adapter import MultiAgentAdapter
14+
from .persistence_hooks import PersistentHook
1615

1716
__all__ = [
18-
"BeforeGraphInvocationEvent",
19-
"AfterGraphInvocationEvent",
17+
"BeforeMultiAgentInvocationEvent",
18+
"AfterMultiAgentInvocationEvent",
2019
"MultiAgentInitializationEvent",
2120
"BeforeNodeInvocationEvent",
2221
"AfterNodeInvocationEvent",
23-
"MultiAgentState",
24-
"MultiAgentAdapter",
25-
"MultiAgentType",
22+
"PersistentHook",
2623
]
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,82 @@
11
"""Multi-agent execution lifecycle events for hook system integration.
22
3-
This module defines event classes that are triggered at key points during
4-
multi-agent orchestrator execution, enabling hooks to respond to lifecycle
5-
events for purposes like persistence, monitoring, and debugging.
6-
7-
Event Types:
8-
- Initialization: When orchestrator starts up
9-
- Before/After Graph: Start/end of overall execution
10-
- Before/After Node: Start/end of individual node execution
3+
These events are fired by orchestrators (Graph/Swarm) at key points so
4+
hooks can persist, monitor, or debug execution. No intermediate state model
5+
is used—hooks read from the orchestrator directly.
116
"""
127

138
from dataclasses import dataclass
14-
from typing import TYPE_CHECKING
9+
from typing import TYPE_CHECKING, Any
1510

16-
from ...hooks.registry import HookEvent
17-
from .multiagent_state import MultiAgentState
11+
from ...hooks.registry import BaseHookEvent
1812

1913
if TYPE_CHECKING:
2014
from ...multiagent.base import MultiAgentBase
2115

2216

2317
@dataclass
24-
class MultiAgentInitializationEvent(HookEvent):
18+
class MultiAgentInitializationEvent(BaseHookEvent):
2519
"""Event triggered when multi-agent orchestrator initializes.
2620
2721
Attributes:
2822
orchestrator: The multi-agent orchestrator instance
29-
state: Current state of the orchestrator
23+
invocation_state: Configuration that user pass in
3024
"""
3125

3226
orchestrator: "MultiAgentBase"
33-
state: MultiAgentState
27+
invocation_state: dict[str, Any] | None = None
3428

3529

3630
@dataclass
37-
class BeforeGraphInvocationEvent(HookEvent):
31+
class BeforeMultiAgentInvocationEvent(BaseHookEvent):
3832
"""Event triggered before orchestrator execution begins.
3933
4034
Attributes:
4135
orchestrator: The multi-agent orchestrator instance
42-
state: Current state before execution starts
36+
invocation_state: Configuration that user pass in
4337
"""
4438

4539
orchestrator: "MultiAgentBase"
46-
state: MultiAgentState
40+
invocation_state: dict[str, Any] | None = None
4741

4842

4943
@dataclass
50-
class BeforeNodeInvocationEvent(HookEvent):
44+
class BeforeNodeInvocationEvent(BaseHookEvent):
5145
"""Event triggered before individual node execution.
5246
5347
Attributes:
5448
orchestrator: The multi-agent orchestrator instance
55-
next_node_to_execute: ID of the node about to be executed
49+
invocation_state: Configuration that user pass in
5650
"""
5751

5852
orchestrator: "MultiAgentBase"
5953
next_node_to_execute: str
54+
invocation_state: dict[str, Any] | None = None
6055

6156

6257
@dataclass
63-
class AfterNodeInvocationEvent(HookEvent):
58+
class AfterNodeInvocationEvent(BaseHookEvent):
6459
"""Event triggered after individual node execution completes.
6560
6661
Attributes:
6762
orchestrator: The multi-agent orchestrator instance
6863
executed_node: ID of the node that just completed execution
69-
state: Updated state after node execution
64+
invocation_state: Configuration that user pass in
7065
"""
7166

7267
orchestrator: "MultiAgentBase"
7368
executed_node: str
74-
state: MultiAgentState
69+
invocation_state: dict[str, Any] | None = None
7570

7671

7772
@dataclass
78-
class AfterGraphInvocationEvent(HookEvent):
73+
class AfterMultiAgentInvocationEvent(BaseHookEvent):
7974
"""Event triggered after orchestrator execution completes.
8075
8176
Attributes:
8277
orchestrator: The multi-agent orchestrator instance
83-
state: Final state after execution completes
78+
invocation_state: Configuration that user pass in
8479
"""
8580

8681
orchestrator: "MultiAgentBase"
87-
state: MultiAgentState
82+
invocation_state: dict[str, Any] | None = None

src/strands/experimental/multiagent_session/persistence_hooks.py renamed to src/strands/experimental/multiagent_hooks/persistence_hooks.py

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,37 @@
1111
"""
1212

1313
import threading
14-
from typing import Optional
14+
from typing import TYPE_CHECKING
1515

1616
from ...hooks.registry import HookProvider, HookRegistry
17-
from ...multiagent.base import MultiAgentBase
1817
from ...session import SessionManager
1918
from .multiagent_events import (
20-
AfterGraphInvocationEvent,
19+
AfterMultiAgentInvocationEvent,
2120
AfterNodeInvocationEvent,
22-
BeforeGraphInvocationEvent,
21+
BeforeMultiAgentInvocationEvent,
2322
BeforeNodeInvocationEvent,
2423
MultiAgentInitializationEvent,
25-
MultiAgentState,
2624
)
27-
from .multiagent_state_adapter import MultiAgentAdapter
2825

26+
if TYPE_CHECKING:
27+
from ...multiagent.base import MultiAgentBase
2928

30-
def _get_multiagent_state(
31-
multiagent_state: Optional[MultiAgentState],
32-
orchestrator: MultiAgentBase,
33-
) -> MultiAgentState:
34-
if multiagent_state is not None:
35-
return multiagent_state
3629

37-
return MultiAgentAdapter.create_multi_agent_state(orchestrator=orchestrator)
38-
39-
40-
class MultiAgentHook(HookProvider):
30+
class PersistentHook(HookProvider):
4131
"""Hook provider for automatic multi-agent session persistence.
4232
4333
This hook automatically persists multi-agent orchestrator state at key
4434
execution points to enable resumable execution after interruptions.
4535
46-
Args:
47-
session_manager: SessionManager instance for state persistence
48-
session_id: Unique identifier for the session
4936
"""
5037

51-
def __init__(self, session_manager: SessionManager, session_id: str):
38+
def __init__(self, session_manager: SessionManager):
5239
"""Initialize the multi-agent persistence hook.
5340
5441
Args:
5542
session_manager: SessionManager instance for state persistence
56-
session_id: Unique identifier for the session
5743
"""
5844
self._session_manager = session_manager
59-
self._session_id = session_id
6045
self._lock = threading.RLock()
6146

6247
def register_hooks(self, registry: HookRegistry, **kwargs: object) -> None:
@@ -67,40 +52,40 @@ def register_hooks(self, registry: HookRegistry, **kwargs: object) -> None:
6752
**kwargs: Additional keyword arguments (unused)
6853
"""
6954
registry.add_callback(MultiAgentInitializationEvent, self._on_initialization)
70-
registry.add_callback(BeforeGraphInvocationEvent, self._on_before_graph)
55+
registry.add_callback(BeforeMultiAgentInvocationEvent, self._on_before_multiagent)
7156
registry.add_callback(BeforeNodeInvocationEvent, self._on_before_node)
7257
registry.add_callback(AfterNodeInvocationEvent, self._on_after_node)
73-
registry.add_callback(AfterGraphInvocationEvent, self._on_after_graph)
58+
registry.add_callback(AfterMultiAgentInvocationEvent, self._on_after_multiagent)
7459

75-
def _on_initialization(self, event: MultiAgentInitializationEvent):
60+
# TODO: We can add **kwarg or invocation_state later if we need to persist
61+
def _on_initialization(self, event: MultiAgentInitializationEvent) -> None:
7662
"""Persist state when multi-agent orchestrator initializes."""
77-
self._persist(_get_multiagent_state(event.state, event.orchestrator))
63+
self._persist(event.orchestrator)
7864

79-
def _on_before_graph(self, event: BeforeGraphInvocationEvent):
80-
"""Hook called before graph execution starts."""
65+
def _on_before_multiagent(self, event: BeforeMultiAgentInvocationEvent) -> None:
66+
"""Persist state when multi-agent orchestrator initializes."""
8167
pass
8268

83-
def _on_before_node(self, event: BeforeNodeInvocationEvent):
69+
def _on_before_node(self, event: BeforeNodeInvocationEvent) -> None:
8470
"""Hook called before individual node execution."""
8571
pass
8672

87-
def _on_after_node(self, event: AfterNodeInvocationEvent):
73+
def _on_after_node(self, event: AfterNodeInvocationEvent) -> None:
8874
"""Persist state after each node completes execution."""
89-
multi_agent_state = _get_multiagent_state(multiagent_state=event.state, orchestrator=event.orchestrator)
90-
self._persist(multi_agent_state)
75+
self._persist(event.orchestrator)
9176

92-
def _on_after_graph(self, event: AfterGraphInvocationEvent):
77+
def _on_after_multiagent(self, event: AfterMultiAgentInvocationEvent) -> None:
9378
"""Persist final state after graph execution completes."""
94-
multiagent_state = _get_multiagent_state(multiagent_state=event.state, orchestrator=event.orchestrator)
95-
self._persist(multiagent_state)
79+
self._persist(event.orchestrator)
9680

97-
def _persist(self, multiagent_state: MultiAgentState) -> None:
81+
def _persist(self, orchestrator: "MultiAgentBase") -> None:
9882
"""Persist the provided MultiAgentState using the configured SessionManager.
9983
10084
This method is synchronized across threads/tasks to avoid write races.
10185
10286
Args:
103-
multiagent_state: State to persist
87+
orchestrator: State to persist
10488
"""
89+
current_state = orchestrator.get_state_from_orchestrator()
10590
with self._lock:
106-
self._session_manager.write_multi_agent_state(multiagent_state)
91+
self._session_manager.write_multi_agent_json(current_state)

src/strands/experimental/multiagent_session/multiagent_state.py

Lines changed: 0 additions & 110 deletions
This file was deleted.

0 commit comments

Comments
 (0)