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:
leookun
2026-07-27 00:55:02 +08:00
parent e401303f64
commit 0f6b0f9484
28 changed files with 808 additions and 441 deletions
+4 -23
View File
@@ -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)
}
}