@@ -117,6 +117,22 @@ function validateInputItem(item, param) {
117117 if ( ! ( typeof item . output === "string" || Array . isArray ( item . output ) ) ) throw invalid ( "function_call_output requires string or array output" , `${ param } .output` ) ;
118118 return ;
119119 }
120+ if ( item . type === "custom_tool_call" ) {
121+ if ( typeof item . call_id !== "string" || ! item . call_id ) throw invalid ( "custom_tool_call requires call_id" , `${ param } .call_id` ) ;
122+ if ( typeof item . name !== "string" || ! item . name ) throw invalid ( "custom_tool_call requires name" , `${ param } .name` ) ;
123+ if ( typeof item . input !== "string" ) throw invalid ( "custom_tool_call input must be a string" , `${ param } .input` ) ;
124+ return ;
125+ }
126+ if ( item . type === "custom_tool_call_output" ) {
127+ if ( typeof item . call_id !== "string" || ! item . call_id ) throw invalid ( "custom_tool_call_output requires call_id" , `${ param } .call_id` ) ;
128+ if ( ! ( typeof item . output === "string" || Array . isArray ( item . output ) ) ) {
129+ throw invalid ( "custom_tool_call_output requires string or array output" , `${ param } .output` ) ;
130+ }
131+ if ( Array . isArray ( item . output ) ) {
132+ item . output . forEach ( ( part , index ) => validateContentPart ( part , `${ param } .output[${ index } ]` ) ) ;
133+ }
134+ return ;
135+ }
120136 if ( item . type !== undefined && item . type !== "message" ) throw invalid ( "Unsupported input item" , `${ param } .type` ) ;
121137 if ( ! [ "user" , "assistant" , "system" , "developer" ] . includes ( item . role ) ) throw invalid ( "Invalid message role" , `${ param } .role` ) ;
122138 if ( item . content == null ) {
@@ -142,15 +158,18 @@ function inputMessages(input) {
142158 if ( reasoning ) pendingReasoning . push ( reasoning ) ;
143159 continue ;
144160 }
145- if ( item . type === "function_call" ) {
161+ if ( item . type === "function_call" || item . type === "custom_tool_call" ) {
146162 if ( ! pendingAssistant ) {
147163 pendingAssistant = { role : "assistant" , content : null , tool_calls : [ ] } ;
148164 messages . push ( pendingAssistant ) ;
149165 }
166+ const custom = item . type === "custom_tool_call" ;
150167 const call = {
151168 id : item . call_id ,
152- type : "function" ,
153- function : { name : item . name , arguments : item . arguments } ,
169+ type : custom ? "custom" : "function" ,
170+ ...( custom
171+ ? { custom : { name : item . name , input : item . input } }
172+ : { function : { name : item . name , arguments : item . arguments } } ) ,
154173 } ;
155174 if ( pendingReasoning . length ) {
156175 call . extra_content = { openai : { reasoning_items : pendingReasoning . map ( ( reasoning ) => ( { ...reasoning } ) ) } } ;
@@ -160,8 +179,15 @@ function inputMessages(input) {
160179 }
161180 pendingAssistant = null ;
162181 pendingReasoning = [ ] ;
163- if ( item . type === "function_call_output" ) {
164- messages . push ( { role : "tool" , tool_call_id : item . call_id , content : textContent ( item . output ) } ) ;
182+ if ( item . type === "function_call_output" || item . type === "custom_tool_call_output" ) {
183+ messages . push ( {
184+ role : "tool" ,
185+ tool_call_id : item . call_id ,
186+ content : item . type === "custom_tool_call_output" && Array . isArray ( item . output )
187+ ? JSON . parse ( JSON . stringify ( item . output ) )
188+ : textContent ( item . output ) ,
189+ ...( item . type === "custom_tool_call_output" ? { extra_content : { openai : { custom_tool_call_output : true } } } : { } ) ,
190+ } ) ;
165191 continue ;
166192 }
167193 messages . push ( {
@@ -275,7 +301,10 @@ function outputFromMessage(message, responseId, request) {
275301 }
276302 }
277303 const callId = call . id || `call_${ crypto . randomUUID ( ) . replaceAll ( "-" , "" ) } ` ;
278- output . push ( { id : `fc_${ responseId . slice ( 5 ) } _${ index } ` , type : "function_call" , status : "completed" , call_id : callId , name : call . function ?. name , arguments : call . function ?. arguments || "" } ) ;
304+ const custom = call . type === "custom" && call . custom ;
305+ output . push ( custom
306+ ? { id : `ctc_${ responseId . slice ( 5 ) } _${ index } ` , type : "custom_tool_call" , status : "completed" , call_id : callId , name : call . custom . name , input : call . custom . input || "" }
307+ : { id : `fc_${ responseId . slice ( 5 ) } _${ index } ` , type : "function_call" , status : "completed" , call_id : callId , name : call . function ?. name , arguments : call . function ?. arguments || "" } ) ;
279308 }
280309 return output ;
281310}
@@ -351,17 +380,26 @@ async function pipeChatCompletionsSseToResponses(streamPipe, sink, model, reques
351380 if ( ! call ) {
352381 const outputIndex = slot ( ) ;
353382 const callId = callDelta . id || `call_${ crypto . randomUUID ( ) . replaceAll ( "-" , "" ) } ` ;
354- call = { id : `fc_${ responseId . slice ( 5 ) } _${ calls . size } ` , call_id : callId , type : "function_call" , status : "in_progress" , name : callDelta . function ?. name || "" , arguments : "" , argumentDeltas : [ ] , outputIndex, emitted : false } ;
383+ const custom = callDelta . type === "custom" || ! ! callDelta . custom ;
384+ call = { id : `${ custom ? "ctc" : "fc" } _${ responseId . slice ( 5 ) } _${ calls . size } ` , call_id : callId , type : custom ? "custom_tool_call" : "function_call" , status : "in_progress" , name : custom ? callDelta . custom ?. name || "" : callDelta . function ?. name || "" , input : "" , arguments : "" , argumentDeltas : [ ] , outputIndex, emitted : false } ;
355385 calls . set ( key , call ) ;
356386 entries . push ( call ) ;
357387 if ( ! deferForReasoning ) {
358- const pending = { id : call . id , type : "function_call" , status : "in_progress" , call_id : call . call_id , name : call . name , arguments : "" } ;
388+ const pending = call . type === "custom_tool_call"
389+ ? { id : call . id , type : call . type , status : "in_progress" , call_id : call . call_id , name : call . name , input : "" }
390+ : { id : call . id , type : call . type , status : "in_progress" , call_id : call . call_id , name : call . name , arguments : "" } ;
359391 writeEvent ( sink , "response.output_item.added" , { output_index : outputIndex , item : pending } , sequence ++ ) ;
360392 call . emitted = true ;
361393 }
362394 }
363395 if ( callDelta . id ) call . call_id = callDelta . id ;
364396 if ( callDelta . function ?. name ) call . name = callDelta . function . name ;
397+ if ( callDelta . custom ?. name ) call . name = callDelta . custom . name ;
398+ if ( callDelta . custom ?. input ) {
399+ call . input += callDelta . custom . input ;
400+ call . argumentDeltas . push ( callDelta . custom . input ) ;
401+ if ( call . emitted ) writeEvent ( sink , "response.custom_tool_call_input.delta" , { item_id : call . id , output_index : call . outputIndex , delta : callDelta . custom . input } , sequence ++ ) ;
402+ }
365403 if ( callDelta . function ?. arguments ) {
366404 call . arguments += callDelta . function . arguments ;
367405 call . argumentDeltas . push ( callDelta . function . arguments ) ;
@@ -406,13 +444,21 @@ async function pipeChatCompletionsSseToResponses(streamPipe, sink, model, reques
406444 writeEvent ( sink , "response.output_item.done" , { output_index : outputIndex , item } , sequence ++ ) ;
407445 } else {
408446 if ( ! entry . emitted ) {
409- const pending = { id : entry . id , type : "function_call" , status : "in_progress" , call_id : entry . call_id , name : entry . name , arguments : "" } ;
447+ const custom = entry . type === "custom_tool_call" ;
448+ const pending = custom
449+ ? { id : entry . id , type : entry . type , status : "in_progress" , call_id : entry . call_id , name : entry . name , input : "" }
450+ : { id : entry . id , type : entry . type , status : "in_progress" , call_id : entry . call_id , name : entry . name , arguments : "" } ;
410451 writeEvent ( sink , "response.output_item.added" , { output_index : outputIndex , item : pending } , sequence ++ ) ;
411- for ( const delta of entry . argumentDeltas ) writeEvent ( sink , "response.function_call_arguments.delta" , { item_id : entry . id , output_index : outputIndex , delta } , sequence ++ ) ;
452+ for ( const delta of entry . argumentDeltas ) writeEvent ( sink , custom ? "response.custom_tool_call_input.delta" : "response.function_call_arguments.delta" , { item_id : entry . id , output_index : outputIndex , delta } , sequence ++ ) ;
412453 }
413- const item = { id : entry . id , type : "function_call" , status : "completed" , call_id : entry . call_id , name : entry . name , arguments : entry . arguments } ;
454+ const custom = entry . type === "custom_tool_call" ;
455+ const item = custom
456+ ? { id : entry . id , type : entry . type , status : "completed" , call_id : entry . call_id , name : entry . name , input : entry . input }
457+ : { id : entry . id , type : entry . type , status : "completed" , call_id : entry . call_id , name : entry . name , arguments : entry . arguments } ;
414458 output . push ( item ) ;
415- writeEvent ( sink , "response.function_call_arguments.done" , { item_id : item . id , output_index : outputIndex , arguments : item . arguments } , sequence ++ ) ;
459+ writeEvent ( sink , custom ? "response.custom_tool_call_input.done" : "response.function_call_arguments.done" , custom
460+ ? { item_id : item . id , output_index : outputIndex , input : item . input }
461+ : { item_id : item . id , output_index : outputIndex , arguments : item . arguments } , sequence ++ ) ;
416462 writeEvent ( sink , "response.output_item.done" , { output_index : outputIndex , item } , sequence ++ ) ;
417463 }
418464 }
0 commit comments