Skip to content

Commit 0faffef

Browse files
Rana Umar MajeedRana Umar Majeed
authored andcommitted
Assign unique Id to each frontend tool call
1 parent bd4254e commit 0faffef

File tree

1 file changed

+30
-1
lines changed
  • integrations/aws-strands/python/src/ag_ui_strands

1 file changed

+30
-1
lines changed

integrations/aws-strands/python/src/ag_ui_strands/agent.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
55
Simple adapter following the Agno pattern.
66
"""
7+
import logging
78
import json
89
import uuid
910
import asyncio
1011
from typing import AsyncIterator, Any
1112
from strands import Agent as StrandsAgentCore
13+
14+
logger = logging.getLogger(__name__)
1215
from ag_ui.core import (
1316
RunAgentInput,
1417
EventType,
@@ -87,6 +90,7 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
8790
last_msg = input_data.messages[-1]
8891
if last_msg.role == "tool":
8992
has_pending_tool_result = True
93+
logger.debug(f"Has pending tool result detected: tool_call_id={getattr(last_msg, 'tool_call_id', 'unknown')}, thread_id={input_data.thread_id}")
9094

9195
# Get the latest user message
9296
user_message = "Hello"
@@ -111,17 +115,22 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
111115
stop_text_streaming = False
112116
halt_event_stream = False
113117

118+
logger.debug(f"Starting agent run: thread_id={input_data.thread_id}, run_id={input_data.run_id}, has_pending_tool_result={has_pending_tool_result}, message_count={len(input_data.messages)}")
119+
114120
# Stream from Strands agent
115121
agent_stream = self.strands_agent.stream_async(user_message)
116122

117123
async for event in agent_stream:
124+
logger.debug(f"Received event: {event}")
118125
if halt_event_stream:
126+
logger.debug(f"Breaking event stream: halt_event_stream flag set to True (thread_id={input_data.thread_id})")
119127
break
120128

121129
# Skip lifecycle events
122130
if event.get("init_event_loop") or event.get("start_event_loop"):
123131
continue
124132
if event.get("complete") or event.get("force_stop"):
133+
logger.debug(f"Breaking event stream: received complete or force_stop event (thread_id={input_data.thread_id}, complete={event.get('complete')}, force_stop={event.get('force_stop')})")
125134
break
126135

127136
# Handle text streaming
@@ -180,6 +189,8 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
180189
tool_args = call_info.get("args")
181190
tool_input = call_info.get("input")
182191
behavior = self.config.tool_behaviors.get(tool_name) if tool_name else None
192+
193+
logger.debug(f"Processing tool result: tool_name={tool_name}, result_tool_id={result_tool_id}, has_pending_tool_result={has_pending_tool_result}, thread_id={input_data.thread_id}")
183194

184195
if not has_pending_tool_result and not (behavior and behavior.skip_messages_snapshot):
185196
assistant_msg = AssistantMessage(
@@ -249,13 +260,24 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
249260
)
250261
message_started = False
251262
halt_event_stream = True
263+
logger.debug(f"Breaking event stream: stop_streaming_after_result behavior triggered (thread_id={input_data.thread_id}, tool_name={tool_name})")
252264
break
253265

254266
# Handle tool calls
255267
elif "current_tool_use" in event and event["current_tool_use"]:
256268
tool_use = event["current_tool_use"]
257269
tool_name = tool_use.get("name")
258-
tool_use_id = tool_use.get("toolUseId") or str(uuid.uuid4())
270+
strands_tool_id = tool_use.get("toolUseId")
271+
272+
# Generate unique ID for frontend tools (to avoid ID conflicts across requests)
273+
# Use Strands' ID for backend tools (so result lookup works)
274+
is_frontend_tool = tool_name in frontend_tool_names
275+
if is_frontend_tool:
276+
tool_use_id = str(uuid.uuid4())
277+
else:
278+
tool_use_id = strands_tool_id or str(uuid.uuid4())
279+
280+
logger.debug(f"Tool call event received: tool_name={tool_name}, tool_use_id={tool_use_id}, strands_id={strands_tool_id}, is_frontend={is_frontend_tool}, already_seen={tool_use_id in tool_calls_seen}, thread_id={input_data.thread_id}")
259281

260282
if tool_name and tool_use_id not in tool_calls_seen:
261283
tool_input = tool_use.get("input", {})
@@ -268,6 +290,8 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
268290

269291
is_frontend_tool = tool_name in frontend_tool_names
270292
behavior = self.config.tool_behaviors.get(tool_name)
293+
294+
logger.debug(f"Processing tool call: tool_name={tool_name}, tool_use_id={tool_use_id}, is_frontend_tool={is_frontend_tool}, has_pending_tool_result={has_pending_tool_result}, tool_calls_seen_count={len(tool_calls_seen)}, thread_id={input_data.thread_id}")
271295
call_context = ToolCallContext(
272296
input_data=input_data,
273297
tool_name=tool_name,
@@ -297,8 +321,12 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
297321
name="PredictState",
298322
value=predict_state_payload,
299323
)
324+
if has_pending_tool_result:
325+
326+
logger.debug(f"Skipping tool call START event due to has_pending_tool_result for {tool_name} (tool_use_id={tool_use_id}, thread_id={input_data.thread_id})")
300327

301328
if not has_pending_tool_result:
329+
logger.debug(f"Emitting tool call events for {tool_name} (tool_use_id={tool_use_id}, thread_id={input_data.thread_id})")
302330
yield ToolCallStartEvent(
303331
type=EventType.TOOL_CALL_START,
304332
tool_call_id=tool_use_id,
@@ -335,6 +363,7 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[Any]:
335363
)
336364

337365
if is_frontend_tool and not (behavior and behavior.continue_after_frontend_call):
366+
logger.debug(f"Breaking event stream: frontend tool call completed (thread_id={input_data.thread_id}, tool_name={tool_name}, tool_call_id={tool_use_id}, has_behavior={behavior is not None}, continue_after_frontend_call={behavior.continue_after_frontend_call if behavior else None})")
338367
break
339368

340369
# End message if started

0 commit comments

Comments
 (0)