@@ -1013,6 +1013,35 @@ func handleBackgroundNonStream(ctx context.Context, store *ResponseStore, respon
10131013 Content : []schema.ORContentPart {makeOutputTextPartWithLogprobs (result , resultLogprobs )},
10141014 })
10151015 }
1016+ } else if ! shouldUseFn && cfg .FunctionsConfig .AutomaticToolParsingFallback && result != "" {
1017+ // Automatic tool parsing fallback: no tools in request but model emitted tool call markup
1018+ parsed := functions .ParseFunctionCall (result , cfg .FunctionsConfig )
1019+ if len (parsed ) > 0 {
1020+ stripped := functions .StripToolCallMarkup (result )
1021+ if stripped != "" {
1022+ allOutputItems = append (allOutputItems , schema.ORItemField {
1023+ Type : "message" , ID : fmt .Sprintf ("msg_%s" , uuid .New ().String ()),
1024+ Status : "completed" , Role : "assistant" ,
1025+ Content : []schema.ORContentPart {makeOutputTextPartWithLogprobs (stripped , resultLogprobs )},
1026+ })
1027+ }
1028+ for _ , fc := range parsed {
1029+ toolCallID := fc .ID
1030+ if toolCallID == "" {
1031+ toolCallID = fmt .Sprintf ("fc_%s" , uuid .New ().String ())
1032+ }
1033+ allOutputItems = append (allOutputItems , schema.ORItemField {
1034+ Type : "function_call" , ID : fmt .Sprintf ("fc_%s" , uuid .New ().String ()),
1035+ Status : "completed" , CallID : toolCallID , Name : fc .Name , Arguments : fc .Arguments ,
1036+ })
1037+ }
1038+ } else {
1039+ allOutputItems = append (allOutputItems , schema.ORItemField {
1040+ Type : "message" , ID : fmt .Sprintf ("msg_%s" , uuid .New ().String ()),
1041+ Status : "completed" , Role : "assistant" ,
1042+ Content : []schema.ORContentPart {makeOutputTextPartWithLogprobs (result , resultLogprobs )},
1043+ })
1044+ }
10161045 } else {
10171046 allOutputItems = append (allOutputItems , schema.ORItemField {
10181047 Type : "message" , ID : fmt .Sprintf ("msg_%s" , uuid .New ().String ()),
@@ -1539,6 +1568,43 @@ func handleOpenResponsesNonStream(c echo.Context, responseID string, createdAt i
15391568 Content : []schema.ORContentPart {makeOutputTextPartWithLogprobs (cleanedResult , resultLogprobs )},
15401569 })
15411570 }
1571+ } else if ! shouldUseFn && cfg .FunctionsConfig .AutomaticToolParsingFallback && cleanedResult != "" {
1572+ // Automatic tool parsing fallback: no tools in request but model emitted tool call markup
1573+ parsed := functions .ParseFunctionCall (cleanedResult , cfg .FunctionsConfig )
1574+ if len (parsed ) > 0 {
1575+ stripped := functions .StripToolCallMarkup (cleanedResult )
1576+ if stripped != "" {
1577+ outputItems = append (outputItems , schema.ORItemField {
1578+ Type : "message" ,
1579+ ID : fmt .Sprintf ("msg_%s" , uuid .New ().String ()),
1580+ Status : "completed" ,
1581+ Role : "assistant" ,
1582+ Content : []schema.ORContentPart {makeOutputTextPartWithLogprobs (stripped , resultLogprobs )},
1583+ })
1584+ }
1585+ for _ , fc := range parsed {
1586+ toolCallID := fc .ID
1587+ if toolCallID == "" {
1588+ toolCallID = fmt .Sprintf ("fc_%s" , uuid .New ().String ())
1589+ }
1590+ outputItems = append (outputItems , schema.ORItemField {
1591+ Type : "function_call" ,
1592+ ID : fmt .Sprintf ("fc_%s" , uuid .New ().String ()),
1593+ Status : "completed" ,
1594+ CallID : toolCallID ,
1595+ Name : fc .Name ,
1596+ Arguments : fc .Arguments ,
1597+ })
1598+ }
1599+ } else {
1600+ outputItems = append (outputItems , schema.ORItemField {
1601+ Type : "message" ,
1602+ ID : fmt .Sprintf ("msg_%s" , uuid .New ().String ()),
1603+ Status : "completed" ,
1604+ Role : "assistant" ,
1605+ Content : []schema.ORContentPart {makeOutputTextPartWithLogprobs (cleanedResult , resultLogprobs )},
1606+ })
1607+ }
15421608 } else {
15431609 // Simple text response (include logprobs if available)
15441610 messageItem := schema.ORItemField {
@@ -2514,6 +2580,15 @@ func handleOpenResponsesStream(c echo.Context, responseID string, createdAt int6
25142580
25152581 result = finalCleanedResult
25162582
2583+ // Automatic tool parsing fallback for streaming: parse tool calls from accumulated text
2584+ var streamFallbackToolCalls []functions.FuncCallResults
2585+ if cfg .FunctionsConfig .AutomaticToolParsingFallback && result != "" {
2586+ streamFallbackToolCalls = functions .ParseFunctionCall (result , cfg .FunctionsConfig )
2587+ if len (streamFallbackToolCalls ) > 0 {
2588+ result = functions .StripToolCallMarkup (result )
2589+ }
2590+ }
2591+
25172592 // Convert logprobs for streaming events
25182593 mcpStreamLogprobs := convertLogprobsForStreaming (noToolLogprobs )
25192594
@@ -2552,10 +2627,42 @@ func handleOpenResponsesStream(c echo.Context, responseID string, createdAt int6
25522627 })
25532628 sequenceNumber ++
25542629
2630+ // Emit function_call items from automatic tool parsing fallback
2631+ for _ , fc := range streamFallbackToolCalls {
2632+ toolCallID := fc .ID
2633+ if toolCallID == "" {
2634+ toolCallID = fmt .Sprintf ("fc_%s" , uuid .New ().String ())
2635+ }
2636+ outputIndex ++
2637+ functionCallItem := & schema.ORItemField {
2638+ Type : "function_call" ,
2639+ ID : toolCallID ,
2640+ Status : "completed" ,
2641+ CallID : toolCallID ,
2642+ Name : fc .Name ,
2643+ Arguments : fc .Arguments ,
2644+ }
2645+ sendSSEEvent (c , & schema.ORStreamEvent {
2646+ Type : "response.output_item.added" ,
2647+ SequenceNumber : sequenceNumber ,
2648+ OutputIndex : & outputIndex ,
2649+ Item : functionCallItem ,
2650+ })
2651+ sequenceNumber ++
2652+ sendSSEEvent (c , & schema.ORStreamEvent {
2653+ Type : "response.output_item.done" ,
2654+ SequenceNumber : sequenceNumber ,
2655+ OutputIndex : & outputIndex ,
2656+ Item : functionCallItem ,
2657+ })
2658+ sequenceNumber ++
2659+ collectedOutputItems = append (collectedOutputItems , * functionCallItem )
2660+ }
2661+
25552662 // Emit response.completed
25562663 now := time .Now ().Unix ()
25572664
2558- // Collect final output items (reasoning first, then message )
2665+ // Collect final output items (reasoning first, then messages, then tool calls )
25592666 var finalOutputItems []schema.ORItemField
25602667 // Add reasoning item if it exists
25612668 if currentReasoningID != "" && finalReasoning != "" {
@@ -2577,6 +2684,12 @@ func handleOpenResponsesStream(c echo.Context, responseID string, createdAt int6
25772684 } else {
25782685 finalOutputItems = append (finalOutputItems , * messageItem )
25792686 }
2687+ // Add function_call items from fallback
2688+ for _ , item := range collectedOutputItems {
2689+ if item .Type == "function_call" {
2690+ finalOutputItems = append (finalOutputItems , item )
2691+ }
2692+ }
25802693 responseCompleted := buildORResponse (responseID , createdAt , & now , "completed" , input , finalOutputItems , & schema.ORUsage {
25812694 InputTokens : noToolTokenUsage .Prompt ,
25822695 OutputTokens : noToolTokenUsage .Completion ,
0 commit comments