From 4b1bc1aeab850646a2c4e490c14a17d488ca2654 Mon Sep 17 00:00:00 2001 From: Patrick Gray Date: Wed, 15 Oct 2025 16:49:43 -0400 Subject: [PATCH] multiagents - temporarily raise exception when interrupted --- src/strands/hooks/registry.py | 9 ++++++--- src/strands/multiagent/graph.py | 8 ++++++++ src/strands/multiagent/swarm.py | 6 ++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/strands/hooks/registry.py b/src/strands/hooks/registry.py index 1cfd5c63e..564be85cb 100644 --- a/src/strands/hooks/registry.py +++ b/src/strands/hooks/registry.py @@ -7,6 +7,7 @@ via hook provider objects. """ +import logging from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Generator, Generic, Protocol, Type, TypeVar @@ -15,6 +16,8 @@ if TYPE_CHECKING: from ..agent import Agent +logger = logging.getLogger(__name__) + @dataclass class BaseHookEvent: @@ -219,9 +222,9 @@ def invoke_callbacks(self, event: TInvokeEvent) -> tuple[TInvokeEvent, list[Inte except InterruptException as exception: interrupt = exception.interrupt if interrupt.name in interrupts: - raise ValueError( - f"interrupt_name=<{interrupt.name}> | interrupt name used more than once" - ) from exception + message = f"interrupt_name=<{interrupt.name}> | interrupt name used more than once" + logger.error(message) + raise ValueError(message) from exception # Each callback is allowed to raise their own interrupt. interrupts[interrupt.name] = interrupt diff --git a/src/strands/multiagent/graph.py b/src/strands/multiagent/graph.py index 60299c1b5..1dbbfc3af 100644 --- a/src/strands/multiagent/graph.py +++ b/src/strands/multiagent/graph.py @@ -578,6 +578,14 @@ async def _execute_node(self, node: GraphNode, invocation_state: dict[str, Any]) else: agent_response = await node.executor.invoke_async(node_input, invocation_state=invocation_state) + if agent_response.stop_reason == "interrupt": + node.executor.messages.pop() # remove interrupted tool use message + node.executor._interrupt_state.deactivate() + + raise RuntimeError( + "user raised interrupt from agent | interrupts are not yet supported in graphs" + ) + # Extract metrics from agent response usage = Usage(inputTokens=0, outputTokens=0, totalTokens=0) metrics = Metrics(latencyMs=0) diff --git a/src/strands/multiagent/swarm.py b/src/strands/multiagent/swarm.py index 42efd5742..7542b1b85 100644 --- a/src/strands/multiagent/swarm.py +++ b/src/strands/multiagent/swarm.py @@ -637,6 +637,12 @@ async def _execute_node( node.reset_executor_state() result = await node.executor.invoke_async(node_input, invocation_state=invocation_state) + if result.stop_reason == "interrupt": + node.executor.messages.pop() # remove interrupted tool use message + node.executor._interrupt_state.deactivate() + + raise RuntimeError("user raised interrupt from agent | interrupts are not yet supported in swarms") + execution_time = round((time.time() - start_time) * 1000) # Create NodeResult