@konneal/engine 0.2.104 → 0.2.110

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.
Files changed (41) hide show
  1. package/dist/ablate.d.ts +31 -0
  2. package/dist/ask-DBIGV3SY.js +15 -0
  3. package/dist/{chunk-25ECO5M7.js → chunk-2R7OVYAO.js} +377 -185
  4. package/dist/{chunk-ANUHRJ4Z.js → chunk-32UDKBA2.js} +4 -3
  5. package/dist/{chunk-U4ERFUOK.js → chunk-4NXH5ZI6.js} +1 -1
  6. package/dist/{chunk-DTW4UW2E.js → chunk-DJ65EF5G.js} +1 -1
  7. package/dist/{chunk-TFGJ6NYS.js → chunk-HAU5INEJ.js} +5 -5
  8. package/dist/{chunk-KNCRYAX3.js → chunk-TH5XGPD5.js} +1 -1
  9. package/dist/{chunk-NCZHN6QW.js → chunk-TNDH2QRT.js} +175 -22
  10. package/dist/{chunk-BOAITSVV.js → chunk-VT7DR6NQ.js} +11 -1
  11. package/dist/config.d.ts +6 -0
  12. package/dist/openapi-types.d.ts +24 -0
  13. package/dist/prompts/doc-summary.md +1 -0
  14. package/dist/prompts/speculative-verify.md +9 -0
  15. package/dist/route.d.ts +29 -0
  16. package/dist/search-HKGN7F3O.js +13 -0
  17. package/dist/speculative.d.ts +9 -0
  18. package/dist/{tools-EXDADNWX.js → tools-GSLHFD37.js} +4 -4
  19. package/dist/worker_mcp/src/index.js +2 -2
  20. package/dist/worker_public/src/config.js +1 -1
  21. package/dist/worker_public/src/index.js +20 -16
  22. package/dist/worker_public/src/requestScope.js +2 -2
  23. package/docs/spec-pipeline.md +12 -1
  24. package/package.json +1 -1
  25. package/workers/worker_public/openapi.yaml +53 -0
  26. package/workers/worker_public/prompts/doc-summary.md +1 -0
  27. package/workers/worker_public/prompts/speculative-verify.md +9 -0
  28. package/workers/worker_public/src/ablate.ts +97 -0
  29. package/workers/worker_public/src/admin.ts +14 -6
  30. package/workers/worker_public/src/ask.ts +120 -16
  31. package/workers/worker_public/src/config.ts +10 -0
  32. package/workers/worker_public/src/pipeline.ts +5 -2
  33. package/workers/worker_public/src/route.ts +77 -0
  34. package/workers/worker_public/src/speculative.ts +39 -0
  35. package/workers/worker_public/src/stages/graphPpr.ts +189 -0
  36. package/workers/worker_public/src/stages/index.ts +19 -0
  37. package/workers/worker_public/src/stages/sectionDescent.ts +91 -7
  38. package/workers/worker_public/src/stages/types.ts +6 -0
  39. package/workers/worker_public/src/verdict.ts +5 -0
  40. package/dist/ask-M3VRAIJB.js +0 -15
  41. package/dist/search-5SNP5CHL.js +0 -13
@@ -17,6 +17,7 @@ import { hyde } from "./hyde.ts";
17
17
  import { glossary } from "./glossary.ts";
18
18
  import { conceptGraph } from "./conceptGraph.ts";
19
19
  import { graphLane } from "./graphLane.ts";
20
+ import { graphPpr } from "./graphPpr.ts";
20
21
  import { licensedLane } from "./licensedLane.ts";
21
22
  import { licensedCover } from "./licensedCover.ts";
22
23
  import { multiQuery } from "./multiQuery.ts";
@@ -48,6 +49,7 @@ export const STAGES: Stage[] = [
48
49
  glossary,
49
50
  conceptGraph,
50
51
  graphLane,
52
+ graphPpr,
51
53
  licensedLane,
52
54
  multiQuery,
53
55
  subQuery,
@@ -75,3 +77,20 @@ export const STAGES: Stage[] = [
75
77
  dedup,
76
78
  windowFloor,
77
79
  ];
80
+
81
+ export const STAGE_NAMES: string[] = STAGES.map((s) => s.name);
82
+
83
+ /** The ablation switch (TODO.sota/09): a configuration is a stage list.
84
+ * The projection keeps REGISTRY order — a subset can narrow the
85
+ * pipeline, never reorder it (the ordering invariants are
86
+ * load-bearing). Unknown names throw so a typo'd configuration can
87
+ * never measure the wrong pipeline silently. */
88
+ export function projectStages(names: readonly string[]): Stage[] {
89
+ const wanted = new Set(names);
90
+ const projected = STAGES.filter((s) => wanted.has(s.name));
91
+ if (projected.length !== wanted.size) {
92
+ const known = new Set(STAGE_NAMES);
93
+ for (const n of wanted) if (!known.has(n)) throw new Error(`unknown stage: ${n}`);
94
+ }
95
+ return projected;
96
+ }
@@ -1,15 +1,60 @@
1
- // Section-summary units → leaf evidence (FABLE multi-granularity): a
2
- // depth-1 summary vector that ranked is a navigation node, not quotable
3
- // evidence: fetch its top child clauses (metadata-filtered, same query
4
- // vector) so the model gets source text to cite, then retire the
5
- // synthetic summary — the answer contract grounds claims in source
6
- // clauses, never in our own summaries. The summary stays only when no
7
- // child answered (it is then the doc's sole representative).
1
+ // Section-summary units → leaf evidence (FABLE multi-granularity, the
2
+ // RAPTOR extension of TODO.sota/06's catalog row 3): a ranked summary
3
+ // is a navigation node, not quotable evidence — the stage descends it
4
+ // to source clauses and retires the synthetic node. Two levels:
5
+ // level 1 (section summary): child_anchors are CLAUSE anchors — the
6
+ // same-query vector search finds the clauses within the document.
7
+ // level 2 (document summary): child_anchors are LEVEL-1 UNIT ids —
8
+ // they come back by id (the ≤20 getByIds law), and each consumed
9
+ // section's clauses come from the D1 corpus DIRECTLY (the l6a law:
10
+ // the corpus is the source of truth; the index metadata filters
11
+ // are dead on this index).
12
+ // The summary stays only when no child answered (it is then the doc's
13
+ // sole representative).
8
14
  import { THRESHOLDS } from "../config.ts";
9
15
  import type { ChunkMeta } from "../../../shared/chunk";
10
16
  import type { Hit } from "../../../shared/chunk";
11
17
  import type { Stage } from "./types.ts";
12
18
 
19
+ const CHUNK_COLS =
20
+ "id, doc_id, docidentifier, doctype, doc_number, edition, language, clause_anchor, clause_title, status, superseded_by, corpus, tier, text, unit_id, block";
21
+
22
+ /** A level-2 document summary's section children, by id (the ≤20
23
+ * getByIds law caps each read). */
24
+ async function sectionChildren(env: any, unitIds: string[]): Promise<Hit[]> {
25
+ const kids: Hit[] = [];
26
+ for (let i = 0; i < unitIds.length; i += 20) {
27
+ const got = (await env.VECTORIZE.getByIds(unitIds.slice(i, i + 20))) ?? [];
28
+ for (const v of got) {
29
+ if (!v?.metadata?.child_anchors) continue;
30
+ kids.push({
31
+ id: v.id,
32
+ score: 0,
33
+ metadata: v.metadata as ChunkMeta,
34
+ text: String((v.metadata as any).chunk_text ?? ""),
35
+ });
36
+ }
37
+ }
38
+ return kids;
39
+ }
40
+
41
+ /** A section's child clauses, from the D1 corpus directly. */
42
+ async function clausesOf(env: any, docId: string, anchors: string[], limit: number): Promise<Hit[]> {
43
+ const marks = anchors.map(() => "?").join(",");
44
+ const res = await env.DB.prepare(
45
+ `SELECT ${CHUNK_COLS} FROM chunks WHERE doc_id = ? AND clause_anchor IN (${marks}) LIMIT ${limit}`,
46
+ )
47
+ .bind(docId, ...anchors)
48
+ .all()
49
+ .catch(() => ({ results: [] }));
50
+ return (res.results ?? []).map((r: any) => ({
51
+ id: String(r.id),
52
+ score: 0,
53
+ metadata: r as unknown as ChunkMeta,
54
+ text: String(r.text ?? ""),
55
+ }));
56
+ }
57
+
13
58
  export const sectionDescent: Stage = {
14
59
  name: "section-descent",
15
60
  failure: "additive",
@@ -20,6 +65,45 @@ export const sectionDescent: Stage = {
20
65
  const sectionHit = c.finalHits.find(
21
66
  (h) => h.metadata.section_summary === "1" && h.metadata.child_anchors && h.score > 0,
22
67
  )!;
68
+
69
+ if ((sectionHit.metadata as any).summary_level === "2") {
70
+ // document summary → its leading section nodes → their clauses
71
+ const unitIds = sectionHit
72
+ .metadata.child_anchors!.split(",")
73
+ .map((s) => s.trim())
74
+ .filter(Boolean)
75
+ .slice(0, 12);
76
+ const kids = await sectionChildren(c.env, unitIds);
77
+ // document reading order (child_anchors order) is the doc-scope
78
+ // prior; the summaries were written in it
79
+ const order = new Map(unitIds.map((id, i) => [id, i]));
80
+ kids.sort((a, b) => (order.get(a.id) ?? 99) - (order.get(b.id) ?? 99));
81
+ const collected: Hit[] = [];
82
+ const consumed: string[] = [];
83
+ for (const kid of kids.slice(0, 2)) {
84
+ const anchors = kid.metadata.child_anchors!.split(",").map((s) => s.trim()).filter(Boolean).slice(0, 25);
85
+ if (!anchors.length) continue;
86
+ const clauses = (await clausesOf(c.env, String(kid.metadata.doc_id), anchors, 6))
87
+ .filter((x) => !c.finalHits.some((h) => h.id === x.id) && !collected.some((h) => h.id === x.id))
88
+ .slice(0, 2)
89
+ .map((x) => ({ ...x, score: sectionHit.score * THRESHOLDS.sectionDescentDiscount }));
90
+ if (clauses.length) {
91
+ collected.push(...clauses);
92
+ consumed.push(kid.id);
93
+ }
94
+ }
95
+ if (collected.length) {
96
+ c.finalHits = [...c.finalHits.filter((h) => h !== sectionHit), ...collected];
97
+ console.log(
98
+ "section descent (doc):",
99
+ sectionHit.metadata.docidentifier,
100
+ "→",
101
+ collected.map((x) => "§" + x.metadata.clause_anchor).join(", "),
102
+ );
103
+ }
104
+ return;
105
+ }
106
+
23
107
  const kids = sectionHit
24
108
  .metadata.child_anchors!.split(",")
25
109
  .map((s) => s.trim())
@@ -67,6 +67,12 @@ export interface RetrieveOptions {
67
67
  * metadata stays). Null = the deployment declares no licensed
68
68
  * content, the scope is inert. */
69
69
  standardKeys?: Set<string> | null;
70
+ /** The ablation switch (TODO.sota/09): run the registry PROJECTED to
71
+ * these stage names (registry order preserved, unknown names throw —
72
+ * projectStages owns the semantics). Absent = the full registry.
73
+ * Admin-gated at the ask boundary (src/ablate.ts); a stage-subset
74
+ * ask never reads or writes the answer caches. */
75
+ stageSubset?: string[];
70
76
  }
71
77
 
72
78
  export interface GlossaryEntry {
@@ -148,6 +148,11 @@ export function extractChecks(content: unknown): string[] {
148
148
  const out: string[] = [];
149
149
  const push = (e?: string) => { const b = e && oclBody(e); if (b) out.push(b); };
150
150
  push(c.check);
151
+ // the plain oiml-r60 projection carries the OCL under `expression`
152
+ // (the r60-lml projection under `check`) — the dead-load geometry
153
+ // nodes bound for their whole life and evaluated to null because
154
+ // their only check sat under the key nobody read
155
+ push(c.expression);
151
156
  push(c.limit?.expression);
152
157
  push(c.acceptance_criteria?.limit && !c.acceptance_criteria.limit.expression?.includes("ocl{") ? null : c.acceptance_criteria?.limit?.expression);
153
158
  // structured threshold limits compile to a comparison expression
@@ -1,15 +0,0 @@
1
- import {
2
- handleAsk
3
- } from "./chunk-NCZHN6QW.js";
4
- import "./chunk-ANUHRJ4Z.js";
5
- import "./chunk-25ECO5M7.js";
6
- import "./chunk-KNCRYAX3.js";
7
- import "./chunk-DTW4UW2E.js";
8
- import "./chunk-A3QHHUN5.js";
9
- import "./chunk-U4ERFUOK.js";
10
- import "./chunk-BOAITSVV.js";
11
- import "./chunk-JZFXSTYX.js";
12
- import "./chunk-3FYJM7LH.js";
13
- export {
14
- handleAsk
15
- };
@@ -1,13 +0,0 @@
1
- import {
2
- handleSearch
3
- } from "./chunk-TFGJ6NYS.js";
4
- import "./chunk-25ECO5M7.js";
5
- import "./chunk-KNCRYAX3.js";
6
- import "./chunk-DTW4UW2E.js";
7
- import "./chunk-A3QHHUN5.js";
8
- import "./chunk-U4ERFUOK.js";
9
- import "./chunk-BOAITSVV.js";
10
- import "./chunk-3FYJM7LH.js";
11
- export {
12
- handleSearch
13
- };