@knolo/cli 6.2.0 → 6.2.2
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/README.md +82 -3
- package/bin/flight.mjs +367 -0
- package/bin/knolo.mjs +52 -0
- package/bin/setup.mjs +260 -0
- package/bin/skill.mjs +1308 -0
- package/package.json +2 -2
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/closure.rs +395 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/cover.rs +692 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/evaluate.rs +343 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/json.rs +486 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/mod.rs +14 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/model.rs +1068 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/kar/session.rs +1156 -0
- package/templates/icp-knowledge-canister/canisters/knolo-core-rust/src/lib.rs +7 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
//! Frozen evaluation order. Roots are hashed from the canonical objects, not from indexes.
|
|
2
|
+
|
|
3
|
+
use crate::kar::closure::{
|
|
4
|
+
close_frontiers, frontier_root, frontier_witness_root, witness_json, Witness,
|
|
5
|
+
};
|
|
6
|
+
use crate::kar::cover::{
|
|
7
|
+
choices_json, decision_json, decision_root, evidence_set_root, select_cover, Choice,
|
|
8
|
+
CoveragePair, Decision,
|
|
9
|
+
};
|
|
10
|
+
use crate::kar::json::{digest_json, Json};
|
|
11
|
+
use crate::kar::model::{
|
|
12
|
+
anchor_root, empty_frontiers, frontiers_json, lookup_requirement, resolve_anchor,
|
|
13
|
+
unresolved_evidence, AnchorCommitment, Graph, Indexes, Plan, PreparedImage, FRONTIERS,
|
|
14
|
+
KAR_VERSION,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
#[derive(Debug, Clone)]
|
|
18
|
+
pub struct KarResult {
|
|
19
|
+
pub status: String,
|
|
20
|
+
pub evidence_ids: Vec<String>,
|
|
21
|
+
pub choices: Vec<Choice>,
|
|
22
|
+
pub frontiers: Vec<(String, Vec<String>)>,
|
|
23
|
+
pub witnesses: Vec<Witness>,
|
|
24
|
+
pub decision: Decision,
|
|
25
|
+
pub roots: Roots,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[derive(Debug, Clone)]
|
|
29
|
+
pub struct Roots {
|
|
30
|
+
pub knowledge_root: String,
|
|
31
|
+
pub semantic_root: String,
|
|
32
|
+
pub query_root: String,
|
|
33
|
+
pub plan_root: String,
|
|
34
|
+
pub anchor_root: String,
|
|
35
|
+
pub frontier_root: String,
|
|
36
|
+
pub frontier_witness_root: String,
|
|
37
|
+
pub evidence_set_root: String,
|
|
38
|
+
pub decision_root: String,
|
|
39
|
+
pub kar_root: String,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub struct BoundInputs<'a> {
|
|
43
|
+
pub prepared: Option<&'a PreparedImage>,
|
|
44
|
+
pub graph: Option<&'a Graph>,
|
|
45
|
+
pub indexes: Option<&'a Indexes>,
|
|
46
|
+
pub image_input: &'a Json,
|
|
47
|
+
pub graph_input: &'a Json,
|
|
48
|
+
pub query: &'a str,
|
|
49
|
+
pub plan_input: &'a Json,
|
|
50
|
+
pub knowledge_root: Option<&'a str>,
|
|
51
|
+
pub semantic_root: Option<&'a str>,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub fn evaluate_bound(input: &BoundInputs<'_>) -> KarResult {
|
|
55
|
+
let query_root = digest_json(&Json::String(input.query.to_string()));
|
|
56
|
+
let plan_root = digest_json(input.plan_input);
|
|
57
|
+
let knowledge_root = input
|
|
58
|
+
.knowledge_root
|
|
59
|
+
.map(|value| value.to_string())
|
|
60
|
+
.unwrap_or_else(|| {
|
|
61
|
+
if let Some(prepared) = input.prepared {
|
|
62
|
+
prepared.knowledge_root.clone()
|
|
63
|
+
} else {
|
|
64
|
+
digest_json(&Json::object(vec![
|
|
65
|
+
("image", input.image_input.clone()),
|
|
66
|
+
("invalidImage", Json::Bool(true)),
|
|
67
|
+
]))
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
let semantic_root = input
|
|
71
|
+
.semantic_root
|
|
72
|
+
.map(|value| value.to_string())
|
|
73
|
+
.unwrap_or_else(|| {
|
|
74
|
+
if let Some(graph) = input.graph {
|
|
75
|
+
crate::kar::model::semantic_root_of(graph)
|
|
76
|
+
} else {
|
|
77
|
+
digest_json(&Json::object(vec![
|
|
78
|
+
("graph", input.graph_input.clone()),
|
|
79
|
+
("invalid", Json::Bool(true)),
|
|
80
|
+
]))
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
let plan = crate::kar::model::validate_plan(input.plan_input);
|
|
84
|
+
let fallback_profile = plan
|
|
85
|
+
.as_ref()
|
|
86
|
+
.map(|item| item.profile.clone())
|
|
87
|
+
.unwrap_or_default();
|
|
88
|
+
let fallback_coverage = plan
|
|
89
|
+
.as_ref()
|
|
90
|
+
.map(required_coverage)
|
|
91
|
+
.unwrap_or_else(zero_coverage);
|
|
92
|
+
|
|
93
|
+
let emit = |status: &str,
|
|
94
|
+
evidence_ids: Vec<String>,
|
|
95
|
+
frontiers: Vec<(String, Vec<String>)>,
|
|
96
|
+
witnesses: Vec<Witness>,
|
|
97
|
+
choices: Vec<Choice>,
|
|
98
|
+
decision: Option<Decision>,
|
|
99
|
+
commitment: AnchorCommitment|
|
|
100
|
+
-> KarResult {
|
|
101
|
+
let mut decided = decision.unwrap_or_else(|| Decision {
|
|
102
|
+
status: status.to_string(),
|
|
103
|
+
profile: fallback_profile.clone(),
|
|
104
|
+
cardinality: 0,
|
|
105
|
+
coverage: fallback_coverage.clone(),
|
|
106
|
+
redundancy: None,
|
|
107
|
+
});
|
|
108
|
+
decided.status = status.to_string();
|
|
109
|
+
let anchor = anchor_root(&commitment);
|
|
110
|
+
let frontier = frontier_root(&frontiers);
|
|
111
|
+
let witness = frontier_witness_root(&witnesses);
|
|
112
|
+
let evidence = evidence_set_root(&choices);
|
|
113
|
+
let decision_hash = decision_root(&decided);
|
|
114
|
+
let kar_root = digest_json(&Json::object(vec![
|
|
115
|
+
("anchorRoot", Json::String(anchor.clone())),
|
|
116
|
+
("decisionRoot", Json::String(decision_hash.clone())),
|
|
117
|
+
("evidenceSetRoot", Json::String(evidence.clone())),
|
|
118
|
+
("frontierRoot", Json::String(frontier.clone())),
|
|
119
|
+
("knowledgeRoot", Json::String(knowledge_root.clone())),
|
|
120
|
+
("planRoot", Json::String(plan_root.clone())),
|
|
121
|
+
("queryRoot", Json::String(query_root.clone())),
|
|
122
|
+
("semanticRoot", Json::String(semantic_root.clone())),
|
|
123
|
+
]));
|
|
124
|
+
KarResult {
|
|
125
|
+
status: status.to_string(),
|
|
126
|
+
evidence_ids,
|
|
127
|
+
choices,
|
|
128
|
+
frontiers,
|
|
129
|
+
witnesses,
|
|
130
|
+
decision: decided,
|
|
131
|
+
roots: Roots {
|
|
132
|
+
knowledge_root,
|
|
133
|
+
semantic_root,
|
|
134
|
+
query_root,
|
|
135
|
+
plan_root,
|
|
136
|
+
anchor_root: anchor,
|
|
137
|
+
frontier_root: frontier,
|
|
138
|
+
frontier_witness_root: witness,
|
|
139
|
+
evidence_set_root: evidence,
|
|
140
|
+
decision_root: decision_hash,
|
|
141
|
+
kar_root,
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
let Some(plan) = plan else {
|
|
147
|
+
return emit(
|
|
148
|
+
"PLAN_INVALID",
|
|
149
|
+
Vec::new(),
|
|
150
|
+
empty_frontiers(),
|
|
151
|
+
Vec::new(),
|
|
152
|
+
Vec::new(),
|
|
153
|
+
None,
|
|
154
|
+
AnchorCommitment::empty(),
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
let (Some(prepared), Some(graph)) = (input.prepared, input.graph) else {
|
|
158
|
+
return emit(
|
|
159
|
+
"GRAPH_INVALID",
|
|
160
|
+
Vec::new(),
|
|
161
|
+
empty_frontiers(),
|
|
162
|
+
Vec::new(),
|
|
163
|
+
Vec::new(),
|
|
164
|
+
None,
|
|
165
|
+
AnchorCommitment::empty(),
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
if graph.knowledge_root != prepared.knowledge_root
|
|
169
|
+
|| unresolved_evidence(graph, &prepared.texts)
|
|
170
|
+
{
|
|
171
|
+
return emit(
|
|
172
|
+
"GRAPH_NOT_BOUND",
|
|
173
|
+
Vec::new(),
|
|
174
|
+
empty_frontiers(),
|
|
175
|
+
Vec::new(),
|
|
176
|
+
Vec::new(),
|
|
177
|
+
None,
|
|
178
|
+
AnchorCommitment::empty(),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
if let Some(lexical) = &plan.lexical {
|
|
182
|
+
for evidence_id in &lexical.evidence_ids {
|
|
183
|
+
if prepared.texts.iter().all(|(id, _)| id != evidence_id) {
|
|
184
|
+
return emit(
|
|
185
|
+
"GRAPH_NOT_BOUND",
|
|
186
|
+
Vec::new(),
|
|
187
|
+
empty_frontiers(),
|
|
188
|
+
Vec::new(),
|
|
189
|
+
Vec::new(),
|
|
190
|
+
None,
|
|
191
|
+
AnchorCommitment::empty(),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let node_ids = input
|
|
197
|
+
.indexes
|
|
198
|
+
.map(|indexes| indexes.node_ids.clone())
|
|
199
|
+
.unwrap_or_else(|| graph.nodes.clone());
|
|
200
|
+
let (commitment, rejected) = resolve_anchor(input.query, &node_ids, &plan.anchor);
|
|
201
|
+
if let crate::kar::model::AnchorSpec::Recompute { procedure } = &plan.anchor {
|
|
202
|
+
if procedure != "member-id-v1" {
|
|
203
|
+
return emit(
|
|
204
|
+
"PLAN_INVALID",
|
|
205
|
+
Vec::new(),
|
|
206
|
+
empty_frontiers(),
|
|
207
|
+
Vec::new(),
|
|
208
|
+
Vec::new(),
|
|
209
|
+
None,
|
|
210
|
+
commitment,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if rejected {
|
|
215
|
+
return emit(
|
|
216
|
+
"ANCHOR_REJECTED",
|
|
217
|
+
Vec::new(),
|
|
218
|
+
empty_frontiers(),
|
|
219
|
+
Vec::new(),
|
|
220
|
+
Vec::new(),
|
|
221
|
+
None,
|
|
222
|
+
commitment,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
let closed = match close_frontiers(graph, &plan, &commitment.nodes, input.indexes) {
|
|
226
|
+
Ok(closed) => closed,
|
|
227
|
+
Err(()) => {
|
|
228
|
+
return emit(
|
|
229
|
+
"CLOSURE_BOUND_EXCEEDED",
|
|
230
|
+
Vec::new(),
|
|
231
|
+
empty_frontiers(),
|
|
232
|
+
Vec::new(),
|
|
233
|
+
Vec::new(),
|
|
234
|
+
None,
|
|
235
|
+
commitment,
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
let cover = select_cover(&plan, &closed.admissions, &prepared.texts);
|
|
240
|
+
emit(
|
|
241
|
+
&cover.status,
|
|
242
|
+
cover.evidence_ids,
|
|
243
|
+
closed.frontiers,
|
|
244
|
+
closed.witnesses,
|
|
245
|
+
cover.choices,
|
|
246
|
+
Some(cover.decision),
|
|
247
|
+
commitment,
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
pub fn certificate_json(result: &KarResult) -> Json {
|
|
252
|
+
Json::object(vec![
|
|
253
|
+
("choices", choices_json(&result.choices)),
|
|
254
|
+
("decision", decision_json(&result.decision)),
|
|
255
|
+
(
|
|
256
|
+
"evidenceIds",
|
|
257
|
+
Json::Array(
|
|
258
|
+
result
|
|
259
|
+
.evidence_ids
|
|
260
|
+
.iter()
|
|
261
|
+
.cloned()
|
|
262
|
+
.map(Json::String)
|
|
263
|
+
.collect(),
|
|
264
|
+
),
|
|
265
|
+
),
|
|
266
|
+
("frontiers", frontiers_json(&result.frontiers)),
|
|
267
|
+
("roots", roots_json(&result.roots)),
|
|
268
|
+
("status", Json::String(result.status.clone())),
|
|
269
|
+
("version", Json::String(KAR_VERSION.into())),
|
|
270
|
+
(
|
|
271
|
+
"witnesses",
|
|
272
|
+
Json::Array(result.witnesses.iter().map(witness_json).collect()),
|
|
273
|
+
),
|
|
274
|
+
])
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fn roots_json(roots: &Roots) -> Json {
|
|
278
|
+
Json::object(vec![
|
|
279
|
+
("anchorRoot", Json::String(roots.anchor_root.clone())),
|
|
280
|
+
("decisionRoot", Json::String(roots.decision_root.clone())),
|
|
281
|
+
(
|
|
282
|
+
"evidenceSetRoot",
|
|
283
|
+
Json::String(roots.evidence_set_root.clone()),
|
|
284
|
+
),
|
|
285
|
+
("frontierRoot", Json::String(roots.frontier_root.clone())),
|
|
286
|
+
(
|
|
287
|
+
"frontierWitnessRoot",
|
|
288
|
+
Json::String(roots.frontier_witness_root.clone()),
|
|
289
|
+
),
|
|
290
|
+
("karRoot", Json::String(roots.kar_root.clone())),
|
|
291
|
+
("knowledgeRoot", Json::String(roots.knowledge_root.clone())),
|
|
292
|
+
("planRoot", Json::String(roots.plan_root.clone())),
|
|
293
|
+
("queryRoot", Json::String(roots.query_root.clone())),
|
|
294
|
+
("semanticRoot", Json::String(roots.semantic_root.clone())),
|
|
295
|
+
])
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
fn required_coverage(plan: &Plan) -> Vec<(String, CoveragePair)> {
|
|
299
|
+
FRONTIERS
|
|
300
|
+
.iter()
|
|
301
|
+
.map(|frontier| {
|
|
302
|
+
let required = if plan.coverage_mode == "requirements" {
|
|
303
|
+
lookup_requirement(plan, frontier).len() as i64
|
|
304
|
+
} else {
|
|
305
|
+
1
|
|
306
|
+
};
|
|
307
|
+
(
|
|
308
|
+
(*frontier).to_string(),
|
|
309
|
+
CoveragePair {
|
|
310
|
+
covered: 0,
|
|
311
|
+
required,
|
|
312
|
+
},
|
|
313
|
+
)
|
|
314
|
+
})
|
|
315
|
+
.collect()
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
fn zero_coverage() -> Vec<(String, CoveragePair)> {
|
|
319
|
+
FRONTIERS
|
|
320
|
+
.iter()
|
|
321
|
+
.map(|frontier| {
|
|
322
|
+
(
|
|
323
|
+
(*frontier).to_string(),
|
|
324
|
+
CoveragePair {
|
|
325
|
+
covered: 0,
|
|
326
|
+
required: 0,
|
|
327
|
+
},
|
|
328
|
+
)
|
|
329
|
+
})
|
|
330
|
+
.collect()
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
pub fn status_code(status: &str) -> Option<String> {
|
|
334
|
+
match status {
|
|
335
|
+
"GRAPH_INVALID" => Some("KAR_GRAPH_INVALID".into()),
|
|
336
|
+
"GRAPH_NOT_BOUND" => Some("KAR_GRAPH_NOT_BOUND".into()),
|
|
337
|
+
"PLAN_INVALID" => Some("KAR_PLAN_INVALID".into()),
|
|
338
|
+
"ANCHOR_REJECTED" => Some("KAR_ANCHOR_REJECTED".into()),
|
|
339
|
+
"CLOSURE_BOUND_EXCEEDED" => Some("KAR_CLOSURE_BOUND_EXCEEDED".into()),
|
|
340
|
+
"SEARCH_BOUND_EXCEEDED" => Some("KAR_SEARCH_BOUND_EXCEEDED".into()),
|
|
341
|
+
_ => None,
|
|
342
|
+
}
|
|
343
|
+
}
|