@@ -5683,11 +5683,24 @@ def _responses_tools_to_chat_tools(
56835683 else None
56845684 )
56855685 description = cast (Optional [str ], tool .get ("description" )) or ""
5686+ text_tool_guidance = (
5687+ "This is a text tool. When calling it, the "
5688+ "`function.arguments` field itself must be the raw input string. "
5689+ "Do not wrap the input in JSON and do not use an object such as "
5690+ "'{\" input\" : \" ...\" }' or '{\" patch\" : \" ...\" }'."
5691+ )
56865692 if isinstance (syntax , str ) and isinstance (definition , str ):
56875693 if description :
5688- description = f"{ description } \n \n { syntax } :\n { definition } "
5694+ description = (
5695+ f"{ description } \n \n { text_tool_guidance } \n \n "
5696+ f"{ syntax } :\n { definition } "
5697+ )
56895698 else :
5690- description = f"{ syntax } :\n { definition } "
5699+ description = f"{ text_tool_guidance } \n \n { syntax } :\n { definition } "
5700+ elif description :
5701+ description = f"{ description } \n \n { text_tool_guidance } "
5702+ else :
5703+ description = text_tool_guidance
56915704 normalized_tools .append (
56925705 {
56935706 "type" : "function" ,
@@ -5697,8 +5710,17 @@ def _responses_tools_to_chat_tools(
56975710 "description" : description or None ,
56985711 "parameters" : {
56995712 "type" : "object" ,
5700- "properties" : {},
5701- "additionalProperties" : True ,
5713+ "properties" : {
5714+ "input" : {
5715+ "type" : "string" ,
5716+ "description" : (
5717+ "Raw input text for this tool. "
5718+ "For apply_patch, put the full patch here."
5719+ ),
5720+ },
5721+ },
5722+ "required" : ["input" ],
5723+ "additionalProperties" : False ,
57025724 },
57035725 "strict" : tool .get ("strict" ),
57045726 "content_type" : "text" ,
@@ -6534,8 +6556,8 @@ def _ensure_tool_stream_item(
65346556 )
65356557 ], item_state
65366558
6537- @staticmethod
65386559 def _update_tool_stream_item (
6560+ self ,
65396561 item : Dict [str , Any ],
65406562 * ,
65416563 call_id : Optional [str ],
@@ -6554,12 +6576,41 @@ def _update_tool_stream_item(
65546576 item ["name" ] = current_name + name_delta
65556577 if isinstance (arguments_delta , str ) and arguments_delta :
65566578 if item .get ("type" ) == "custom_tool_call" :
6557- item ["input" ] = cast (str , item .get ("input" , "" )) + arguments_delta
6579+ raw_arguments = cast (str , item .get ("_raw_arguments" , "" )) + arguments_delta
6580+ item ["_raw_arguments" ] = raw_arguments
6581+ normalized_input = self ._normalize_text_tool_payload (raw_arguments )
6582+ if normalized_input is not None :
6583+ item ["input" ] = normalized_input
65586584 else :
65596585 item ["arguments" ] = (
65606586 cast (str , item .get ("arguments" , "" )) + arguments_delta
65616587 )
65626588
6589+ @staticmethod
6590+ def _normalize_text_tool_payload (payload : str ) -> Optional [str ]:
6591+ if payload == "" :
6592+ return ""
6593+ stripped = payload .lstrip ()
6594+ if not stripped :
6595+ return ""
6596+ if stripped [0 ] not in '{["' :
6597+ return payload
6598+ try :
6599+ decoded = json .loads (payload )
6600+ except Exception :
6601+ return None
6602+ if isinstance (decoded , str ):
6603+ return decoded
6604+ if isinstance (decoded , dict ):
6605+ input_value = decoded .get ("input" )
6606+ if isinstance (input_value , str ):
6607+ return input_value
6608+ if len (decoded ) == 1 :
6609+ sole_value = next (iter (decoded .values ()))
6610+ if isinstance (sole_value , str ):
6611+ return sole_value
6612+ return payload
6613+
65636614 def _finalize_response_stream_items (
65646615 self ,
65656616 state : "OpenAIFormatter.ResponsesStream" ,
@@ -6645,6 +6696,7 @@ def _finalize_response_stream_items(
66456696 for tool_call_index in sorted (state .tool_items ):
66466697 item_state = state .tool_items [tool_call_index ]
66476698 item = item_state .item
6699+ item .pop ("_raw_arguments" , None )
66486700 if (
66496701 item .get ("status" ) != "in_progress"
66506702 and item .get ("type" ) != "custom_tool_call"
@@ -6772,6 +6824,7 @@ def convert_chat_chunk_to_response_events(
67726824 name = cast (Optional [str ], function .get ("name" )),
67736825 )
67746826 events .extend (added )
6827+ previous_input = cast (str , item_state .item .get ("input" , "" ))
67756828 self ._update_tool_stream_item (
67766829 item_state .item ,
67776830 call_id = cast (Optional [str ], tool_call .get ("id" )),
@@ -6781,13 +6834,19 @@ def convert_chat_chunk_to_response_events(
67816834 arguments_delta = function .get ("arguments" )
67826835 if isinstance (arguments_delta , str ) and arguments_delta :
67836836 if item_state .item .get ("type" ) == "custom_tool_call" :
6837+ current_input = cast (str , item_state .item .get ("input" , "" ))
6838+ if not current_input or current_input == previous_input :
6839+ continue
6840+ delta_text = current_input
6841+ if current_input .startswith (previous_input ):
6842+ delta_text = current_input [len (previous_input ) :]
67846843 events .append (
67856844 self ._response_event (
67866845 state ,
67876846 "response.custom_tool_call_input.delta" ,
67886847 item_id = cast (str , item_state .item ["id" ]),
67896848 output_index = item_state .output_index ,
6790- delta = arguments_delta ,
6849+ delta = delta_text ,
67916850 )
67926851 )
67936852 continue
0 commit comments