on main help sign in
chore: fix openai provider for multi tool call
71b7127Kujtim Hoxha
internal/llm/provider/openai.go+44−1
@@ -272,10 +272,13 @@ func (o *openaiClient) stream(ctx context.Context, messages []message.Message, t
272 currentContent := ""
272 currentContent := ""
273 toolCalls := make([]message.ToolCall, 0)
273 toolCalls := make([]message.ToolCall, 0)
274
274
275+ var currentToolCallID string
276+ var currentToolCall openai.ChatCompletionMessageToolCall
277+ var msgToolCalls []openai.ChatCompletionMessageToolCall
275 for openaiStream.Next() {
278 for openaiStream.Next() {
276 chunk := openaiStream.Current()
279 chunk := openaiStream.Current()
277 acc.AddChunk(chunk)
280 acc.AddChunk(chunk)
278−
281+ // This fixes multiple tool calls for some providers
279 for _, choice := range chunk.Choices {
282 for _, choice := range chunk.Choices {
280 if choice.Delta.Content != "" {
283 if choice.Delta.Content != "" {
281 eventChan <- ProviderEvent{
284 eventChan <- ProviderEvent{
@@ -283,6 +286,45 @@ func (o *openaiClient) stream(ctx context.Context, messages []message.Message, t
283 Content: choice.Delta.Content,
286 Content: choice.Delta.Content,
284 }
287 }
285 currentContent += choice.Delta.Content
288 currentContent += choice.Delta.Content
289+ } else if len(choice.Delta.ToolCalls) > 0 {
290+ toolCall := choice.Delta.ToolCalls[0]
291+ // Detect tool use start
292+ if currentToolCallID == "" {
293+ if toolCall.ID != "" {
294+ currentToolCallID = toolCall.ID
295+ currentToolCall = openai.ChatCompletionMessageToolCall{
296+ ID: toolCall.ID,
297+ Type: "function",
298+ Function: openai.ChatCompletionMessageToolCallFunction{
299+ Name: toolCall.Function.Name,
300+ Arguments: toolCall.Function.Arguments,
301+ },
302+ }
303+ }
304+ } else {
305+ // Delta tool use
306+ if toolCall.ID == "" {
307+ currentToolCall.Function.Arguments += toolCall.Function.Arguments
308+ } else {
309+ // Detect new tool use
310+ if toolCall.ID != currentToolCallID {
311+ msgToolCalls = append(msgToolCalls, currentToolCall)
312+ currentToolCallID = toolCall.ID
313+ currentToolCall = openai.ChatCompletionMessageToolCall{
314+ ID: toolCall.ID,
315+ Type: "function",
316+ Function: openai.ChatCompletionMessageToolCallFunction{
317+ Name: toolCall.Function.Name,
318+ Arguments: toolCall.Function.Arguments,
319+ },
320+ }
321+ }
322+ }
323+ }
324+ }
325+ if choice.FinishReason == "tool_calls" {
326+ msgToolCalls = append(msgToolCalls, currentToolCall)
327+ acc.Choices[0].Message.ToolCalls = msgToolCalls
286 }
328 }
287 }
329 }
288 }
330 }
@@ -293,6 +335,7 @@ func (o *openaiClient) stream(ctx context.Context, messages []message.Message, t
293 jsonData, _ := json.Marshal(acc.ChatCompletion)
335 jsonData, _ := json.Marshal(acc.ChatCompletion)
294 slog.Debug("Response", "messages", string(jsonData))
336 slog.Debug("Response", "messages", string(jsonData))
295 }
337 }
338+
296 resultFinishReason := acc.ChatCompletion.Choices[0].FinishReason
339 resultFinishReason := acc.ChatCompletion.Choices[0].FinishReason
297 if resultFinishReason == "" {
340 if resultFinishReason == "" {
298 // If the finish reason is empty, we assume it was a successful completion
341 // If the finish reason is empty, we assume it was a successful completion