refactor: improve chat history persistence reliability with metadata-only migration, error handling, and optimized file updates

This commit is contained in:
CJACK.
2026-04-22 16:22:04 +00:00
parent 797ab77873
commit fe8a6bd3cd
6 changed files with 490 additions and 150 deletions

View File

@@ -36,9 +36,13 @@ func CollectStream(resp *http.Response, thinkingEnabled bool, closeBody bool) Co
currentType = "thinking"
}
_ = deepseek.ScanSSELines(resp, func(line []byte) bool {
if chunk, done, parsed := ParseDeepSeekSSELine(line); parsed && !done {
chunk, done, parsed := ParseDeepSeekSSELine(line)
if parsed && !done {
collector.ingestChunk(chunk)
}
if done {
return false
}
if stopped {
return true
}
@@ -52,7 +56,8 @@ func CollectStream(resp *http.Response, thinkingEnabled bool, closeBody bool) Co
contentFilter = true
}
// Keep scanning to collect late-arriving citation metadata lines
// that can appear after response/status=FINISHED.
// that can appear after response/status=FINISHED, but stop as soon
// as [DONE] arrives.
stopped = true
return true
}

View File

@@ -5,6 +5,7 @@ import (
"net/http"
"strings"
"testing"
"time"
)
// ─── CollectStream edge cases ────────────────────────────────────────
@@ -227,6 +228,39 @@ func TestCollectStreamStatusFinished(t *testing.T) {
}
}
func TestCollectStreamStopsOnDoneAfterFinished(t *testing.T) {
pr, pw := io.Pipe()
defer func() { _ = pw.Close() }()
resp := &http.Response{
StatusCode: http.StatusOK,
Header: make(http.Header),
Body: pr,
}
resultCh := make(chan CollectResult, 1)
go func() {
resultCh <- CollectStream(resp, false, false)
}()
_, _ = io.WriteString(pw, "data: {\"p\":\"response/content\",\"v\":\"Hello\"}\n")
_, _ = io.WriteString(pw, "data: {\"p\":\"response/status\",\"v\":\"FINISHED\"}\n")
_, _ = io.WriteString(pw, "data: {\"p\":\"response/fragments/-1/results\",\"v\":[{\"url\":\"https://example.com/a\",\"cite_index\":1}]}\n")
_, _ = io.WriteString(pw, "data: [DONE]\n")
select {
case result := <-resultCh:
if result.Text != "Hello" {
t.Fatalf("expected text to freeze at FINISHED, got %q", result.Text)
}
if got := result.CitationLinks[1]; got != "https://example.com/a" {
t.Fatalf("expected citation metadata after FINISHED, got %q", got)
}
case <-time.After(500 * time.Millisecond):
t.Fatal("CollectStream did not stop on [DONE] after FINISHED")
}
}
func TestCollectStreamStopsOnContentFilterStatus(t *testing.T) {
resp := makeHTTPResponse(
"data: {\"p\":\"response/content\",\"v\":\"safe\"}\n" +