mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-03-20 07:51:52 +00:00
fix: filter out orphaned tool results from history and current context
This commit is contained in:
@@ -578,6 +578,7 @@ func processOpenAIMessages(messages gjson.Result, modelID, origin string) ([]Kir
|
||||
|
||||
// Truncate history if too long to prevent Kiro API errors
|
||||
history = truncateHistoryIfNeeded(history)
|
||||
history, currentToolResults = filterOrphanedToolResults(history, currentToolResults)
|
||||
|
||||
return history, currentUserMsg, currentToolResults
|
||||
}
|
||||
@@ -593,6 +594,61 @@ func truncateHistoryIfNeeded(history []KiroHistoryMessage) []KiroHistoryMessage
|
||||
return history[len(history)-kiroMaxHistoryMessages:]
|
||||
}
|
||||
|
||||
func filterOrphanedToolResults(history []KiroHistoryMessage, currentToolResults []KiroToolResult) ([]KiroHistoryMessage, []KiroToolResult) {
|
||||
// Remove tool results with no matching tool_use in retained history.
|
||||
// This happens after truncation when the assistant turn that produced tool_use
|
||||
// is dropped but a later user/tool_result survives.
|
||||
validToolUseIDs := make(map[string]bool)
|
||||
for _, h := range history {
|
||||
if h.AssistantResponseMessage == nil {
|
||||
continue
|
||||
}
|
||||
for _, tu := range h.AssistantResponseMessage.ToolUses {
|
||||
validToolUseIDs[tu.ToolUseID] = true
|
||||
}
|
||||
}
|
||||
|
||||
for i, h := range history {
|
||||
if h.UserInputMessage == nil || h.UserInputMessage.UserInputMessageContext == nil {
|
||||
continue
|
||||
}
|
||||
ctx := h.UserInputMessage.UserInputMessageContext
|
||||
if len(ctx.ToolResults) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
filtered := make([]KiroToolResult, 0, len(ctx.ToolResults))
|
||||
for _, tr := range ctx.ToolResults {
|
||||
if validToolUseIDs[tr.ToolUseID] {
|
||||
filtered = append(filtered, tr)
|
||||
continue
|
||||
}
|
||||
log.Debugf("kiro-openai: dropping orphaned tool_result in history[%d]: toolUseId=%s (no matching tool_use)", i, tr.ToolUseID)
|
||||
}
|
||||
ctx.ToolResults = filtered
|
||||
if len(ctx.ToolResults) == 0 && len(ctx.Tools) == 0 {
|
||||
h.UserInputMessage.UserInputMessageContext = nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(currentToolResults) > 0 {
|
||||
filtered := make([]KiroToolResult, 0, len(currentToolResults))
|
||||
for _, tr := range currentToolResults {
|
||||
if validToolUseIDs[tr.ToolUseID] {
|
||||
filtered = append(filtered, tr)
|
||||
continue
|
||||
}
|
||||
log.Debugf("kiro-openai: dropping orphaned tool_result in currentMessage: toolUseId=%s (no matching tool_use)", tr.ToolUseID)
|
||||
}
|
||||
if len(filtered) != len(currentToolResults) {
|
||||
log.Infof("kiro-openai: dropped %d orphaned tool_result(s) from currentMessage", len(currentToolResults)-len(filtered))
|
||||
}
|
||||
currentToolResults = filtered
|
||||
}
|
||||
|
||||
return history, currentToolResults
|
||||
}
|
||||
|
||||
// buildUserMessageFromOpenAI builds a user message from OpenAI format and extracts tool results
|
||||
func buildUserMessageFromOpenAI(msg gjson.Result, modelID, origin string) (KiroUserInputMessage, []KiroToolResult) {
|
||||
content := msg.Get("content")
|
||||
|
||||
@@ -384,3 +384,57 @@ func TestAssistantEndsConversation(t *testing.T) {
|
||||
t.Error("Expected a 'Continue' message to be created when assistant is last")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOrphanedToolResults_RemovesHistoryAndCurrentOrphans(t *testing.T) {
|
||||
history := []KiroHistoryMessage{
|
||||
{
|
||||
AssistantResponseMessage: &KiroAssistantResponseMessage{
|
||||
Content: "assistant",
|
||||
ToolUses: []KiroToolUse{
|
||||
{ToolUseID: "keep-1", Name: "Read", Input: map[string]interface{}{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
UserInputMessage: &KiroUserInputMessage{
|
||||
Content: "user-with-mixed-results",
|
||||
UserInputMessageContext: &KiroUserInputMessageContext{
|
||||
ToolResults: []KiroToolResult{
|
||||
{ToolUseID: "keep-1", Status: "success", Content: []KiroTextContent{{Text: "ok"}}},
|
||||
{ToolUseID: "orphan-1", Status: "success", Content: []KiroTextContent{{Text: "bad"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
UserInputMessage: &KiroUserInputMessage{
|
||||
Content: "user-only-orphans",
|
||||
UserInputMessageContext: &KiroUserInputMessageContext{
|
||||
ToolResults: []KiroToolResult{
|
||||
{ToolUseID: "orphan-2", Status: "success", Content: []KiroTextContent{{Text: "bad"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
currentToolResults := []KiroToolResult{
|
||||
{ToolUseID: "keep-1", Status: "success", Content: []KiroTextContent{{Text: "ok"}}},
|
||||
{ToolUseID: "orphan-3", Status: "success", Content: []KiroTextContent{{Text: "bad"}}},
|
||||
}
|
||||
|
||||
filteredHistory, filteredCurrent := filterOrphanedToolResults(history, currentToolResults)
|
||||
|
||||
ctx1 := filteredHistory[1].UserInputMessage.UserInputMessageContext
|
||||
if ctx1 == nil || len(ctx1.ToolResults) != 1 || ctx1.ToolResults[0].ToolUseID != "keep-1" {
|
||||
t.Fatalf("expected mixed history message to keep only keep-1, got: %+v", ctx1)
|
||||
}
|
||||
|
||||
if filteredHistory[2].UserInputMessage.UserInputMessageContext != nil {
|
||||
t.Fatalf("expected orphan-only history context to be removed")
|
||||
}
|
||||
|
||||
if len(filteredCurrent) != 1 || filteredCurrent[0].ToolUseID != "keep-1" {
|
||||
t.Fatalf("expected current tool results to keep only keep-1, got: %+v", filteredCurrent)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user