From 0041f389207c3a26b0d72e68f94c5afd3ea0d4fc Mon Sep 17 00:00:00 2001 From: "Mikhail Andreev (adw0rd)" Date: Fri, 27 Mar 2026 23:41:08 +0300 Subject: [PATCH] fix: use prior assistant text when LLM returns empty after tool execution When the LLM returns both text content and tool calls in the same response, the follow-up LLM call after tool execution sometimes returns an empty response (the model considers the turn complete since it already provided its answer). Track the last non-empty assistant text content and use it as a fallback instead of the generic "model returned an empty response" error message. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- pkg/agent/loop.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index c9efb318b..91b5dc404 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -1689,6 +1689,7 @@ func (al *AgentLoop) runTurn(ctx context.Context, ts *turnState) (turnResult, er activeCandidates, activeModel := al.selectCandidates(ts.agent, ts.userMessage, messages) pendingMessages := append([]providers.Message(nil), ts.opts.InitialSteeringMessages...) var finalContent string + var lastAssistantTextContent string // text from last response that also had tool calls turnLoop: for ts.currentIteration() < ts.agent.MaxIterations || len(pendingMessages) > 0 || func() bool { @@ -2169,6 +2170,9 @@ turnLoop: }) allResponsesHandled := len(normalizedToolCalls) > 0 + if response.Content != "" { + lastAssistantTextContent = response.Content + } assistantMsg := providers.Message{ Role: "assistant", Content: response.Content, @@ -2668,6 +2672,9 @@ turnLoop: if finalContent == "" { if ts.currentIteration() >= ts.agent.MaxIterations && ts.agent.MaxIterations > 0 { finalContent = toolLimitResponse + } else if lastAssistantTextContent != "" { + // LLM returned empty after tool execution but already provided text in the tool-call response + finalContent = lastAssistantTextContent } else { finalContent = ts.opts.DefaultResponse }