@@ -68,38 +68,26 @@ def _is_supported_image_ref(cls, image_ref: str) -> bool:
6868 return ext in cls ._ALLOWED_IMAGE_EXTENSIONS
6969
7070 @classmethod
71- def _coerce_image_urls (cls , image_urls : T .Any ) -> list [T .Any ]:
72- if image_urls is None :
73- return []
74- if isinstance (image_urls , str ):
75- return [image_urls ]
76- if isinstance (image_urls , (Sequence , AbstractSet )) and not isinstance (
77- image_urls , (str , bytes , bytearray )
71+ async def _collect_handoff_image_urls (
72+ cls ,
73+ run_context : ContextWrapper [AstrAgentContext ],
74+ image_urls_raw : T .Any ,
75+ ) -> list [str ]:
76+ candidates : list [T .Any ] = []
77+ if image_urls_raw is None :
78+ pass
79+ elif isinstance (image_urls_raw , str ):
80+ candidates .append (image_urls_raw )
81+ elif isinstance (image_urls_raw , (Sequence , AbstractSet )) and not isinstance (
82+ image_urls_raw , (str , bytes , bytearray )
7883 ):
79- return list (image_urls )
80- logger .warning (
81- "Unsupported image_urls type in handoff tool args: %s" ,
82- type (image_urls ).__name__ ,
83- )
84- return []
85-
86- @classmethod
87- def _filter_supported_image_urls (cls , candidates : list [T .Any ]) -> list [str ]:
88- normalized = normalize_and_dedupe_strings (candidates )
89- sanitized = [item for item in normalized if cls ._is_supported_image_ref (item )]
90- dropped_count = len (normalized ) - len (sanitized )
91- if dropped_count > 0 :
84+ candidates .extend (image_urls_raw )
85+ else :
9286 logger .warning (
93- "Dropped %d invalid image_urls entries in handoff tool args. " ,
94- dropped_count ,
87+ "Unsupported image_urls type in handoff tool args: %s " ,
88+ type ( image_urls_raw ). __name__ ,
9589 )
96- return sanitized
9790
98- @classmethod
99- async def _iter_event_image_paths (
100- cls , run_context : ContextWrapper [AstrAgentContext ]
101- ) -> list [str ]:
102- paths : list [str ] = []
10391 event = getattr (run_context .context , "event" , None )
10492 message_obj = getattr (event , "message_obj" , None )
10593 message = getattr (message_obj , "message" , None )
@@ -110,26 +98,24 @@ async def _iter_event_image_paths(
11098 try :
11199 path = await component .convert_to_file_path ()
112100 if path and cls ._is_supported_image_ref (path ):
113- paths .append (path )
101+ candidates .append (path )
114102 except Exception as e :
115103 logger .error (
116104 "Failed to convert handoff image component at index %d: %s" ,
117105 idx ,
118106 e ,
119107 exc_info = True ,
120108 )
121- return paths
122109
123- @classmethod
124- async def _prepare_handoff_image_urls (
125- cls ,
126- run_context : ContextWrapper [AstrAgentContext ],
127- image_urls : T .Any ,
128- ) -> list [str ]:
129- candidates = cls ._coerce_image_urls (image_urls )
130- event_paths = await cls ._iter_event_image_paths (run_context )
131- candidates .extend (event_paths )
132- return cls ._filter_supported_image_urls (candidates )
110+ normalized = normalize_and_dedupe_strings (candidates )
111+ sanitized = [item for item in normalized if cls ._is_supported_image_ref (item )]
112+ dropped_count = len (normalized ) - len (sanitized )
113+ if dropped_count > 0 :
114+ logger .warning (
115+ "Dropped %d invalid image_urls entries in handoff tool args." ,
116+ dropped_count ,
117+ )
118+ return sanitized
133119
134120 @classmethod
135121 async def execute (cls , tool , run_context , ** tool_args ):
@@ -151,7 +137,7 @@ async def execute(cls, tool, run_context, **tool_args):
151137 ):
152138 yield r
153139 return
154- async for r in cls ._execute_handoff (tool , run_context , tool_args ):
140+ async for r in cls ._execute_handoff (tool , run_context , ** tool_args ):
155141 yield r
156142 return
157143
@@ -254,14 +240,17 @@ async def _execute_handoff(
254240 cls ,
255241 tool : HandoffTool ,
256242 run_context : ContextWrapper [AstrAgentContext ],
257- tool_args : dict [ str , T .Any ] ,
243+ ** tool_args : T .Any ,
258244 ):
259245 input_ = tool_args .get ("input" )
260- image_urls = await cls ._prepare_handoff_image_urls (
246+ image_urls = await cls ._collect_handoff_image_urls (
261247 run_context ,
262248 tool_args .get ("image_urls" ),
263249 )
264- tool_args ["image_urls" ] = image_urls
250+ effective_tool_args : dict [str , T .Any ] = {
251+ ** tool_args ,
252+ "image_urls" : image_urls ,
253+ }
265254
266255 # Build handoff toolset from registered tools plus runtime computer tools.
267256 toolset = cls ._build_handoff_toolset (run_context , tool .agent .tools )
@@ -295,7 +284,7 @@ async def _execute_handoff(
295284 event = event ,
296285 chat_provider_id = prov_id ,
297286 prompt = input_ ,
298- image_urls = image_urls ,
287+ image_urls = effective_tool_args [ " image_urls" ] ,
299288 system_prompt = tool .agent .instructions ,
300289 tools = toolset ,
301290 contexts = contexts ,
@@ -360,9 +349,8 @@ async def _do_handoff_background(
360349 ) -> None :
361350 """Run the subagent handoff and, on completion, wake the main agent."""
362351 result_text = ""
363- prepared_tool_args = dict (tool_args )
364352 try :
365- async for r in cls ._execute_handoff (tool , run_context , prepared_tool_args ):
353+ async for r in cls ._execute_handoff (tool , run_context , ** tool_args ):
366354 if isinstance (r , mcp .types .CallToolResult ):
367355 for content in r .content :
368356 if isinstance (content , mcp .types .TextContent ):
@@ -379,7 +367,7 @@ async def _do_handoff_background(
379367 task_id = task_id ,
380368 tool_name = tool .name ,
381369 result_text = result_text ,
382- tool_args = prepared_tool_args ,
370+ tool_args = tool_args ,
383371 note = (
384372 event .get_extra ("background_note" )
385373 or f"Background task for subagent '{ tool .agent .name } ' finished."
0 commit comments