44
55Simple adapter following the Agno pattern.
66"""
7+ import logging
78import json
89import uuid
910import asyncio
1011from typing import AsyncIterator , Any
1112from strands import Agent as StrandsAgentCore
13+
14+ logger = logging .getLogger (__name__ )
1215from 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