Skip to content

Commit b357d0b

Browse files
committed
interrupt - interruptible multi agent hook interface (strands-agents#1207)
1 parent 091dfcb commit b357d0b

File tree

4 files changed

+25
-7
lines changed

4 files changed

+25
-7
lines changed

src/strands/interrupt.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ def resume(self, prompt: "AgentInput") -> None:
9999

100100
self.interrupts[interrupt_id].response = interrupt_response
101101

102+
self.context["responses"] = contents
103+
102104
def to_dict(self) -> dict[str, Any]:
103105
"""Serialize to dict for session management."""
104106
return asdict(self)

src/strands/types/interrupt.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,14 @@ def approve(self, event: BeforeToolCallEvent) -> None:
7171
- Interrupts are session managed in-between return and user response.
7272
"""
7373

74-
from typing import TYPE_CHECKING, Any, Protocol, TypedDict
74+
from typing import Any, Protocol, TypedDict
7575

7676
from ..interrupt import Interrupt, InterruptException
7777

78-
if TYPE_CHECKING:
79-
from ..agent import Agent
80-
8178

8279
class _Interruptible(Protocol):
8380
"""Interface that adds interrupt support to hook events and tools."""
8481

85-
agent: "Agent"
86-
8782
def interrupt(self, name: str, reason: Any = None, response: Any = None) -> Any:
8883
"""Trigger the interrupt with a reason.
8984
@@ -97,9 +92,17 @@ def interrupt(self, name: str, reason: Any = None, response: Any = None) -> Any:
9792
9893
Raises:
9994
InterruptException: If human input is required.
95+
RuntimeError: If agent instance attribute not set.
10096
"""
97+
for attr_name in ["agent", "source"]:
98+
if hasattr(self, attr_name):
99+
agent = getattr(self, attr_name)
100+
break
101+
else:
102+
raise RuntimeError("agent instance attribute not set")
103+
101104
id = self._interrupt_id(name)
102-
state = self.agent._interrupt_state
105+
state = agent._interrupt_state
103106

104107
interrupt_ = state.interrupts.setdefault(id, Interrupt(id, name, reason, response))
105108
if interrupt_.response:

tests/strands/test_interrupt.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ def test_interrupt_state_resume():
9595
exp_response = "test response"
9696
assert tru_response == exp_response
9797

98+
tru_context = interrupt_state.context
99+
exp_context = {"responses": prompt}
100+
assert tru_context == exp_context
101+
98102

99103
def test_interrupt_state_resumse_deactivated():
100104
interrupt_state = _InterruptState(activated=False)

tests/strands/types/test_interrupt.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,12 @@ def test_interrupt_hook_event_interrupt_response_empty(interrupt, agent, interru
7777

7878
with pytest.raises(InterruptException):
7979
interrupt_hook_event.interrupt("test_name")
80+
81+
82+
def test_interrupt_hook_event_interrupt_missing_agent():
83+
class Event(_Interruptible):
84+
pass
85+
86+
event = Event()
87+
with pytest.raises(RuntimeError, match="agent instance attribute not set"):
88+
event.interrupt("test_name")

0 commit comments

Comments
 (0)