fix(reasoning): persist canonical history for DeepSeek and web chat

This commit is contained in:
lc6464
2026-04-24 21:45:41 +08:00
parent ccd19a48ce
commit bb0f983708
17 changed files with 1016 additions and 43 deletions
+12
View File
@@ -16,6 +16,7 @@ import (
"github.com/sipeed/picoclaw/pkg/fileutil"
"github.com/sipeed/picoclaw/pkg/providers"
"github.com/sipeed/picoclaw/pkg/providers/messageutil"
)
const (
@@ -482,6 +483,9 @@ func readMessages(path string, skip int) ([]providers.Message, error) {
lineNum, filepath.Base(path), err)
continue
}
if messageutil.IsTransientAssistantThoughtMessage(msg) {
continue
}
msgs = append(msgs, msg)
}
if scanner.Err() != nil {
@@ -535,6 +539,10 @@ func (s *JSONLStore) AddFullMessage(
// addMsg is the shared implementation for AddMessage and AddFullMessage.
func (s *JSONLStore) addMsg(sessionKey string, msg providers.Message) error {
if messageutil.IsTransientAssistantThoughtMessage(msg) {
return nil
}
l := s.sessionLock(sessionKey)
l.Lock()
defer l.Unlock()
@@ -684,6 +692,8 @@ func (s *JSONLStore) SetHistory(
sessionKey string,
history []providers.Message,
) error {
history = messageutil.FilterInvalidHistoryMessages(history)
l := s.sessionLock(sessionKey)
l.Lock()
defer l.Unlock()
@@ -762,6 +772,8 @@ func (s *JSONLStore) Compact(
func (s *JSONLStore) rewriteJSONL(
sessionKey string, msgs []providers.Message,
) error {
msgs = messageutil.FilterInvalidHistoryMessages(msgs)
var buf bytes.Buffer
for i, msg := range msgs {
line, err := json.Marshal(msg)
+62
View File
@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"testing"
@@ -155,6 +156,27 @@ func TestAddFullMessage_ToolCallID(t *testing.T) {
}
}
func TestAddFullMessage_DropsTransientAssistantThought(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
err := store.AddFullMessage(ctx, "transient-thought", providers.Message{
Role: "assistant",
ReasoningContent: "internal chain of thought",
})
if err != nil {
t.Fatalf("AddFullMessage: %v", err)
}
history, err := store.GetHistory(ctx, "transient-thought")
if err != nil {
t.Fatalf("GetHistory: %v", err)
}
if len(history) != 0 {
t.Fatalf("expected transient thought to be discarded, got %d messages", len(history))
}
}
func TestGetHistory_EmptySession(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
@@ -243,6 +265,46 @@ func TestSetSummary_GetSummary(t *testing.T) {
}
}
func TestSetHistory_DropsTransientAssistantThought(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
newHistory := []providers.Message{
{Role: "user", Content: "hello"},
{Role: "assistant", ReasoningContent: "internal chain of thought"},
{Role: "assistant", Content: "visible answer", ReasoningContent: "visible thought"},
}
err := store.SetHistory(ctx, "replace", newHistory)
if err != nil {
t.Fatalf("SetHistory: %v", err)
}
history, err := store.GetHistory(ctx, "replace")
if err != nil {
t.Fatalf("GetHistory: %v", err)
}
if len(history) != 2 {
t.Fatalf("expected transient thought to be removed, got %d messages", len(history))
}
if history[0].Role != "user" || history[0].Content != "hello" {
t.Fatalf("history[0] = %+v, want user/hello", history[0])
}
if history[1].Role != "assistant" || history[1].Content != "visible answer" ||
history[1].ReasoningContent != "visible thought" {
t.Fatalf("history[1] = %+v, want assistant visible answer with reasoning", history[1])
}
data, err := os.ReadFile(store.jsonlPath("replace"))
if err != nil {
t.Fatalf("ReadFile(jsonl): %v", err)
}
lines := strings.Split(strings.TrimSpace(string(data)), "\n")
if len(lines) != 2 {
t.Fatalf("jsonl line count = %d, want 2", len(lines))
}
}
func TestSessionMetaScopeAndAliasesPersist(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()