@almadar/std 16.144.0 → 16.146.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/behaviors/lolo/ml/atoms/std-ml-classify.lolo +121 -0
- package/behaviors/lolo/ml/atoms/std-ml-exact-check.lolo +90 -0
- package/behaviors/lolo/ml/atoms/std-ml-infer.lolo +141 -0
- package/behaviors/lolo/ml/atoms/std-ml-label-capture.lolo +75 -0
- package/behaviors/lolo/ml/atoms/std-ml-lookup.lolo +83 -0
- package/behaviors/lolo/ml/atoms/std-ml-posterior.lolo +104 -0
- package/behaviors/lolo/ml/atoms/std-ml-similarity.lolo +166 -0
- package/behaviors/lolo/ml/molecules/std-escalating-decision.lolo +271 -0
- package/behaviors/lolo/ml/molecules/std-knowledge-tracing.lolo +115 -0
- package/behaviors/lolo/ui/core/atoms/std-app-layout.lolo +14 -0
- package/behaviors/lolo/ui/core/atoms/std-board.lolo +20 -6
- package/behaviors/lolo/ui/core/atoms/std-browse.lolo +23 -6
- package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +80 -2
- package/behaviors/lolo/ui/core/atoms/std-stats.lolo +1 -1
- package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +3 -3
- package/behaviors/lolo/ui/core/atoms/ui-typography.lolo +1 -1
- package/behaviors/lolo/ui/core/molecules/ui-calendar-grid.lolo +18 -0
- package/behaviors/lolo/ui/core/molecules/ui-data-grid.lolo +2 -0
- package/behaviors/lolo/ui/core/molecules/ui-data-list.lolo +3 -2
- package/behaviors/lolo/ui/core/molecules/ui-filter-group.lolo +20 -0
- package/behaviors/lolo/ui/core/molecules/ui-search-input.lolo +7 -2
- package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +3 -2
- package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +2 -0
- package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +2 -0
- package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +2 -0
- package/behaviors/lolo/ui/core/organisms/ui-timeline.lolo +2 -0
- package/behaviors/lolo/ui/game/atoms/std-dungeon-combat.lolo +2 -2
- package/behaviors/lolo/ui/game/molecules/std-worldmap-board-2d.lolo +1 -1
- package/behaviors/lolo/ui/game/organisms/ui-boardgame-board.lolo +2 -2
- package/behaviors/lolo/ui/game/organisms/ui-builder-board.lolo +2 -2
- package/behaviors/lolo/ui/game/organisms/ui-debugger-board.lolo +3 -3
- package/behaviors/lolo/ui/game/organisms/ui-physics-sandbox-board.lolo +3 -3
- package/behaviors/lolo/ui/game/organisms/ui-roguelike-board.lolo +1 -1
- package/behaviors/lolo/ui/game/organisms/ui-sequencer-board.lolo +1 -1
- package/behaviors/lolo/ui/learning/atoms/std-algo-heapsort.lolo +2 -2
- package/behaviors/lolo/ui/learning/atoms/std-algo-mst.lolo +3 -3
- package/behaviors/registry/factory-signatures.json +5018 -824
- package/behaviors/registry/ml/atoms/std-ml-classify.orb +622 -0
- package/behaviors/registry/ml/atoms/std-ml-exact-check.orb +505 -0
- package/behaviors/registry/ml/atoms/std-ml-infer.orb +770 -0
- package/behaviors/registry/ml/atoms/std-ml-label-capture.orb +367 -0
- package/behaviors/registry/ml/atoms/std-ml-lookup.orb +473 -0
- package/behaviors/registry/ml/atoms/std-ml-posterior.orb +669 -0
- package/behaviors/registry/ml/atoms/std-ml-similarity.orb +917 -0
- package/behaviors/registry/ml/molecules/std-escalating-decision.orb +1553 -0
- package/behaviors/registry/ml/molecules/std-knowledge-tracing.orb +577 -0
- package/behaviors/registry/ui/core/atoms/std-app-layout.orb +33 -1
- package/behaviors/registry/ui/core/atoms/std-board.orb +64 -20
- package/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
- package/behaviors/registry/ui/core/atoms/std-calendar.orb +213 -3
- package/behaviors/registry/ui/core/atoms/ui-typography.orb +1 -1
- package/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +27 -0
- package/behaviors/registry/ui/core/molecules/ui-data-grid.orb +25 -12
- package/behaviors/registry/ui/core/molecules/ui-data-list.orb +26 -13
- package/behaviors/registry/ui/core/molecules/ui-filter-group.orb +86 -0
- package/behaviors/registry/ui/core/molecules/ui-search-input.orb +37 -1
- package/behaviors/registry/ui/core/molecules/ui-table-view.orb +25 -12
- package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +13 -0
- package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +21 -8
- package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +13 -0
- package/behaviors/registry/ui/core/organisms/ui-timeline.orb +13 -0
- package/behaviors/registry/ui/game/atoms/std-dungeon-combat.orb +4 -4
- package/behaviors/registry/ui/game/molecules/std-worldmap-board-2d.orb +6 -9
- package/behaviors/registry/ui/game/organisms/ui-boardgame-board.orb +4 -4
- package/behaviors/registry/ui/game/organisms/ui-builder-board.orb +4 -4
- package/behaviors/registry/ui/game/organisms/ui-debugger-board.orb +12 -12
- package/behaviors/registry/ui/game/organisms/ui-physics-sandbox-board.orb +19 -19
- package/behaviors/registry/ui/game/organisms/ui-roguelike-board.orb +1 -1
- package/behaviors/registry/ui/game/organisms/ui-sequencer-board.orb +2 -2
- package/behaviors/registry/ui/learning/atoms/std-algo-heapsort.orb +8 -8
- package/canonical-operators.json +30 -6
- package/dist/behaviors/behaviors-embeddings.json +267 -267
- package/dist/behaviors/exports-reader.js +2462 -2080
- package/dist/behaviors/functions/index.d.ts +725 -1
- package/dist/behaviors/functions/index.js +2437 -2081
- package/dist/behaviors/index.d.ts +1 -1
- package/dist/behaviors/index.js +2463 -2081
- package/dist/behaviors/knob-embeddings.json +1 -1
- package/dist/behaviors/query.js +2462 -2080
- package/dist/behaviors/registry/factory-signatures.json +5018 -824
- package/dist/behaviors/registry/ml/atoms/std-ml-classify.orb +622 -0
- package/dist/behaviors/registry/ml/atoms/std-ml-exact-check.orb +505 -0
- package/dist/behaviors/registry/ml/atoms/std-ml-infer.orb +770 -0
- package/dist/behaviors/registry/ml/atoms/std-ml-label-capture.orb +367 -0
- package/dist/behaviors/registry/ml/atoms/std-ml-lookup.orb +473 -0
- package/dist/behaviors/registry/ml/atoms/std-ml-posterior.orb +669 -0
- package/dist/behaviors/registry/ml/atoms/std-ml-similarity.orb +917 -0
- package/dist/behaviors/registry/ml/molecules/std-escalating-decision.orb +1553 -0
- package/dist/behaviors/registry/ml/molecules/std-knowledge-tracing.orb +577 -0
- package/dist/behaviors/registry/ui/core/atoms/std-app-layout.orb +33 -1
- package/dist/behaviors/registry/ui/core/atoms/std-board.orb +64 -20
- package/dist/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
- package/dist/behaviors/registry/ui/core/atoms/std-calendar.orb +213 -3
- package/dist/behaviors/registry/ui/core/atoms/ui-typography.orb +1 -1
- package/dist/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +27 -0
- package/dist/behaviors/registry/ui/core/molecules/ui-data-grid.orb +25 -12
- package/dist/behaviors/registry/ui/core/molecules/ui-data-list.orb +26 -13
- package/dist/behaviors/registry/ui/core/molecules/ui-filter-group.orb +86 -0
- package/dist/behaviors/registry/ui/core/molecules/ui-search-input.orb +37 -1
- package/dist/behaviors/registry/ui/core/molecules/ui-table-view.orb +25 -12
- package/dist/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +13 -0
- package/dist/behaviors/registry/ui/core/organisms/ui-entity-list.orb +21 -8
- package/dist/behaviors/registry/ui/core/organisms/ui-entity-table.orb +13 -0
- package/dist/behaviors/registry/ui/core/organisms/ui-timeline.orb +13 -0
- package/dist/behaviors/registry/ui/game/atoms/std-dungeon-combat.orb +4 -4
- package/dist/behaviors/registry/ui/game/molecules/std-worldmap-board-2d.orb +6 -9
- package/dist/behaviors/registry/ui/game/organisms/ui-boardgame-board.orb +4 -4
- package/dist/behaviors/registry/ui/game/organisms/ui-builder-board.orb +4 -4
- package/dist/behaviors/registry/ui/game/organisms/ui-debugger-board.orb +12 -12
- package/dist/behaviors/registry/ui/game/organisms/ui-physics-sandbox-board.orb +19 -19
- package/dist/behaviors/registry/ui/game/organisms/ui-roguelike-board.orb +1 -1
- package/dist/behaviors/registry/ui/game/organisms/ui-sequencer-board.orb +2 -2
- package/dist/behaviors/registry/ui/learning/atoms/std-algo-heapsort.orb +8 -8
- package/dist/behaviors-embeddings.json +267 -267
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2498 -2088
- package/dist/knob-embeddings.json +1 -1
- package/dist/modules/array.js +28 -0
- package/dist/modules/contract.js +4 -4
- package/dist/modules/data.js +3 -3
- package/dist/modules/index.js +35 -7
- package/dist/registry/factory-signatures.json +5018 -824
- package/dist/registry.js +35 -7
- package/package.json +3 -3
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
app std-ml-similarity "1.0.0"
|
|
2
|
+
"Retrieval rung (R3) — embed a candidate against references and gate on cosine floor + margin; abstains when not confident."
|
|
3
|
+
|
|
4
|
+
orbital MlSimilarityOrbital {
|
|
5
|
+
|
|
6
|
+
entity SimilarityComparison [runtime] {
|
|
7
|
+
query : string = ""
|
|
8
|
+
candidates : [string] = []
|
|
9
|
+
embeddings : [[float]] = []
|
|
10
|
+
queryVector : [float] = []
|
|
11
|
+
candidateVectors : [[float]] = []
|
|
12
|
+
matchIndex : int = 0
|
|
13
|
+
matchScore : float = 0
|
|
14
|
+
matchMargin : float = 0
|
|
15
|
+
reason : string = ""
|
|
16
|
+
request : object = {}
|
|
17
|
+
status : "idle" | "embedding" | "resolved" = idle
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
trait SimilarityRun -> SimilarityComparison [lifecycle, instance] {
|
|
21
|
+
initial: idle
|
|
22
|
+
|
|
23
|
+
config {
|
|
24
|
+
floor : float = 0.82
|
|
25
|
+
@label "Match floor"
|
|
26
|
+
@description "Minimum cosine score for a confident match"
|
|
27
|
+
@tier "policy"
|
|
28
|
+
margin : float = 0.05
|
|
29
|
+
@label "Match margin"
|
|
30
|
+
@description "Minimum gap between the best and second-best score required to trust the match"
|
|
31
|
+
@tier "policy"
|
|
32
|
+
embedModel : string = ""
|
|
33
|
+
@label "Embedding model"
|
|
34
|
+
@description "Reserved for embedding-model selection; llm/embed takes only a texts array today, so this knob is not yet wired into the call"
|
|
35
|
+
@tier "infra"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
state idle {
|
|
39
|
+
INIT -> idle
|
|
40
|
+
(set @entity.query "")
|
|
41
|
+
(set @entity.candidates [])
|
|
42
|
+
(set @entity.embeddings [])
|
|
43
|
+
(set @entity.queryVector [])
|
|
44
|
+
(set @entity.candidateVectors [])
|
|
45
|
+
(set @entity.matchIndex 0)
|
|
46
|
+
(set @entity.matchScore 0)
|
|
47
|
+
(set @entity.matchMargin 0)
|
|
48
|
+
(set @entity.reason "")
|
|
49
|
+
(set @entity.request {})
|
|
50
|
+
(set @entity.status idle)
|
|
51
|
+
|
|
52
|
+
COMPARE -> embedding when (> (array/len ?candidates) 0)
|
|
53
|
+
(set @entity.query ?query)
|
|
54
|
+
(set @entity.candidates ?candidates)
|
|
55
|
+
(set @entity.request ?request)
|
|
56
|
+
(set @entity.status embedding)
|
|
57
|
+
(llm/embed (array/prepend ?candidates ?query) { emit: { success: EMBEDDED, failure: EMBED_FAILED } })
|
|
58
|
+
|
|
59
|
+
COMPARE -> resolved when (== (array/len ?candidates) 0)
|
|
60
|
+
(set @entity.query ?query)
|
|
61
|
+
(set @entity.candidates [])
|
|
62
|
+
(set @entity.embeddings [])
|
|
63
|
+
(set @entity.queryVector [])
|
|
64
|
+
(set @entity.candidateVectors [])
|
|
65
|
+
(set @entity.matchIndex 0)
|
|
66
|
+
(set @entity.matchScore 0)
|
|
67
|
+
(set @entity.matchMargin 0)
|
|
68
|
+
(set @entity.reason "empty candidate list")
|
|
69
|
+
(set @entity.request ?request)
|
|
70
|
+
(set @entity.status resolved)
|
|
71
|
+
(emit SIMILARITY_ABSTAINED { reason: "empty candidate list", request: @entity.request })
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
state embedding {
|
|
75
|
+
EMBEDDED -> resolved
|
|
76
|
+
(set @entity.embeddings ?result)
|
|
77
|
+
(set @entity.status resolved)
|
|
78
|
+
;; An embed result without query + at least one candidate vector is an
|
|
79
|
+
;; abstain, not a crash — the rung can prove nothing from it.
|
|
80
|
+
(if (< (array/len ?result) 2)
|
|
81
|
+
(do
|
|
82
|
+
(set @entity.queryVector [])
|
|
83
|
+
(set @entity.candidateVectors [])
|
|
84
|
+
(set @entity.matchIndex 0)
|
|
85
|
+
(set @entity.matchScore 0)
|
|
86
|
+
(set @entity.matchMargin 0)
|
|
87
|
+
(set @entity.reason "embedding result incomplete")
|
|
88
|
+
(emit SIMILARITY_ABSTAINED { reason: "embedding result incomplete", request: @entity.request }))
|
|
89
|
+
(do
|
|
90
|
+
(set @entity.queryVector (array/first @entity.embeddings))
|
|
91
|
+
(set @entity.candidateVectors (array/drop @entity.embeddings 1))
|
|
92
|
+
(let ((nearest (array/nearest @entity.queryVector @entity.candidateVectors)))
|
|
93
|
+
(do
|
|
94
|
+
(set @entity.matchIndex (object/get @nearest "index"))
|
|
95
|
+
(set @entity.matchScore (object/get @nearest "score"))
|
|
96
|
+
(set @entity.matchMargin (object/get @nearest "margin"))
|
|
97
|
+
(if (and (>= @entity.matchScore @config.floor) (>= @entity.matchMargin @config.margin))
|
|
98
|
+
(do
|
|
99
|
+
(set @entity.reason "")
|
|
100
|
+
(emit SIMILARITY_MATCHED { index: @entity.matchIndex, score: @entity.matchScore }))
|
|
101
|
+
(do
|
|
102
|
+
(set @entity.reason (if (< @entity.matchScore @config.floor) "below floor" "thin margin"))
|
|
103
|
+
(emit SIMILARITY_ABSTAINED { reason: @entity.reason, request: @entity.request })))))))
|
|
104
|
+
|
|
105
|
+
EMBED_FAILED -> resolved
|
|
106
|
+
(set @entity.embeddings [])
|
|
107
|
+
(set @entity.queryVector [])
|
|
108
|
+
(set @entity.candidateVectors [])
|
|
109
|
+
(set @entity.matchIndex 0)
|
|
110
|
+
(set @entity.matchScore 0)
|
|
111
|
+
(set @entity.matchMargin 0)
|
|
112
|
+
(set @entity.reason "embedding unavailable")
|
|
113
|
+
(set @entity.status resolved)
|
|
114
|
+
(emit SIMILARITY_ABSTAINED { reason: "embedding unavailable", request: @entity.request })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
state resolved {
|
|
118
|
+
INIT -> resolved
|
|
119
|
+
(set @entity.status resolved)
|
|
120
|
+
RESET -> idle
|
|
121
|
+
(set @entity.query "")
|
|
122
|
+
(set @entity.candidates [])
|
|
123
|
+
(set @entity.embeddings [])
|
|
124
|
+
(set @entity.queryVector [])
|
|
125
|
+
(set @entity.candidateVectors [])
|
|
126
|
+
(set @entity.matchIndex 0)
|
|
127
|
+
(set @entity.matchScore 0)
|
|
128
|
+
(set @entity.matchMargin 0)
|
|
129
|
+
(set @entity.reason "")
|
|
130
|
+
(set @entity.request {})
|
|
131
|
+
(set @entity.status idle)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
emits {
|
|
135
|
+
EMBEDDED {
|
|
136
|
+
result : [[float]]!
|
|
137
|
+
}
|
|
138
|
+
@description "Raw embeddings from llm/embed — query vector at index 0, candidate vectors following in input order"
|
|
139
|
+
@tier "secondary"
|
|
140
|
+
|
|
141
|
+
SIMILARITY_MATCHED -> external {
|
|
142
|
+
index : int!
|
|
143
|
+
score : float!
|
|
144
|
+
}
|
|
145
|
+
@description "Candidate matched with high confidence — score at or above floor, with a clear margin over the runner-up"
|
|
146
|
+
@tier "primary"
|
|
147
|
+
|
|
148
|
+
SIMILARITY_ABSTAINED -> external {
|
|
149
|
+
reason : string!
|
|
150
|
+
request : object
|
|
151
|
+
}
|
|
152
|
+
@description "No confident match — reason is one of: empty candidate list, below floor, thin margin, embedding unavailable. request echoes the originating request untouched so the next rung can project its own inputs out of it"
|
|
153
|
+
@tier "primary"
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
listens {
|
|
157
|
+
COMPARE {
|
|
158
|
+
query : string!
|
|
159
|
+
candidates : [string]!
|
|
160
|
+
request : object
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
page "/ml-similarity" as MlSimilarityPage -> SimilarityRun
|
|
166
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
app std-escalating-decision "1.0.0"
|
|
2
|
+
"std-escalating-decision — the intelligence ladder itself, as one reusable composition. One request enters at DECIDE and falls through the rungs in order — R0 exact check, R1 table lookup, R3 retrieval, R4 learned inference, R5 generative classification — each rung handing off to the next by abstaining, until one proves the answer. Exits DECIDED, tagged with which rung answered (rung, confidence, matched, reasoning), or DECISION_FAILED if even the generative floor could not serve it. The escalation is a chain, not a hub: each rung listens directly to the rung above it and projects its own inputs out of the carried request, so the trait graph is a DAG and no event ever returns to a rung that already ran. Domain-agnostic: it knows about candidates and categories, not students or quizzes — grading, routing, matching, moderation, and duplicate detection are all this same molecule with different knobs. R2 (the Beta posterior) is deliberately not in this chain: it is the 'decide what to do next' half of a turn and already lives in std-knowledge-tracing."
|
|
3
|
+
|
|
4
|
+
orbital EscalatingDecisionOrbital {
|
|
5
|
+
uses Rung0 from "std/behaviors/std-ml-exact-check"
|
|
6
|
+
uses Rung1 from "std/behaviors/std-ml-lookup"
|
|
7
|
+
uses Rung3 from "std/behaviors/std-ml-similarity"
|
|
8
|
+
uses Rung4 from "std/behaviors/std-ml-infer"
|
|
9
|
+
uses Rung5 from "std/behaviors/std-ml-classify"
|
|
10
|
+
|
|
11
|
+
;; The entry holds no request data: the whole DECIDE payload travels down
|
|
12
|
+
;; the chain as `request`, so there is nothing for this trait to remember.
|
|
13
|
+
entity LadderRequest [runtime] {
|
|
14
|
+
status : "idle" | "dispatched" = idle
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
;; `matched` is deliberately absent: it is an exact key, a table entry, a
|
|
18
|
+
;; match index, a tensor, or a category label depending on which rung served
|
|
19
|
+
;; the request. `any` is honest in payload position but lowers to `string` in
|
|
20
|
+
;; entity position (C-LOLO-ANY-LOWERS-TO-STRING), so the value is carried on
|
|
21
|
+
;; the DECIDED payload only, never mirrored onto this entity as a lie.
|
|
22
|
+
entity LadderVerdict [runtime] {
|
|
23
|
+
rung : string = ""
|
|
24
|
+
confidence : number = 0
|
|
25
|
+
reasoning : string = ""
|
|
26
|
+
failReason : string = ""
|
|
27
|
+
status : "idle" | "decided" | "failed" = idle
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
entity ExactCheck [runtime] {
|
|
31
|
+
matched : boolean = false
|
|
32
|
+
status : "idle" | "checked" = idle
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
entity KeyLookup [runtime] {
|
|
36
|
+
matched : boolean = false
|
|
37
|
+
status : "idle" | "checked" = idle
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
entity SimilarityComparison [runtime] {
|
|
41
|
+
query : string = ""
|
|
42
|
+
candidates : [string] = []
|
|
43
|
+
embeddings : [[float]] = []
|
|
44
|
+
queryVector : [float] = []
|
|
45
|
+
candidateVectors : [[float]] = []
|
|
46
|
+
matchIndex : int = 0
|
|
47
|
+
matchScore : float = 0
|
|
48
|
+
matchMargin : float = 0
|
|
49
|
+
reason : string = ""
|
|
50
|
+
request : object = {}
|
|
51
|
+
status : "idle" | "embedding" | "resolved" = idle
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
entity Inference [runtime] {
|
|
55
|
+
input : object = {}
|
|
56
|
+
output : [float] = []
|
|
57
|
+
confidence : float = 0
|
|
58
|
+
violations : [object] = []
|
|
59
|
+
reason : string = ""
|
|
60
|
+
request : object = {}
|
|
61
|
+
status : "idle" | "inferring" | "resolved" = idle
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
entity ClassifierVerdict [runtime] {
|
|
65
|
+
text : string = ""
|
|
66
|
+
category : string = ""
|
|
67
|
+
confidence : float = 0
|
|
68
|
+
reasoning : string = ""
|
|
69
|
+
errorMessage : string = ""
|
|
70
|
+
request : object = {}
|
|
71
|
+
status : "idle" | "classifying" | "done" = idle
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
;; The ladder's entry. Holds nothing the rungs need — the whole DECIDE
|
|
75
|
+
;; payload travels down the chain as `request`, and each rung projects its
|
|
76
|
+
;; own inputs out of it. This trait listens to no rung, which is what keeps
|
|
77
|
+
;; the graph acyclic.
|
|
78
|
+
trait LadderEntry -> LadderRequest [lifecycle, instance] {
|
|
79
|
+
initial: idle
|
|
80
|
+
|
|
81
|
+
state idle {
|
|
82
|
+
INIT -> idle
|
|
83
|
+
(set @entity.status idle)
|
|
84
|
+
|
|
85
|
+
DECIDE -> dispatched
|
|
86
|
+
(set @entity.status dispatched)
|
|
87
|
+
(emit RUN_EXACT { candidate: ?candidate, key: ?key, request: ? })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
state dispatched {
|
|
91
|
+
INIT -> dispatched
|
|
92
|
+
(set @entity.status dispatched)
|
|
93
|
+
RESET -> idle
|
|
94
|
+
(set @entity.status idle)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
emits {
|
|
98
|
+
RUN_EXACT {
|
|
99
|
+
candidate : any!
|
|
100
|
+
key : any!
|
|
101
|
+
request : object!
|
|
102
|
+
}
|
|
103
|
+
@description "Starts the ladder at the exact-check rung (R0). request is the whole DECIDE payload, carried down the chain so each rung can project its own inputs."
|
|
104
|
+
@tier "secondary"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
listens {
|
|
108
|
+
DECIDE {
|
|
109
|
+
candidate : any!
|
|
110
|
+
key : any!
|
|
111
|
+
accepted : [any]!
|
|
112
|
+
categories : [string]!
|
|
113
|
+
}
|
|
114
|
+
@description "Public entry point — decide the candidate, falling through the ladder until a rung can prove the answer. accepted is R1's table; categories is R3's comparison set (R5's closed label set is the ClassifyRung categories knob, a design-time decision rather than a per-request one)."
|
|
115
|
+
@tier "primary"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
trait ExactRung = Rung0.traits.ExactCheckRun -> ExactCheck {
|
|
120
|
+
listens {
|
|
121
|
+
LadderEntry.RUN_EXACT -> CHECK
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
trait LookupRung = Rung1.traits.LookupRun -> KeyLookup {
|
|
126
|
+
listens {
|
|
127
|
+
ExactRung.EXACT_UNMATCHED -> LOOKUP
|
|
128
|
+
with { candidate: ?candidate, accepted: (object/get ?request "accepted"), request: ?request }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
trait SimilarityRung = Rung3.traits.SimilarityRun -> SimilarityComparison {
|
|
133
|
+
listens {
|
|
134
|
+
LookupRung.KEY_MISS -> COMPARE
|
|
135
|
+
with { query: (str/concat ?candidate), candidates: (object/get ?request "categories"), request: ?request }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
trait InferRung = Rung4.traits.InferenceRun -> Inference {
|
|
140
|
+
listens {
|
|
141
|
+
SimilarityRung.SIMILARITY_ABSTAINED -> INFER
|
|
142
|
+
with { input: ?request, request: ?request }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
trait ClassifyRung = Rung5.traits.ClassifyRun -> ClassifierVerdict {
|
|
147
|
+
listens {
|
|
148
|
+
InferRung.INFER_ABSTAINED -> CLASSIFY
|
|
149
|
+
with { text: (str/concat (object/get ?request "candidate")), request: ?request }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
;; The sink. Listens to every rung's success plus the generative floor's
|
|
154
|
+
;; failure, and emits the one public verdict. Nothing listens to it, so it
|
|
155
|
+
;; closes the DAG.
|
|
156
|
+
trait LadderVerdictSink -> LadderVerdict [lifecycle, instance] {
|
|
157
|
+
initial: idle
|
|
158
|
+
|
|
159
|
+
state idle {
|
|
160
|
+
INIT -> idle
|
|
161
|
+
(set @entity.rung "")
|
|
162
|
+
(set @entity.confidence 0)
|
|
163
|
+
(set @entity.reasoning "")
|
|
164
|
+
(set @entity.failReason "")
|
|
165
|
+
(set @entity.status idle)
|
|
166
|
+
|
|
167
|
+
FROM_EXACT -> decided
|
|
168
|
+
(set @entity.rung "R0")
|
|
169
|
+
(set @entity.confidence 1)
|
|
170
|
+
(set @entity.reasoning "")
|
|
171
|
+
(set @entity.status decided)
|
|
172
|
+
(emit DECIDED { rung: "R0", confidence: 1, matched: ?key, reasoning: "" })
|
|
173
|
+
|
|
174
|
+
FROM_LOOKUP -> decided
|
|
175
|
+
(set @entity.rung "R1")
|
|
176
|
+
(set @entity.confidence 1)
|
|
177
|
+
(set @entity.reasoning "")
|
|
178
|
+
(set @entity.status decided)
|
|
179
|
+
(emit DECIDED { rung: "R1", confidence: 1, matched: ?entry, reasoning: "" })
|
|
180
|
+
|
|
181
|
+
FROM_SIMILARITY -> decided
|
|
182
|
+
(set @entity.rung "R3")
|
|
183
|
+
(set @entity.confidence ?score)
|
|
184
|
+
(set @entity.reasoning "")
|
|
185
|
+
(set @entity.status decided)
|
|
186
|
+
(emit DECIDED { rung: "R3", confidence: ?score, matched: ?index, reasoning: "" })
|
|
187
|
+
|
|
188
|
+
FROM_INFER -> decided
|
|
189
|
+
(set @entity.rung "R4")
|
|
190
|
+
(set @entity.confidence ?confidence)
|
|
191
|
+
(set @entity.reasoning "")
|
|
192
|
+
(set @entity.status decided)
|
|
193
|
+
(emit DECIDED { rung: "R4", confidence: ?confidence, matched: ?output, reasoning: "" })
|
|
194
|
+
|
|
195
|
+
FROM_CLASSIFY -> decided
|
|
196
|
+
(set @entity.rung "R5")
|
|
197
|
+
(set @entity.confidence ?confidence)
|
|
198
|
+
(set @entity.reasoning ?reasoning)
|
|
199
|
+
(set @entity.status decided)
|
|
200
|
+
(emit DECIDED { rung: "R5", confidence: ?confidence, matched: ?category, reasoning: ?reasoning })
|
|
201
|
+
|
|
202
|
+
FROM_CLASSIFY_FAILURE -> failed
|
|
203
|
+
(set @entity.failReason ?error)
|
|
204
|
+
(set @entity.status failed)
|
|
205
|
+
(emit DECISION_FAILED { reason: ?error })
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
state decided {
|
|
209
|
+
INIT -> decided
|
|
210
|
+
(set @entity.status decided)
|
|
211
|
+
RESET -> idle
|
|
212
|
+
(set @entity.rung "")
|
|
213
|
+
(set @entity.confidence 0)
|
|
214
|
+
(set @entity.reasoning "")
|
|
215
|
+
(set @entity.failReason "")
|
|
216
|
+
(set @entity.status idle)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
state failed {
|
|
220
|
+
INIT -> failed
|
|
221
|
+
(set @entity.status failed)
|
|
222
|
+
RESET -> idle
|
|
223
|
+
(set @entity.rung "")
|
|
224
|
+
(set @entity.confidence 0)
|
|
225
|
+
(set @entity.reasoning "")
|
|
226
|
+
(set @entity.failReason "")
|
|
227
|
+
(set @entity.status idle)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
emits {
|
|
231
|
+
DECIDED -> external {
|
|
232
|
+
rung : string!
|
|
233
|
+
confidence : number!
|
|
234
|
+
matched : any!
|
|
235
|
+
reasoning : string!
|
|
236
|
+
}
|
|
237
|
+
@description "The ladder proved an answer. rung names which one served it: R0 exact, R1 table, R3 retrieved, R4 learned, R5 generative."
|
|
238
|
+
@tier "primary"
|
|
239
|
+
|
|
240
|
+
DECISION_FAILED -> external {
|
|
241
|
+
reason : string!
|
|
242
|
+
}
|
|
243
|
+
@description "Even the generative floor (R5) could not answer — the classify service call itself failed."
|
|
244
|
+
@tier "primary"
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
listens {
|
|
248
|
+
FROM_EXACT { key : any! }
|
|
249
|
+
FROM_LOOKUP { entry : any! }
|
|
250
|
+
FROM_SIMILARITY { index : int! score : float! }
|
|
251
|
+
FROM_INFER { output : [float]! confidence : float! }
|
|
252
|
+
FROM_CLASSIFY { category : string! confidence : float! reasoning : string! }
|
|
253
|
+
FROM_CLASSIFY_FAILURE { error : string! }
|
|
254
|
+
|
|
255
|
+
ExactRung.EXACT_MATCHED -> FROM_EXACT
|
|
256
|
+
with { key: ?key }
|
|
257
|
+
LookupRung.KEY_HIT -> FROM_LOOKUP
|
|
258
|
+
with { entry: ?entry }
|
|
259
|
+
SimilarityRung.SIMILARITY_MATCHED -> FROM_SIMILARITY
|
|
260
|
+
with { index: ?index, score: ?score }
|
|
261
|
+
InferRung.INFERRED -> FROM_INFER
|
|
262
|
+
with { output: ?output, confidence: ?confidence }
|
|
263
|
+
ClassifyRung.CLASSIFIED -> FROM_CLASSIFY
|
|
264
|
+
with { category: ?category, confidence: ?confidence, reasoning: ?reasoning }
|
|
265
|
+
ClassifyRung.CLASSIFY_FAILED -> FROM_CLASSIFY_FAILURE
|
|
266
|
+
with { error: ?error }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
page "/ladder/decide" as LadderDecidePage -> LadderEntry, ExactRung, LookupRung, SimilarityRung, InferRung, ClassifyRung, LadderVerdictSink
|
|
271
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
app std-knowledge-tracing "1.0.0"
|
|
2
|
+
"std-knowledge-tracing — turns one true/false observation ('did the learner get this right?') into a tracked belief about mastery, and captures the same observation as a labeled training example in the same motion. Composes std-ml-posterior (the R2 statistical rung — a Beta-Bernoulli belief over an unknown success rate) with std-ml-label-capture: every posterior verdict — MASTERY_REACHED, REMEDIATION_NEEDED, or the abstain EVIDENCE_INSUFFICIENT — becomes a persisted labeled example (the input skill, the posterior's own verdict, and the ground-truth outcome that produced it), so a cheaper rung can later be trained to reproduce the same call without running the statistics. Domain-agnostic: it knows about skills and observations, not students, quizzes, or courses. One instance tracks one belief (e.g. one learner × one skill); the host composes as many instances as it has beliefs to hold."
|
|
3
|
+
orbital KnowledgeTracingOrbital {
|
|
4
|
+
uses Posterior from "std/behaviors/std-ml-posterior"
|
|
5
|
+
uses LabelCap from "std/behaviors/std-ml-label-capture"
|
|
6
|
+
|
|
7
|
+
entity KnowledgeState [runtime, shared] {
|
|
8
|
+
skillId : string = ""
|
|
9
|
+
lastOutcome : boolean = false
|
|
10
|
+
alpha : float = 1
|
|
11
|
+
beta : float = 1
|
|
12
|
+
mean : float = 0
|
|
13
|
+
intervalLow : float = 0
|
|
14
|
+
intervalHigh : float = 0
|
|
15
|
+
observations : int = 0
|
|
16
|
+
status : "idle" | "insufficient" | "mastered" | "remediate" = idle
|
|
17
|
+
input : json = null
|
|
18
|
+
verdict : json = null
|
|
19
|
+
source : string = ""
|
|
20
|
+
outcome : json = null
|
|
21
|
+
sink : string = ""
|
|
22
|
+
recordedAt : number = 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
trait ObservationRelay -> KnowledgeState [lifecycle, instance] {
|
|
26
|
+
initial: idle
|
|
27
|
+
|
|
28
|
+
state idle {
|
|
29
|
+
INIT -> idle
|
|
30
|
+
(set @entity.skillId "")
|
|
31
|
+
(set @entity.lastOutcome false)
|
|
32
|
+
OBSERVE_SKILL -> idle
|
|
33
|
+
(set @entity.skillId ?skillId)
|
|
34
|
+
(set @entity.lastOutcome ?outcome)
|
|
35
|
+
(emit OBSERVATION_MADE { outcome: ?outcome })
|
|
36
|
+
VERDICT_REACHED -> idle
|
|
37
|
+
(emit CAPTURE_LABEL {
|
|
38
|
+
input: { skillId: @entity.skillId },
|
|
39
|
+
verdict: { status: ?status, mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh },
|
|
40
|
+
source: "posterior",
|
|
41
|
+
outcome: @entity.lastOutcome
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
emits {
|
|
46
|
+
OBSERVATION_MADE {
|
|
47
|
+
outcome : boolean!
|
|
48
|
+
}
|
|
49
|
+
@description "Forwards the raw outcome to PosteriorTracker once skillId is recorded on the shared entity."
|
|
50
|
+
@tier "secondary"
|
|
51
|
+
|
|
52
|
+
CAPTURE_LABEL {
|
|
53
|
+
input : any!
|
|
54
|
+
verdict : any!
|
|
55
|
+
source : string!
|
|
56
|
+
outcome : any
|
|
57
|
+
}
|
|
58
|
+
@description "The posterior's verdict reshaped into a labeled example for LabelCaptureListener."
|
|
59
|
+
@tier "secondary"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
listens {
|
|
63
|
+
OBSERVE_SKILL {
|
|
64
|
+
skillId : string!
|
|
65
|
+
outcome : boolean!
|
|
66
|
+
}
|
|
67
|
+
@description "Public entry point — one true/false observation of a learner against one skill."
|
|
68
|
+
@tier "primary"
|
|
69
|
+
|
|
70
|
+
PosteriorTracker.MASTERY_REACHED -> VERDICT_REACHED
|
|
71
|
+
with { status: "mastered", mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh }
|
|
72
|
+
PosteriorTracker.REMEDIATION_NEEDED -> VERDICT_REACHED
|
|
73
|
+
with { status: "remediate", mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh }
|
|
74
|
+
PosteriorTracker.EVIDENCE_INSUFFICIENT -> VERDICT_REACHED
|
|
75
|
+
with { status: "insufficient", mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh }
|
|
76
|
+
|
|
77
|
+
VERDICT_REACHED {
|
|
78
|
+
status : string!
|
|
79
|
+
mean : float!
|
|
80
|
+
intervalLow : float!
|
|
81
|
+
intervalHigh : float!
|
|
82
|
+
}
|
|
83
|
+
@description "Any posterior verdict, tagged with which one it was."
|
|
84
|
+
@tier "secondary"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
trait PosteriorTracker = Posterior.traits.PosteriorBelief -> KnowledgeState {
|
|
89
|
+
config {
|
|
90
|
+
priorAlpha: 1
|
|
91
|
+
priorBeta: 1
|
|
92
|
+
masteryFloor: 0.85
|
|
93
|
+
remediationCeiling: 0.35
|
|
94
|
+
intervalWidthMax: 0.25
|
|
95
|
+
sampleCount: 200
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
listens {
|
|
99
|
+
ObservationRelay.OBSERVATION_MADE -> OBSERVE
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
trait Labels = LabelCap.traits.LabelCaptureListener -> KnowledgeState {
|
|
104
|
+
config {
|
|
105
|
+
sink: "knowledge-tracing"
|
|
106
|
+
sampleRate: 1
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
listens {
|
|
110
|
+
ObservationRelay.CAPTURE_LABEL -> CAPTURE
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
page "/knowledge-tracing" as KnowledgeTracingPage -> ObservationRelay, PosteriorTracker, Labels
|
|
115
|
+
}
|
|
@@ -43,6 +43,7 @@ orbital AppLayoutOrbital {
|
|
|
43
43
|
searchEvent: @config.searchEvent
|
|
44
44
|
topBarActions: @config.topBarActions
|
|
45
45
|
type: @config.layoutPattern
|
|
46
|
+
user: (if @config.viewerName { avatar: @config.viewerAvatar, email: @config.viewerEmail, name: @config.viewerName } null)
|
|
46
47
|
}]
|
|
47
48
|
})
|
|
48
49
|
AppLayoutStateLoaded -> composing
|
|
@@ -94,6 +95,19 @@ orbital AppLayoutOrbital {
|
|
|
94
95
|
@description "Name shown in the top-left of the chrome header."
|
|
95
96
|
@synonyms "app title, product name, brand name, application name, site name"
|
|
96
97
|
@tier "presentation"
|
|
98
|
+
viewerName : string = ""
|
|
99
|
+
@label "Who is signed in"
|
|
100
|
+
@description "Display name of the signed-in viewer, shown in the app bar's account menu — typically @user.name. Blank renders no account menu, so people can see which account (and which persona) they are viewing the app as only when it is actually known."
|
|
101
|
+
@synonyms "signed in user, current user, account, profile, persona, who am i"
|
|
102
|
+
@tier "presentation"
|
|
103
|
+
viewerEmail : string = ""
|
|
104
|
+
@label "Signed-in email"
|
|
105
|
+
@description "Email shown under the name in the account menu — typically @user.email."
|
|
106
|
+
@tier "presentation"
|
|
107
|
+
viewerAvatar : string = ""
|
|
108
|
+
@label "Signed-in avatar"
|
|
109
|
+
@description "Avatar image URL for the account menu."
|
|
110
|
+
@tier "presentation"
|
|
97
111
|
navItems : [NavItem] = []
|
|
98
112
|
@label "Which sections appear in the sidebar?"
|
|
99
113
|
@description "Top-level navigation links shown on the left rail."
|
|
@@ -62,6 +62,17 @@ orbital BoardOrbital {
|
|
|
62
62
|
title: "Failed to load board"
|
|
63
63
|
type: error-state
|
|
64
64
|
})
|
|
65
|
+
;; Card move/delete and add-card save all land here from their own
|
|
66
|
+
;; states; without this the persist success is dropped and the spinner
|
|
67
|
+
;; never resolves (only the failure twin below used to recover).
|
|
68
|
+
BoardItemsSaved -> loading
|
|
69
|
+
(fetch BoardView {
|
|
70
|
+
emit: { failure: BoardItemsLoadFailed, success: BoardItemsLoaded }
|
|
71
|
+
})
|
|
72
|
+
(render-ui main {
|
|
73
|
+
title: "Refreshing board…"
|
|
74
|
+
type: loading-state
|
|
75
|
+
})
|
|
65
76
|
BoardItemsSaveFailed -> error
|
|
66
77
|
(set @entity.errorMessage ?error)
|
|
67
78
|
(render-ui main {
|
|
@@ -81,10 +92,10 @@ orbital BoardOrbital {
|
|
|
81
92
|
type: loading-state
|
|
82
93
|
})
|
|
83
94
|
OPEN_CARD -> viewing_card
|
|
84
|
-
(set @entity.currentId ?
|
|
85
|
-
(set @entity.currentTitle ?
|
|
86
|
-
(set @entity.currentDescription ?
|
|
87
|
-
(set @entity.currentStage ?
|
|
95
|
+
(set @entity.currentId ?id)
|
|
96
|
+
(set @entity.currentTitle ?title)
|
|
97
|
+
(set @entity.currentDescription ?description)
|
|
98
|
+
(set @entity.currentStage ?stage)
|
|
88
99
|
(set @entity.currentNotes ?row.notes)
|
|
89
100
|
(render-ui main {
|
|
90
101
|
children: [{
|
|
@@ -362,6 +373,9 @@ orbital BoardOrbital {
|
|
|
362
373
|
OPEN_CARD {
|
|
363
374
|
id : string!
|
|
364
375
|
row : object!
|
|
376
|
+
title : string
|
|
377
|
+
description : string
|
|
378
|
+
stage : string
|
|
365
379
|
}
|
|
366
380
|
@description "A card within the board has been selected or activated."
|
|
367
381
|
@synonyms "select, activate, view, show"
|
|
@@ -558,7 +572,7 @@ orbital BoardOrbital {
|
|
|
558
572
|
align: center
|
|
559
573
|
children: [{
|
|
560
574
|
action: OPEN_CARD
|
|
561
|
-
actionPayload: { id: @config.cardIdBinding, row: @item }
|
|
575
|
+
actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
|
|
562
576
|
icon: arrow-right
|
|
563
577
|
label: Open
|
|
564
578
|
type: button
|
|
@@ -670,7 +684,7 @@ orbital BoardOrbital {
|
|
|
670
684
|
variant: primary
|
|
671
685
|
}, {
|
|
672
686
|
action: OPEN_CARD
|
|
673
|
-
actionPayload: { id: @config.cardIdBinding, row: @item }
|
|
687
|
+
actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
|
|
674
688
|
className: ml-auto
|
|
675
689
|
icon: arrow-right
|
|
676
690
|
label: Open
|