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