返回 DeepSeek-Reasonix
catalog_reducer.go
根目录 / internal / session / catalog_reducer.go
1 package session
2
3 import (
4 "fmt"
5 "strings"
6 )
7
8 // Retain only stable IDs and short authored previews. Upserts can erase the
9 // first preview, and history replacement can reorder it, so keeping only the
10 // first message would be incorrect. No message/tool/model body survives apply.
11 type catalogReducer struct {
12 state Projection
13 endedTurns map[string]bool
14 resultSequences map[string]uint64
15 positions map[string]bool
16 }
17
18 func (r *catalogReducer) apply(commit Commit) error {
19 for _, ev := range commit.Events {
20 one := commit
21 one.Events = []Event{ev}
22 // Reuse the canonical payload validators and turn/config semantics.
23 if err := applyProjectionCommit(&r.state, one); err != nil {
24 return err
25 }
26 if ev.Kind == "history/replace" || ev.Kind == "legacy/import" {
27 r.positions = nil
28 }
29 if r.positions == nil {
30 r.positions = map[string]bool{}
31 }
32 if ev.Kind == "message/retract" {
33 ids, err := retractedMessageIDs(ev, ev.Payload)
34 if err != nil {
35 return err
36 }
37 for _, id := range ids {
38 delete(r.positions, id)
39 }
40 }
41 for _, message := range r.state.Messages {
42 exists := r.positions[message.ID]
43 if ev.Kind == "message/complete" && exists {
44 return damagedPayload(ev, fmt.Errorf("duplicate stable message id %q", message.ID))
45 }
46 r.positions[strings.Clone(message.ID)] = true
47 }
48 for _, turn := range r.state.Turns {
49 if turn.EndSequence != 0 {
50 if r.endedTurns == nil {
51 r.endedTurns = map[string]bool{}
52 }
53 r.endedTurns[turn.TurnID] = true
54 if turn.Status.Terminal() && turn.MessageID != "" {
55 if r.resultSequences == nil {
56 r.resultSequences = map[string]uint64{}
57 }
58 r.resultSequences[turn.TurnID] = turn.BoundarySequence
59 }
60 }
61 }
62 // Keep only state used by subsequent metadata events. Body-heavy state,
63 // closed turns and authority maps belong to runtime/history projections.
64 s := r.state
65 r.state = Projection{CommittedSequence: s.CommittedSequence, TurnID: s.TurnID,
66 CurrentTurnStart: s.CurrentTurnStart, CurrentTurnMessageID: s.CurrentTurnMessageID, TurnStatus: s.TurnStatus,
67 Title: s.Title, TitleSequence: s.TitleSequence,
68 ModelRef: s.ModelRef, ModelIdentity: s.ModelIdentity,
69 TranscriptInputs: s.TranscriptInputs, HiddenTurns: s.HiddenTurns, RetractedInputs: s.RetractedInputs}
70 }
71 return nil
72 }
73
74 func (r *catalogReducer) metadata(manifest Manifest) catalogMetadata {
75 m := metadataFromProjection(manifest, r.state.CommittedSequence, r.state)
76 for id := range r.endedTurns {
77 if !r.state.HiddenTurns[id] {
78 m.Turns++
79 }
80 }
81 for id, sequence := range r.resultSequences {
82 if !r.state.HiddenTurns[id] {
83 m.ResultSequence = max(m.ResultSequence, sequence)
84 }
85 }
86 return m
87 }
88
88 lines GO