@almadar/std 16.143.0 → 16.145.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 +154 -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-board.lolo +28 -6
- package/behaviors/lolo/ui/core/atoms/std-browse.lolo +23 -6
- package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +85 -2
- package/behaviors/lolo/ui/core/atoms/std-file-store.lolo +17 -0
- package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +17 -0
- 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/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 +862 -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-board.orb +90 -20
- package/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
- package/behaviors/registry/ui/core/atoms/std-calendar.orb +238 -3
- package/behaviors/registry/ui/core/atoms/std-file-store.orb +40 -0
- package/behaviors/registry/ui/core/atoms/std-version-history.orb +40 -0
- 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/canonical-operators.json +30 -6
- package/dist/behaviors/behaviors-embeddings.json +134 -134
- 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 +862 -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-board.orb +90 -20
- package/dist/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
- package/dist/behaviors/registry/ui/core/atoms/std-calendar.orb +238 -3
- package/dist/behaviors/registry/ui/core/atoms/std-file-store.orb +40 -0
- package/dist/behaviors/registry/ui/core/atoms/std-version-history.orb +40 -0
- 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-embeddings.json +134 -134
- 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 +6 -5
|
@@ -0,0 +1,154 @@
|
|
|
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.queryVector (array/first @entity.embeddings))
|
|
78
|
+
(set @entity.candidateVectors (array/drop @entity.embeddings 1))
|
|
79
|
+
(set @entity.status resolved)
|
|
80
|
+
(let ((nearest (array/nearest @entity.queryVector @entity.candidateVectors)))
|
|
81
|
+
(do
|
|
82
|
+
(set @entity.matchIndex (object/get @nearest "index"))
|
|
83
|
+
(set @entity.matchScore (object/get @nearest "score"))
|
|
84
|
+
(set @entity.matchMargin (object/get @nearest "margin"))
|
|
85
|
+
(if (and (>= @entity.matchScore @config.floor) (>= @entity.matchMargin @config.margin))
|
|
86
|
+
(do
|
|
87
|
+
(set @entity.reason "")
|
|
88
|
+
(emit SIMILARITY_MATCHED { index: @entity.matchIndex, score: @entity.matchScore }))
|
|
89
|
+
(do
|
|
90
|
+
(set @entity.reason (if (< @entity.matchScore @config.floor) "below floor" "thin margin"))
|
|
91
|
+
(emit SIMILARITY_ABSTAINED { reason: @entity.reason, request: @entity.request })))))
|
|
92
|
+
|
|
93
|
+
EMBED_FAILED -> resolved
|
|
94
|
+
(set @entity.embeddings [])
|
|
95
|
+
(set @entity.queryVector [])
|
|
96
|
+
(set @entity.candidateVectors [])
|
|
97
|
+
(set @entity.matchIndex 0)
|
|
98
|
+
(set @entity.matchScore 0)
|
|
99
|
+
(set @entity.matchMargin 0)
|
|
100
|
+
(set @entity.reason "embedding unavailable")
|
|
101
|
+
(set @entity.status resolved)
|
|
102
|
+
(emit SIMILARITY_ABSTAINED { reason: "embedding unavailable", request: @entity.request })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
state resolved {
|
|
106
|
+
INIT -> resolved
|
|
107
|
+
(set @entity.status resolved)
|
|
108
|
+
RESET -> idle
|
|
109
|
+
(set @entity.query "")
|
|
110
|
+
(set @entity.candidates [])
|
|
111
|
+
(set @entity.embeddings [])
|
|
112
|
+
(set @entity.queryVector [])
|
|
113
|
+
(set @entity.candidateVectors [])
|
|
114
|
+
(set @entity.matchIndex 0)
|
|
115
|
+
(set @entity.matchScore 0)
|
|
116
|
+
(set @entity.matchMargin 0)
|
|
117
|
+
(set @entity.reason "")
|
|
118
|
+
(set @entity.request {})
|
|
119
|
+
(set @entity.status idle)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
emits {
|
|
123
|
+
EMBEDDED {
|
|
124
|
+
result : [[float]]!
|
|
125
|
+
}
|
|
126
|
+
@description "Raw embeddings from llm/embed — query vector at index 0, candidate vectors following in input order"
|
|
127
|
+
@tier "secondary"
|
|
128
|
+
|
|
129
|
+
SIMILARITY_MATCHED -> external {
|
|
130
|
+
index : int!
|
|
131
|
+
score : float!
|
|
132
|
+
}
|
|
133
|
+
@description "Candidate matched with high confidence — score at or above floor, with a clear margin over the runner-up"
|
|
134
|
+
@tier "primary"
|
|
135
|
+
|
|
136
|
+
SIMILARITY_ABSTAINED -> external {
|
|
137
|
+
reason : string!
|
|
138
|
+
request : object
|
|
139
|
+
}
|
|
140
|
+
@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"
|
|
141
|
+
@tier "primary"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
listens {
|
|
145
|
+
COMPARE {
|
|
146
|
+
query : string!
|
|
147
|
+
candidates : [string]!
|
|
148
|
+
request : object
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
page "/ml-similarity" as MlSimilarityPage -> SimilarityRun
|
|
154
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -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 {
|
|
@@ -72,11 +83,19 @@ orbital BoardOrbital {
|
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
state viewing_board {
|
|
86
|
+
INIT -> loading
|
|
87
|
+
(fetch BoardView {
|
|
88
|
+
emit: { failure: BoardItemsLoadFailed, success: BoardItemsLoaded }
|
|
89
|
+
})
|
|
90
|
+
(render-ui main {
|
|
91
|
+
title: "Loading board…"
|
|
92
|
+
type: loading-state
|
|
93
|
+
})
|
|
75
94
|
OPEN_CARD -> viewing_card
|
|
76
|
-
(set @entity.currentId ?
|
|
77
|
-
(set @entity.currentTitle ?
|
|
78
|
-
(set @entity.currentDescription ?
|
|
79
|
-
(set @entity.currentStage ?
|
|
95
|
+
(set @entity.currentId ?id)
|
|
96
|
+
(set @entity.currentTitle ?title)
|
|
97
|
+
(set @entity.currentDescription ?description)
|
|
98
|
+
(set @entity.currentStage ?stage)
|
|
80
99
|
(set @entity.currentNotes ?row.notes)
|
|
81
100
|
(render-ui main {
|
|
82
101
|
children: [{
|
|
@@ -354,6 +373,9 @@ orbital BoardOrbital {
|
|
|
354
373
|
OPEN_CARD {
|
|
355
374
|
id : string!
|
|
356
375
|
row : object!
|
|
376
|
+
title : string
|
|
377
|
+
description : string
|
|
378
|
+
stage : string
|
|
357
379
|
}
|
|
358
380
|
@description "A card within the board has been selected or activated."
|
|
359
381
|
@synonyms "select, activate, view, show"
|
|
@@ -550,7 +572,7 @@ orbital BoardOrbital {
|
|
|
550
572
|
align: center
|
|
551
573
|
children: [{
|
|
552
574
|
action: OPEN_CARD
|
|
553
|
-
actionPayload: { id: @config.cardIdBinding, row: @item }
|
|
575
|
+
actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
|
|
554
576
|
icon: arrow-right
|
|
555
577
|
label: Open
|
|
556
578
|
type: button
|
|
@@ -662,7 +684,7 @@ orbital BoardOrbital {
|
|
|
662
684
|
variant: primary
|
|
663
685
|
}, {
|
|
664
686
|
action: OPEN_CARD
|
|
665
|
-
actionPayload: { id: @config.cardIdBinding, row: @item }
|
|
687
|
+
actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
|
|
666
688
|
className: ml-auto
|
|
667
689
|
icon: arrow-right
|
|
668
690
|
label: Open
|
|
@@ -305,6 +305,11 @@ orbital BrowseItemOrbital {
|
|
|
305
305
|
@description "Actions beyond this count collapse into a '⋯' overflow menu. Set 2–3 for dense tables."
|
|
306
306
|
@synonyms "inline actions limit, overflow threshold, max buttons per row"
|
|
307
307
|
@tier "presentation"
|
|
308
|
+
bodySearch : boolean = true
|
|
309
|
+
@label "Show the list's own search box?"
|
|
310
|
+
@description "The dense/feed/gallery looks embed their own search box above the rows. Set false when the page already provides a search affordance (a toolbar Search molecule), so the surface shows one search box instead of two."
|
|
311
|
+
@synonyms "inline search, list search, built-in search, show search, duplicate search"
|
|
312
|
+
@tier "presentation"
|
|
308
313
|
searchPlaceholder : string = "Search…"
|
|
309
314
|
@label "Search box placeholder text"
|
|
310
315
|
@description "Hint text inside the list search box (e.g. 'Search orders…')."
|
|
@@ -327,7 +332,7 @@ orbital BrowseItemOrbital {
|
|
|
327
332
|
@description "Render-ui SExpr rendered after rows load when browseLook = table (the default). Composes the canonical data-grid call wrapped in a stack. See denseBodyContent/feedBodyContent/galleryBodyContent for the other browseLook bodies; any may be overridden directly while inheriting the same trait, state machine, emits, and listens."
|
|
328
333
|
@tier "internal"
|
|
329
334
|
denseBodyContent : render-ui = {
|
|
330
|
-
children: [{
|
|
335
|
+
children: [(if @config.bodySearch {
|
|
331
336
|
align: center
|
|
332
337
|
children: [{
|
|
333
338
|
className: "w-full max-w-2xl"
|
|
@@ -340,7 +345,11 @@ orbital BrowseItemOrbital {
|
|
|
340
345
|
direction: horizontal
|
|
341
346
|
gap: sm
|
|
342
347
|
type: stack
|
|
343
|
-
}
|
|
348
|
+
} {
|
|
349
|
+
children: []
|
|
350
|
+
gap: none
|
|
351
|
+
type: stack
|
|
352
|
+
}), {
|
|
344
353
|
children: [@trait.DenseTableView]
|
|
345
354
|
className: "rounded-lg border border-[var(--color-border)] overflow-x-auto bg-[var(--color-card)] shadow-sm"
|
|
346
355
|
direction: vertical
|
|
@@ -356,7 +365,7 @@ orbital BrowseItemOrbital {
|
|
|
356
365
|
@description "Render-ui SExpr rendered after rows load when browseLook = dense. Spreadsheet-style sticky-header data-table with a search bar; composes columns/itemActions/maxInlineActions/displayPageSize/searchPlaceholder. Folded from std-browse-dense (Phase 5.B)."
|
|
357
366
|
@tier "internal"
|
|
358
367
|
feedBodyContent : render-ui = {
|
|
359
|
-
children: [{
|
|
368
|
+
children: [(if @config.bodySearch {
|
|
360
369
|
align: center
|
|
361
370
|
children: [{
|
|
362
371
|
className: w-full
|
|
@@ -369,7 +378,11 @@ orbital BrowseItemOrbital {
|
|
|
369
378
|
direction: horizontal
|
|
370
379
|
gap: sm
|
|
371
380
|
type: stack
|
|
372
|
-
}
|
|
381
|
+
} {
|
|
382
|
+
children: []
|
|
383
|
+
gap: none
|
|
384
|
+
type: stack
|
|
385
|
+
}), {
|
|
373
386
|
entity: ?data
|
|
374
387
|
fields: []
|
|
375
388
|
gap: md
|
|
@@ -451,7 +464,7 @@ orbital BrowseItemOrbital {
|
|
|
451
464
|
@description "Render-ui SExpr rendered after rows load when browseLook = feed. Chronological card-per-item stream with avatar, title, meta, body, timestamp; composes titleBinding/descriptionBinding/categoryBinding/badgeBinding/metaBinding/searchPlaceholder/itemActions. Folded from std-browse-feed (Phase 5.B)."
|
|
452
465
|
@tier "internal"
|
|
453
466
|
galleryBodyContent : render-ui = {
|
|
454
|
-
children: [{
|
|
467
|
+
children: [(if @config.bodySearch {
|
|
455
468
|
align: center
|
|
456
469
|
children: [{
|
|
457
470
|
className: w-full
|
|
@@ -464,7 +477,11 @@ orbital BrowseItemOrbital {
|
|
|
464
477
|
direction: horizontal
|
|
465
478
|
gap: sm
|
|
466
479
|
type: stack
|
|
467
|
-
}
|
|
480
|
+
} {
|
|
481
|
+
children: []
|
|
482
|
+
gap: none
|
|
483
|
+
type: stack
|
|
484
|
+
}), {
|
|
468
485
|
className: w-full
|
|
469
486
|
entity: ?data
|
|
470
487
|
fields: []
|