on main help sign in
fix: some openai providers

Co-authored-by: Peter Steinberger <steipete@gmail.com>

commit0de5b24655432cfaf8268e1387251e902940dfef authorKujtim Hoxha <kujtimii.h@gmail.com> dateMon 4 Aug 2025, 14:14 (1 year ago) parent5177982
1 file changed+41−57
internal/llm/provider/openai.go+41−57
internal/llm/provider/openai.go+41−57
@@ -6,6 +6,7 @@ import (
66 "fmt"
77 "io"
88 "log/slog"
9+ "slices"
910 "strings"
1011 "time"
1112
@@ -14,6 +15,7 @@ import (
1415 "github.com/charmbracelet/crush/internal/llm/tools"
1516 "github.com/charmbracelet/crush/internal/log"
1617 "github.com/charmbracelet/crush/internal/message"
18+ "github.com/google/uuid"
1719 "github.com/openai/openai-go"
1820 "github.com/openai/openai-go/option"
1921 "github.com/openai/openai-go/packages/param"
@@ -338,21 +340,16 @@ func (o *openaiClient) stream(ctx context.Context, messages []message.Message, t
338340 acc := openai.ChatCompletionAccumulator{}
339341 currentContent := ""
340342 toolCalls := make([]message.ToolCall, 0)
341−
342− var currentToolCallID string
343− var currentToolCall openai.ChatCompletionMessageToolCall
344343 var msgToolCalls []openai.ChatCompletionMessageToolCall
345− currentToolIndex := 0
346344 for openaiStream.Next() {
347345 chunk := openaiStream.Current()
348346 // Kujtim: this is an issue with openrouter qwen, its sending -1 for the tool index
349347 if len(chunk.Choices) > 0 && len(chunk.Choices[0].Delta.ToolCalls) > 0 && chunk.Choices[0].Delta.ToolCalls[0].Index == -1 {
350− chunk.Choices[0].Delta.ToolCalls[0].Index = int64(currentToolIndex)
351− currentToolIndex++
348+ chunk.Choices[0].Delta.ToolCalls[0].Index = 0
352349 }
353350 acc.AddChunk(chunk)
354351 // This fixes multiple tool calls for some providers
355− for _, choice := range chunk.Choices {
352+ for i, choice := range chunk.Choices {
356353 if choice.Delta.Content != "" {
357354 eventChan <- ProviderEvent{
358355 Type: EventContentDelta,
@@ -361,63 +358,50 @@ func (o *openaiClient) stream(ctx context.Context, messages []message.Message, t
361358 currentContent += choice.Delta.Content
362359 } else if len(choice.Delta.ToolCalls) > 0 {
363360 toolCall := choice.Delta.ToolCalls[0]
364− // Detect tool use start
365− if currentToolCallID == "" {
366− if toolCall.ID != "" {
367− currentToolCallID = toolCall.ID
368− eventChan <- ProviderEvent{
369− Type: EventToolUseStart,
370− ToolCall: &message.ToolCall{
371− ID: toolCall.ID,
372− Name: toolCall.Function.Name,
373− Finished: false,
374− },
361+ newToolCall := false
362+ if len(msgToolCalls)-1 >= int(toolCall.Index) { // tool call exists
363+ existingToolCall := msgToolCalls[toolCall.Index]
364+ if toolCall.ID != "" && toolCall.ID != existingToolCall.ID {
365+ found := false
366+ // try to find the tool based on the ID
367+ for i, tool := range msgToolCalls {
368+ if tool.ID == toolCall.ID {
369+ msgToolCalls[i].Function.Arguments += toolCall.Function.Arguments
370+ found = true
371+ }
375372 }
376− currentToolCall = openai.ChatCompletionMessageToolCall{
377− ID: toolCall.ID,
378− Type: "function",
379− Function: openai.ChatCompletionMessageToolCallFunction{
380− Name: toolCall.Function.Name,
381− Arguments: toolCall.Function.Arguments,
382− },
373+ if !found {
374+ newToolCall = true
383375 }
384− }
385− } else {
386− // Delta tool use
387− if toolCall.ID == "" || toolCall.ID == currentToolCallID {
388− currentToolCall.Function.Arguments += toolCall.Function.Arguments
389376 } else {
390− // Detect new tool use
391− if toolCall.ID != currentToolCallID {
392− msgToolCalls = append(msgToolCalls, currentToolCall)
393− currentToolCallID = toolCall.ID
394− eventChan <- ProviderEvent{
395− Type: EventToolUseStart,
396− ToolCall: &message.ToolCall{
397− ID: toolCall.ID,
398− Name: toolCall.Function.Name,
399− Finished: false,
400− },
401− }
402− currentToolCall = openai.ChatCompletionMessageToolCall{
403− ID: toolCall.ID,
404− Type: "function",
405− Function: openai.ChatCompletionMessageToolCallFunction{
406− Name: toolCall.Function.Name,
407− Arguments: toolCall.Function.Arguments,
408− },
409− }
410− }
377+ msgToolCalls[toolCall.Index].Function.Arguments += toolCall.Function.Arguments
411378 }
379+ } else {
380+ newToolCall = true
412381 }
413− }
414− // Kujtim: some models send finish stop even for tool calls
415− if choice.FinishReason == "tool_calls" || (choice.FinishReason == "stop" && currentToolCallID != "") {
416− msgToolCalls = append(msgToolCalls, currentToolCall)
417− if len(acc.Choices) > 0 {
418− acc.Choices[0].Message.ToolCalls = msgToolCalls
382+ if newToolCall { // new tool call
383+ if toolCall.ID == "" {
384+ toolCall.ID = uuid.NewString()
385+ }
386+ eventChan <- ProviderEvent{
387+ Type: EventToolUseStart,
388+ ToolCall: &message.ToolCall{
389+ ID: toolCall.ID,
390+ Name: toolCall.Function.Name,
391+ Finished: false,
392+ },
393+ }
394+ msgToolCalls = append(msgToolCalls, openai.ChatCompletionMessageToolCall{
395+ ID: toolCall.ID,
396+ Type: "function",
397+ Function: openai.ChatCompletionMessageToolCallFunction{
398+ Name: toolCall.Function.Name,
399+ Arguments: toolCall.Function.Arguments,
400+ },
401+ })
419402 }
420403 }
404+ acc.Choices[i].Message.ToolCalls = slices.Clone(msgToolCalls)
421405 }
422406 }
423407