9cb9505114
Manually uploaded text documents could get stuck showing "generating summary" forever, and editing/re-saving a document mixed its trace into the previous attempt. Three related fixes: 1. finalizeIndexedKnowledgeState no longer marks a text document completed the moment chunks are indexed. Doing so made KnowledgePostProcess hit its non-processing guard and skip the summary/question/graph fan-out, stranding summary_status on "pending". The row now stays "processing" (still retrievable via enable_status=enabled) and post-process remains the sole authority that drives processing -> finalizing -> completed. 2. ProcessManualUpdate now allocates a fresh span-tracking attempt via OpenAttempt. Previously it ran with attempt 0, so processChunks dropped every stage span and KnowledgePostProcess fell back to LatestAttempt, piling the new run's summary/wiki subspans onto the prior attempt's trace. 3. Enrichment workers (summary, question, graph extract) skip when a newer attempt has superseded theirs. A stale subtask from a previous upload/edit/reparse must not read deleted chunks or decrement the new attempt's pending_subtasks_count, which would race-promote the row to completed before the new attempt finishes. Updated TestFinalizeIndexedKnowledgeState to expect the deferred completion for text documents.
71 lines
2.1 KiB
Go
71 lines
2.1 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
)
|
|
|
|
func TestFinalizeIndexedKnowledgeState(t *testing.T) {
|
|
now := time.Date(2026, 5, 20, 12, 0, 0, 0, time.UTC)
|
|
|
|
tests := []struct {
|
|
name string
|
|
hasPendingMultimodal bool
|
|
textChunkCount int
|
|
wantParseStatus string
|
|
wantSummaryStatus string
|
|
}{
|
|
{
|
|
name: "text document stays processing so post-process can fan out enrichment",
|
|
textChunkCount: 2,
|
|
wantParseStatus: types.ParseStatusProcessing,
|
|
wantSummaryStatus: types.SummaryStatusNone,
|
|
},
|
|
{
|
|
name: "empty indexed document is completed without summary work",
|
|
textChunkCount: 0,
|
|
wantParseStatus: types.ParseStatusCompleted,
|
|
wantSummaryStatus: types.SummaryStatusNone,
|
|
},
|
|
{
|
|
name: "document waits while multimodal image work is pending",
|
|
hasPendingMultimodal: true,
|
|
textChunkCount: 2,
|
|
wantParseStatus: types.ParseStatusProcessing,
|
|
wantSummaryStatus: types.SummaryStatusNone,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
knowledge := &types.Knowledge{
|
|
ParseStatus: types.ParseStatusProcessing,
|
|
SummaryStatus: types.SummaryStatusCompleted,
|
|
}
|
|
|
|
finalizeIndexedKnowledgeState(knowledge, 4096, tt.textChunkCount, tt.hasPendingMultimodal, now)
|
|
|
|
if knowledge.ParseStatus != tt.wantParseStatus {
|
|
t.Fatalf("ParseStatus = %q, want %q", knowledge.ParseStatus, tt.wantParseStatus)
|
|
}
|
|
if knowledge.SummaryStatus != tt.wantSummaryStatus {
|
|
t.Fatalf("SummaryStatus = %q, want %q", knowledge.SummaryStatus, tt.wantSummaryStatus)
|
|
}
|
|
if knowledge.EnableStatus != "enabled" {
|
|
t.Fatalf("EnableStatus = %q, want enabled", knowledge.EnableStatus)
|
|
}
|
|
if knowledge.StorageSize != 4096 {
|
|
t.Fatalf("StorageSize = %d, want 4096", knowledge.StorageSize)
|
|
}
|
|
if knowledge.ProcessedAt == nil || !knowledge.ProcessedAt.Equal(now) {
|
|
t.Fatalf("ProcessedAt = %v, want %v", knowledge.ProcessedAt, now)
|
|
}
|
|
if !knowledge.UpdatedAt.Equal(now) {
|
|
t.Fatalf("UpdatedAt = %v, want %v", knowledge.UpdatedAt, now)
|
|
}
|
|
})
|
|
}
|
|
}
|