fix: tool calls break the converation if interrupted.

Manolo Santos created

Change summary

internal/llm/provider/anthropic.go |  3 +++
internal/llm/provider/gemini.go    |  3 +++
internal/llm/provider/openai.go    | 32 +++++++++++++++++++++++---------
3 files changed, 29 insertions(+), 9 deletions(-)

Detailed changes

internal/llm/provider/anthropic.go 🔗

@@ -151,6 +151,9 @@ func (a *anthropicClient) convertMessages(messages []message.Message) (anthropic
 			}
 
 			for _, toolCall := range msg.ToolCalls() {
+				if !toolCall.Finished {
+					continue
+				}
 				var inputMap map[string]any
 				err := json.Unmarshal([]byte(toolCall.Input), &inputMap)
 				if err != nil {

internal/llm/provider/gemini.go 🔗

@@ -81,6 +81,9 @@ func (g *geminiClient) convertMessages(messages []message.Message) []*genai.Cont
 
 			if len(msg.ToolCalls()) > 0 {
 				for _, call := range msg.ToolCalls() {
+					if !call.Finished {
+						continue
+					}
 					args, _ := parseJSONToMap(call.Input)
 					assistantParts = append(assistantParts, &genai.Part{
 						FunctionCall: &genai.FunctionCall{

internal/llm/provider/openai.go 🔗

@@ -125,16 +125,25 @@ func (o *openaiClient) convertMessages(messages []message.Message) (openaiMessag
 				Role: "assistant",
 			}
 
+			// Only include finished tool calls; interrupted tool calls must not be resent.
 			if len(msg.ToolCalls()) > 0 {
-				assistantMsg.ToolCalls = make([]openai.ChatCompletionMessageToolCallParam, len(msg.ToolCalls()))
-				for i, call := range msg.ToolCalls() {
-					assistantMsg.ToolCalls[i] = openai.ChatCompletionMessageToolCallParam{
-						ID:   call.ID,
-						Type: "function",
-						Function: openai.ChatCompletionMessageToolCallFunctionParam{
-							Name:      call.Name,
-							Arguments: call.Input,
-						},
+				finished := make([]message.ToolCall, 0, len(msg.ToolCalls()))
+				for _, call := range msg.ToolCalls() {
+					if call.Finished {
+						finished = append(finished, call)
+					}
+				}
+				if len(finished) > 0 {
+					assistantMsg.ToolCalls = make([]openai.ChatCompletionMessageToolCallParam, len(finished))
+					for i, call := range finished {
+						assistantMsg.ToolCalls[i] = openai.ChatCompletionMessageToolCallParam{
+							ID:   call.ID,
+							Type: "function",
+							Function: openai.ChatCompletionMessageToolCallFunctionParam{
+								Name:      call.Name,
+								Arguments: call.Input,
+							},
+						}
 					}
 				}
 			}
@@ -151,6 +160,11 @@ func (o *openaiClient) convertMessages(messages []message.Message) (openaiMessag
 					},
 				})
 			}
+			// Skip empty assistant messages (no content and no finished tool calls)
+			if msg.Content().String() == "" && len(assistantMsg.ToolCalls) == 0 {
+				continue
+			}
+
 			openaiMessages = append(openaiMessages, openai.ChatCompletionMessageParamUnion{
 				OfAssistant: &assistantMsg,
 			})