collector.go raw
1 package profile
2
3 import (
4 "sync"
5
6 "git.mleku.dev/mleku/dendrite/pkg/grow"
7 )
8
9 // Collector accumulates a Profile from growth events.
10 // Thread-safe: multiple goroutines can call Record concurrently.
11 type Collector struct {
12 mu sync.Mutex
13 profile *Profile
14 lastTag string // tag of most recently bonded element, for transition tracking
15 }
16
17 // NewCollector creates a collector with an empty profile.
18 func NewCollector() *Collector {
19 return &Collector{profile: New()}
20 }
21
22 // RecordGrowEvent processes a single growth event.
23 func (c *Collector) RecordGrowEvent(ev grow.Event) {
24 c.mu.Lock()
25 defer c.mu.Unlock()
26
27 c.profile.TokensIngested++
28
29 switch ev.Type {
30 case grow.EventBonded:
31 c.profile.BondEvents++
32 c.profile.PathFreq[ev.NodeID]++
33 c.profile.WalkDistHist[ev.Steps]++
34 if ev.Element != nil {
35 tag := ev.Element.Type()
36 c.profile.BondDist[tag]++
37 if c.lastTag != "" {
38 c.profile.TransitionFreq[[2]string{c.lastTag, tag}]++
39 }
40 c.lastTag = tag
41 }
42 case grow.EventRejected:
43 c.profile.RejectEvents++
44 case grow.EventExpired:
45 c.profile.ExpireEvents++
46 }
47 }
48
49 // RecordProbeEvent processes a single probe event from inference on a
50 // trained lattice. Probe matches record into the same profile structure
51 // as growth bonds — the statistical fingerprint is what matters, not
52 // whether bonding actually occurred.
53 func (c *Collector) RecordProbeEvent(ev grow.ProbeEvent) {
54 c.mu.Lock()
55 defer c.mu.Unlock()
56
57 c.profile.TokensIngested++
58
59 switch ev.Type {
60 case grow.EventBonded:
61 c.profile.BondEvents++
62 c.profile.PathFreq[ev.NodeID]++
63 c.profile.WalkDistHist[ev.Steps]++
64 if ev.Element != nil {
65 tag := ev.Element.Type()
66 c.profile.BondDist[tag]++
67 if c.lastTag != "" {
68 c.profile.TransitionFreq[[2]string{c.lastTag, tag}]++
69 }
70 c.lastTag = tag
71 }
72 case grow.EventExpired:
73 c.profile.ExpireEvents++
74 }
75 }
76
77 // RecordNewVertex increments the new vertex counter.
78 // Called when the hexagram engine creates a new node (OpNucleate/OpExplore).
79 func (c *Collector) RecordNewVertex() {
80 c.mu.Lock()
81 c.profile.NewVertices++
82 c.mu.Unlock()
83 }
84
85 // Snapshot returns a deep copy of the current profile.
86 func (c *Collector) Snapshot() *Profile {
87 c.mu.Lock()
88 defer c.mu.Unlock()
89 return c.profile.Clone()
90 }
91