fix(tools): preserve context memory and thought signatures in toolloop
- Retain and during multi-turn tool calls to resolve API 400 errors (missing a thought_signature). - Append the final assistant message to the session history before exiting the loop, fixing the state memory loss issue in the team strategy. - Add fallback to when standard content is empty (e.g., for Gemini 2.0 Pro Thinking). - Preserve during token budget exhaustion and truncation recovery to prevent broken chain-of-thought.
This commit is contained in:
parent
bac9a1c84b
commit
d22ca85481
1 changed files with 36 additions and 8 deletions
|
|
@ -92,8 +92,9 @@ func RunToolLoop(
|
||||||
})
|
})
|
||||||
finalContent = response.Content
|
finalContent = response.Content
|
||||||
messages = append(messages, providers.Message{
|
messages = append(messages, providers.Message{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
Content: response.Content,
|
Content: response.Content,
|
||||||
|
ReasoningContent: response.ReasoningContent, // [Fix] Preserve reasoning content to maintain context
|
||||||
})
|
})
|
||||||
messages = append(messages, providers.Message{
|
messages = append(messages, providers.Message{
|
||||||
Role: "user",
|
Role: "user",
|
||||||
|
|
@ -121,8 +122,9 @@ func RunToolLoop(
|
||||||
logger.WarnCF("toolloop", "LLM response was truncated (max_tokens hit), injecting recovery message",
|
logger.WarnCF("toolloop", "LLM response was truncated (max_tokens hit), injecting recovery message",
|
||||||
map[string]any{"iteration": iteration})
|
map[string]any{"iteration": iteration})
|
||||||
messages = append(messages, providers.Message{
|
messages = append(messages, providers.Message{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
Content: response.Content,
|
Content: response.Content,
|
||||||
|
ReasoningContent: response.ReasoningContent, // [Fix] Preserve reasoning content to prevent broken chain of thought
|
||||||
})
|
})
|
||||||
messages = append(messages, providers.Message{
|
messages = append(messages, providers.Message{
|
||||||
Role: "user",
|
Role: "user",
|
||||||
|
|
@ -134,11 +136,25 @@ func RunToolLoop(
|
||||||
// 4. If no tool calls, we're done
|
// 4. If no tool calls, we're done
|
||||||
if len(response.ToolCalls) == 0 {
|
if len(response.ToolCalls) == 0 {
|
||||||
finalContent = response.Content
|
finalContent = response.Content
|
||||||
|
// [Fix] Fallback for models (like Gemini 2.0 Pro Thinking) that put output in reasoning block
|
||||||
|
if finalContent == "" && response.ReasoningContent != "" {
|
||||||
|
finalContent = response.ReasoningContent
|
||||||
|
}
|
||||||
|
|
||||||
logger.InfoCF("toolloop", "LLM response without tool calls (direct answer)",
|
logger.InfoCF("toolloop", "LLM response without tool calls (direct answer)",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"iteration": iteration,
|
"iteration": iteration,
|
||||||
"content_chars": len(finalContent),
|
"content_chars": len(finalContent),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// [Fix] Append the final answer to the messages array!
|
||||||
|
// Essential for Team's evaluator_optimizer strategy to retain state in the next loop.
|
||||||
|
messages = append(messages, providers.Message{
|
||||||
|
Role: "assistant",
|
||||||
|
Content: finalContent,
|
||||||
|
ReasoningContent: response.ReasoningContent,
|
||||||
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,20 +177,32 @@ func RunToolLoop(
|
||||||
|
|
||||||
// 6. Build assistant message with tool calls
|
// 6. Build assistant message with tool calls
|
||||||
assistantMsg := providers.Message{
|
assistantMsg := providers.Message{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
Content: response.Content,
|
Content: response.Content,
|
||||||
|
ReasoningContent: response.ReasoningContent, // [Fix] Include ReasoningContent
|
||||||
}
|
}
|
||||||
for _, tc := range normalizedToolCalls {
|
for _, tc := range normalizedToolCalls {
|
||||||
argumentsJSON, _ := json.Marshal(tc.Arguments)
|
argumentsJSON, _ := json.Marshal(tc.Arguments)
|
||||||
|
|
||||||
|
// [Fix] Preserve ThoughtSignature and ExtraContent for compatibility with models like Gemini 2.0/3.0
|
||||||
|
extraContent := tc.ExtraContent
|
||||||
|
thoughtSignature := ""
|
||||||
|
if tc.Function != nil {
|
||||||
|
thoughtSignature = tc.Function.ThoughtSignature
|
||||||
|
}
|
||||||
|
|
||||||
assistantMsg.ToolCalls = append(assistantMsg.ToolCalls, providers.ToolCall{
|
assistantMsg.ToolCalls = append(assistantMsg.ToolCalls, providers.ToolCall{
|
||||||
ID: tc.ID,
|
ID: tc.ID,
|
||||||
Type: "function",
|
Type: "function",
|
||||||
Name: tc.Name,
|
Name: tc.Name,
|
||||||
Arguments: tc.Arguments,
|
Arguments: tc.Arguments,
|
||||||
Function: &providers.FunctionCall{
|
Function: &providers.FunctionCall{
|
||||||
Name: tc.Name,
|
Name: tc.Name,
|
||||||
Arguments: string(argumentsJSON),
|
Arguments: string(argumentsJSON),
|
||||||
|
ThoughtSignature: thoughtSignature, // [Fix] Preserve thought signature
|
||||||
},
|
},
|
||||||
|
ExtraContent: extraContent, // [Fix] Preserve extra content
|
||||||
|
ThoughtSignature: thoughtSignature, // [Fix] Preserve thought signature
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
messages = append(messages, assistantMsg)
|
messages = append(messages, assistantMsg)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue