mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-18 03:57:06 +08:00
release: 0.0.41
- 修复内存泄漏问题,该可能导致内存异常占用 - 支持俄语增加翻译范围 - 修复qwen-3.8-max中断问题(mimo也应该属于同一类问题) - 修复claude模型可能无法识别图片问题 @GGHansome - 支持自定义Openai端点 @Sxuan-Coder - WebSearch 接入百度搜索,DuckDuckGo 作为兜底 @杨超 - 修复一些兼容性问题 @kael-odin - 修复window上一些表现问题 @philau2512 🔔 如何让AI自动拉模型配置? (以下为提示词,把地址和密钥换为你的) 我的模型配置在 ~/.cursor-local-assistant-v2/config.yaml,我的API地址是:https://xxx 密钥是xxx,帮我拉所有模型配置进去,不要影响已有模型,根据models标准接口拉取。
This commit is contained in:
@@ -249,7 +249,7 @@ func canMergeProviderAssistantToolCalls(last Message, current Message) bool {
|
||||
if strings.TrimSpace(last.Role) != "assistant" || strings.TrimSpace(current.Role) != "assistant" {
|
||||
return false
|
||||
}
|
||||
if len(last.ToolCalls) == 0 || len(current.ToolCalls) == 0 {
|
||||
if len(current.ToolCalls) == 0 {
|
||||
return false
|
||||
}
|
||||
if strings.TrimSpace(last.ToolCallID) != "" || strings.TrimSpace(last.Name) != "" {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package modeladapter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSanitizeProviderMessagesMergesLegacyAssistantTextAndToolCallTurnsIdempotently(t *testing.T) {
|
||||
input := []Message{
|
||||
{
|
||||
Role: "assistant",
|
||||
Content: "Now let me pass stream.Mode in service.go.",
|
||||
ReasoningContent: "I need to update service.go.",
|
||||
},
|
||||
{
|
||||
Role: "assistant",
|
||||
Content: "",
|
||||
ReasoningContent: "I need to update service.go.",
|
||||
ToolCalls: []ToolCallDescriptor{
|
||||
{
|
||||
ID: "call_1",
|
||||
Type: "function",
|
||||
Function: ToolCallFunctionShape{
|
||||
Name: "PatchEdit",
|
||||
Arguments: `{"path":"/workspace/service.go"}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Role: "tool",
|
||||
Content: `{"success":{"path":"/workspace/service.go"}}`,
|
||||
ToolCallID: "call_1",
|
||||
Name: "PatchEdit",
|
||||
},
|
||||
}
|
||||
|
||||
first := sanitizeProviderMessages(input)
|
||||
if len(first) != 2 {
|
||||
t.Fatalf("message count = %d, want 2: %#v", len(first), first)
|
||||
}
|
||||
|
||||
assistant := first[0]
|
||||
if assistant.Content != input[0].Content {
|
||||
t.Fatalf("assistant content = %q", assistant.Content)
|
||||
}
|
||||
if assistant.ReasoningContent != input[0].ReasoningContent {
|
||||
t.Fatalf("assistant reasoning = %q, want one copy of %q", assistant.ReasoningContent, input[0].ReasoningContent)
|
||||
}
|
||||
if len(assistant.ToolCalls) != 1 || assistant.ToolCalls[0].ID != "call_1" {
|
||||
t.Fatalf("assistant tool calls = %#v", assistant.ToolCalls)
|
||||
}
|
||||
|
||||
second := sanitizeProviderMessages(first)
|
||||
if !reflect.DeepEqual(second, first) {
|
||||
t.Fatalf("sanitizing normalized messages changed them:\nfirst: %#v\nsecond: %#v", first, second)
|
||||
}
|
||||
}
|
||||
@@ -897,8 +897,8 @@ func buildMessagesFromPendingAssistantRaw(raw string) []Message {
|
||||
reasoningSignature := ""
|
||||
pendingToolCalls := make([]ToolCallDescriptor, 0, len(payload.Content))
|
||||
pendingToolResults := make([]Message, 0, len(payload.Content))
|
||||
flushAssistantText := func(forceReasoningOnly bool) {
|
||||
if len(textParts) == 0 && (!forceReasoningOnly || len(reasoningParts) == 0) {
|
||||
flushAssistantTurn := func() {
|
||||
if len(textParts) == 0 && len(reasoningParts) == 0 && len(pendingToolCalls) == 0 {
|
||||
return
|
||||
}
|
||||
messages = append(messages, Message{
|
||||
@@ -906,23 +906,10 @@ func buildMessagesFromPendingAssistantRaw(raw string) []Message {
|
||||
Content: formatMessageText(strings.Join(textParts, "")),
|
||||
ReasoningContent: strings.Join(reasoningParts, ""),
|
||||
ReasoningSignature: reasoningSignature,
|
||||
})
|
||||
textParts = textParts[:0]
|
||||
reasoningParts = reasoningParts[:0]
|
||||
reasoningSignature = ""
|
||||
}
|
||||
flushToolCalls := func() {
|
||||
if len(pendingToolCalls) == 0 {
|
||||
return
|
||||
}
|
||||
messages = append(messages, Message{
|
||||
Role: "assistant",
|
||||
Content: "",
|
||||
ReasoningContent: strings.Join(reasoningParts, ""),
|
||||
ReasoningSignature: reasoningSignature,
|
||||
ToolCalls: append([]ToolCallDescriptor(nil), pendingToolCalls...),
|
||||
})
|
||||
messages = append(messages, pendingToolResults...)
|
||||
textParts = textParts[:0]
|
||||
reasoningParts = reasoningParts[:0]
|
||||
reasoningSignature = ""
|
||||
pendingToolCalls = pendingToolCalls[:0]
|
||||
@@ -932,8 +919,6 @@ func buildMessagesFromPendingAssistantRaw(raw string) []Message {
|
||||
for _, item := range payload.Content {
|
||||
switch strings.TrimSpace(item.Type) {
|
||||
case "reasoning":
|
||||
flushToolCalls()
|
||||
flushAssistantText(false)
|
||||
if item.Text != "" {
|
||||
reasoningParts = append(reasoningParts, item.Text)
|
||||
}
|
||||
@@ -941,12 +926,10 @@ func buildMessagesFromPendingAssistantRaw(raw string) []Message {
|
||||
reasoningSignature = strings.TrimSpace(item.Signature)
|
||||
}
|
||||
case "text":
|
||||
flushToolCalls()
|
||||
if item.Text != "" {
|
||||
textParts = append(textParts, item.Text)
|
||||
}
|
||||
case "tool-call":
|
||||
flushAssistantText(false)
|
||||
pendingToolCalls = append(pendingToolCalls, ToolCallDescriptor{
|
||||
ID: strings.TrimSpace(item.ToolCallID),
|
||||
Index: len(pendingToolCalls),
|
||||
@@ -966,9 +949,7 @@ func buildMessagesFromPendingAssistantRaw(raw string) []Message {
|
||||
}
|
||||
}
|
||||
}
|
||||
flushAssistantText(false)
|
||||
flushToolCalls()
|
||||
flushAssistantText(true)
|
||||
flushAssistantTurn()
|
||||
return messages
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package promptengine
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildReplayMessagesFromPendingAssistantOutputsKeepsTextAndToolCallInOneAssistantTurn(t *testing.T) {
|
||||
raw := `{
|
||||
"id":"1",
|
||||
"role":"assistant",
|
||||
"content":[
|
||||
{"type":"reasoning","text":"I need to update service.go.","signature":"reasoning-signature"},
|
||||
{"type":"text","text":"Now let me pass stream.Mode in service.go."},
|
||||
{"type":"tool-call","toolCallId":"call_1","toolName":"PatchEdit","args":{"path":"/workspace/service.go","old_string":"old","new_string":"new"},"result":{"success":{"path":"/workspace/service.go"}}}
|
||||
]
|
||||
}`
|
||||
|
||||
first := BuildReplayMessagesFromPendingAssistantOutputs([]string{raw})
|
||||
if len(first) != 2 {
|
||||
t.Fatalf("message count = %d, want 2 (assistant tool-call turn plus tool result): %#v", len(first), first)
|
||||
}
|
||||
|
||||
assistant := first[0]
|
||||
if assistant.Role != "assistant" {
|
||||
t.Fatalf("assistant role = %q, want assistant", assistant.Role)
|
||||
}
|
||||
if assistant.Content != "Now let me pass stream.Mode in service.go." {
|
||||
t.Fatalf("assistant content = %q", assistant.Content)
|
||||
}
|
||||
if assistant.ReasoningContent != "I need to update service.go." {
|
||||
t.Fatalf("assistant reasoning = %q", assistant.ReasoningContent)
|
||||
}
|
||||
if assistant.ReasoningSignature != "reasoning-signature" {
|
||||
t.Fatalf("assistant reasoning signature = %q", assistant.ReasoningSignature)
|
||||
}
|
||||
if len(assistant.ToolCalls) != 1 {
|
||||
t.Fatalf("assistant tool calls = %d, want 1", len(assistant.ToolCalls))
|
||||
}
|
||||
if assistant.ToolCalls[0].ID != "call_1" || assistant.ToolCalls[0].Function.Name != "PatchEdit" {
|
||||
t.Fatalf("assistant tool call = %#v", assistant.ToolCalls[0])
|
||||
}
|
||||
|
||||
toolResult := first[1]
|
||||
if toolResult.Role != "tool" || toolResult.ToolCallID != "call_1" || toolResult.Name != "PatchEdit" {
|
||||
t.Fatalf("tool result = %#v", toolResult)
|
||||
}
|
||||
|
||||
second := BuildReplayMessagesFromPendingAssistantOutputs([]string{raw})
|
||||
if !reflect.DeepEqual(second, first) {
|
||||
t.Fatalf("rebuilding the same pending output changed messages:\nfirst: %#v\nsecond: %#v", first, second)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user