@@ -401,6 +401,55 @@ def is_opening_code_block(content):
401401 return len (backtick_segments ) > 1 and len (backtick_segments ) % 2 == 0
402402
403403
404+ _OPENAI_TOOL_DISPLAY_NAMES = {
405+ 'web_search_call' : 'Web Search' ,
406+ 'file_search_call' : 'File Search' ,
407+ 'computer_call' : 'Computer Use' ,
408+ }
409+
410+
411+ def _render_openai_tool_call_handler (item : dict , done : bool ) -> str :
412+ """Render an OpenAI Responses API server-side tool item as a <details> block.
413+
414+ Handles web_search_call, file_search_call, and computer_call items whose
415+ schemas are defined in the openai-python SDK (generated from OpenAPI spec).
416+ """
417+ item_type = item .get ('type' , '' )
418+ call_id = item .get ('id' , '' )
419+ display_name = _OPENAI_TOOL_DISPLAY_NAMES .get (item_type , item_type )
420+
421+ # Build a short summary of what the tool did
422+ summary = ''
423+ if item_type == 'web_search_call' :
424+ action = item .get ('action' , {})
425+ if isinstance (action , dict ):
426+ atype = action .get ('type' , '' )
427+ if atype == 'search' :
428+ queries = action .get ('queries' ) or []
429+ query = action .get ('query' , '' )
430+ summary = f'Search: { ", " .join (str (q ) for q in queries )} ' if queries else (f'Search: { query } ' if query else '' )
431+ elif atype == 'open_page' :
432+ summary = f'Open page: { action .get ("url" , "" )} ' if action .get ('url' ) else ''
433+ elif atype == 'find_in_page' :
434+ summary = f'Find in page: { action .get ("pattern" , "" )} ' if action .get ('pattern' ) else ''
435+ elif item_type == 'file_search_call' :
436+ queries = item .get ('queries' , [])
437+ if queries :
438+ summary = f'Queries: { ", " .join (str (q ) for q in queries )} '
439+ elif item_type == 'computer_call' :
440+ action = item .get ('action' )
441+ actions = item .get ('actions' )
442+ if isinstance (action , dict ):
443+ summary = f'Action: { action .get ("type" , "unknown" )} '
444+ elif isinstance (actions , list ) and actions :
445+ summary = f'Actions: { ", " .join (a .get ("type" , "?" ) for a in actions if isinstance (a , dict ))} '
446+
447+ escaped_name = html .escape (display_name )
448+ if done :
449+ return f'<details type="tool_calls" done="true" id="{ call_id } " name="{ escaped_name } " arguments="">\n <summary>Tool Executed</summary>\n { html .escape (summary )} \n </details>\n '
450+ return f'<details type="tool_calls" done="false" id="{ call_id } " name="{ escaped_name } " arguments="">\n <summary>Executing...</summary>\n </details>\n '
451+
452+
404453def serialize_output (output : list ) -> str :
405454 """
406455 Convert OR-aligned output items to HTML for display.
@@ -452,49 +501,12 @@ def serialize_output(output: list) -> str:
452501 # Already handled inline with function_call above
453502 pass
454503
455- elif item_type in ('web_search_call' , 'file_search_call' , 'computer_call' ):
456- # OpenAI Responses API built-in server-side tool output items.
457- # These are emitted when the model uses native tools (web_search,
458- # file_search, computer_use) through the Responses API. Render as
459- # collapsible tool call blocks matching the function_call pattern.
504+ elif item_type in _OPENAI_TOOL_DISPLAY_NAMES :
460505 if content and not content .endswith ('\n ' ):
461506 content += '\n '
462-
463- call_id = item .get ('id' , '' )
464507 status = item .get ('status' , 'in_progress' )
465-
466- # Derive a human-readable display name
467- display_names = {
468- 'web_search_call' : 'Web Search' ,
469- 'file_search_call' : 'File Search' ,
470- 'computer_call' : 'Computer Use' ,
471- }
472- display_name = display_names .get (item_type , item_type )
473-
474- # Extract a summary of what the tool did for the details body
475- summary_text = ''
476- if item_type == 'web_search_call' :
477- action = item .get ('action' , {})
478- if isinstance (action , dict ):
479- query = action .get ('query' , '' )
480- if query :
481- summary_text = f'Query: { query } '
482- elif item_type == 'file_search_call' :
483- queries = item .get ('queries' , [])
484- if queries :
485- summary_text = f'Queries: { ", " .join (str (q ) for q in queries )} '
486- elif item_type == 'computer_call' :
487- action = item .get ('action' , {})
488- if isinstance (action , dict ):
489- action_type = action .get ('type' , '' )
490- if action_type :
491- summary_text = f'Action: { action_type } '
492-
493- done = status == 'completed' or idx != len (output ) - 1
494- if done :
495- content += f'<details type="tool_calls" done="true" id="{ call_id } " name="{ html .escape (display_name )} " arguments="">\n <summary>Tool Executed</summary>\n { html .escape (summary_text )} \n </details>\n '
496- else :
497- content += f'<details type="tool_calls" done="false" id="{ call_id } " name="{ html .escape (display_name )} " arguments="">\n <summary>Executing...</summary>\n </details>\n '
508+ done = status in ('completed' , 'failed' , 'incomplete' ) or idx != len (output ) - 1
509+ content += _render_openai_tool_call_handler (item , done )
498510
499511 elif item_type == 'reasoning' :
500512 reasoning_content = ''
0 commit comments