mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-04-26 20:55:47 +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
|
// Truncate history if too long to prevent Kiro API errors
|
||||||
history = truncateHistoryIfNeeded(history)
|
history = truncateHistoryIfNeeded(history)
|
||||||
|
history, currentToolResults = filterOrphanedToolResults(history, currentToolResults)
|
||||||
|
|
||||||
return history, currentUserMsg, currentToolResults
|
return history, currentUserMsg, currentToolResults
|
||||||
}
|
}
|
||||||
@@ -593,6 +594,61 @@ func truncateHistoryIfNeeded(history []KiroHistoryMessage) []KiroHistoryMessage
|
|||||||
return history[len(history)-kiroMaxHistoryMessages:]
|
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
|
// buildUserMessageFromOpenAI builds a user message from OpenAI format and extracts tool results
|
||||||
func buildUserMessageFromOpenAI(msg gjson.Result, modelID, origin string) (KiroUserInputMessage, []KiroToolResult) {
|
func buildUserMessageFromOpenAI(msg gjson.Result, modelID, origin string) (KiroUserInputMessage, []KiroToolResult) {
|
||||||
content := msg.Get("content")
|
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")
|
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