mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-05-07 04:35:46 +08:00
fix(cursor): preserve tool call context in multi-turn conversations
When an assistant message appears after tool results without a pending user message, append it to the last turn's assistant text instead of dropping it. Also add bakeToolResultsIntoTurns() to merge tool results into turn context when no active H2 session exists for resume, ensuring the model sees the full tool interaction history in follow-up requests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -321,6 +321,13 @@ func (e *CursorExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
|
||||
}
|
||||
e.mu.Unlock()
|
||||
|
||||
// If tool results exist but no session to resume, bake them into turns
|
||||
// so the model sees tool interaction context in the new conversation.
|
||||
if len(parsed.ToolResults) > 0 {
|
||||
log.Debugf("cursor: no session to resume, baking %d tool results into turns", len(parsed.ToolResults))
|
||||
bakeToolResultsIntoTurns(parsed)
|
||||
}
|
||||
|
||||
params := buildRunRequestParams(parsed)
|
||||
requestBytes := cursorproto.EncodeRunRequest(params)
|
||||
framedRequest := cursorproto.FrameConnectMessage(requestBytes, 0)
|
||||
@@ -898,12 +905,22 @@ func parseOpenAIRequest(payload []byte) *parsedOpenAIRequest {
|
||||
pendingUser = extractTextContent(msg.Get("content"))
|
||||
p.Images = extractImages(msg.Get("content"))
|
||||
case "assistant":
|
||||
assistantText := extractTextContent(msg.Get("content"))
|
||||
if pendingUser != "" {
|
||||
p.Turns = append(p.Turns, cursorproto.TurnData{
|
||||
UserText: pendingUser,
|
||||
AssistantText: extractTextContent(msg.Get("content")),
|
||||
AssistantText: assistantText,
|
||||
})
|
||||
pendingUser = ""
|
||||
} else if len(p.Turns) > 0 && assistantText != "" {
|
||||
// Assistant message after tool results (no pending user) —
|
||||
// append to the last turn's assistant text to preserve context.
|
||||
last := &p.Turns[len(p.Turns)-1]
|
||||
if last.AssistantText != "" {
|
||||
last.AssistantText += "\n" + assistantText
|
||||
} else {
|
||||
last.AssistantText = assistantText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -922,6 +939,27 @@ func parseOpenAIRequest(payload []byte) *parsedOpenAIRequest {
|
||||
return p
|
||||
}
|
||||
|
||||
// bakeToolResultsIntoTurns merges tool results into the last turn's assistant text
|
||||
// when there's no active H2 session to resume. This ensures the model sees the
|
||||
// full tool interaction context in a new conversation.
|
||||
func bakeToolResultsIntoTurns(parsed *parsedOpenAIRequest) {
|
||||
if len(parsed.ToolResults) == 0 || len(parsed.Turns) == 0 {
|
||||
return
|
||||
}
|
||||
last := &parsed.Turns[len(parsed.Turns)-1]
|
||||
var toolContext strings.Builder
|
||||
for _, tr := range parsed.ToolResults {
|
||||
toolContext.WriteString("\n\n[Tool Result]\n")
|
||||
toolContext.WriteString(tr.Content)
|
||||
}
|
||||
if last.AssistantText != "" {
|
||||
last.AssistantText += toolContext.String()
|
||||
} else {
|
||||
last.AssistantText = toolContext.String()
|
||||
}
|
||||
parsed.ToolResults = nil // consumed
|
||||
}
|
||||
|
||||
func extractTextContent(content gjson.Result) string {
|
||||
if content.Type == gjson.String {
|
||||
return content.String()
|
||||
|
||||
Reference in New Issue
Block a user