@konneal/engine 0.2.28 → 0.2.30

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-INCFGVH3.js";
4
- import "./chunk-4G5OGWXY.js";
3
+ } from "./chunk-TDKNQPA2.js";
4
+ import "./chunk-F66NYFUK.js";
5
5
  import "./chunk-2RKYO3OC.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-JGKSUSF5.js";
@@ -22747,12 +22747,31 @@ var federate = {
22747
22747
  // workers/worker_public/src/stages/seal.ts
22748
22748
  var seal = {
22749
22749
  name: "seal",
22750
- when: (c) => !!c.opts.sealScope,
22750
+ when: (c) => !!c.opts.sealScope || !!c.opts.editionSteer || !!c.opts.editionExclude,
22751
22751
  run: (c) => {
22752
22752
  const before = c.hits.length;
22753
22753
  const scope = c.opts.sealScope;
22754
- c.hits = c.hits.filter((h) => h.metadata.doc_number === scope.doc_number && (!scope.edition || h.metadata.edition === scope.edition));
22755
- console.log("context seal:", before, "\u2192", c.hits.length, "candidates within", `doc#${scope.doc_number}${scope.edition ? "@" + scope.edition : ""}`);
22754
+ if (scope) {
22755
+ c.hits = c.hits.filter((h) => h.metadata.doc_number === scope.doc_number && (!scope.edition || h.metadata.edition === scope.edition));
22756
+ console.log("context seal:", before, "\u2192", c.hits.length, "candidates within", `doc#${scope.doc_number}${scope.edition ? "@" + scope.edition : ""}`);
22757
+ return;
22758
+ }
22759
+ const exclude = c.opts.editionExclude;
22760
+ if (exclude) {
22761
+ const before2 = c.hits.length;
22762
+ c.hits = c.hits.filter((h) => !(h.metadata.doc_number === exclude.doc_number && h.metadata.edition !== exclude.edition));
22763
+ console.log("edition exclude:", before2, "\u2192", c.hits.length, "after dropping superseded candidates of", `doc#${exclude.doc_number}@${exclude.edition}`);
22764
+ return;
22765
+ }
22766
+ const steer = c.opts.editionSteer;
22767
+ const current = [];
22768
+ const superseded = [];
22769
+ for (const h of c.hits) {
22770
+ const same = h.metadata.doc_number === steer.doc_number;
22771
+ (same && h.metadata.edition !== steer.edition ? superseded : current).push(h);
22772
+ }
22773
+ c.hits = [...current, ...superseded];
22774
+ console.log("edition steer:", superseded.length, "superseded candidates demoted behind", current.length, "current-edition candidates of", `doc#${steer.doc_number}@${steer.edition}`);
22756
22775
  }
22757
22776
  };
22758
22777
 
@@ -7,7 +7,7 @@ import {
7
7
  sessionFrom,
8
8
  telemetry,
9
9
  understandQuery
10
- } from "./chunk-4G5OGWXY.js";
10
+ } from "./chunk-F66NYFUK.js";
11
11
  import {
12
12
  corsHeaders,
13
13
  err,
@@ -28,7 +28,7 @@ import {
28
28
  syntheticUnderstanding,
29
29
  telemetry,
30
30
  understandQuery
31
- } from "./chunk-4G5OGWXY.js";
31
+ } from "./chunk-F66NYFUK.js";
32
32
  import {
33
33
  corsHeaders,
34
34
  err,
@@ -2188,7 +2188,7 @@ Answer account questions from these records ONLY: name the record when you use i
2188
2188
  // the evidence view's ground truth: the exact passages this
2189
2189
  // answer was built from, compact — cache hits carry none,
2190
2190
  // because the cache stores the answer and never the passages
2191
- passages: usedHits.slice(0, 8).map((h) => ({ d: h.metadata.docidentifier ?? "", a: h.metadata.clause_anchor ?? "", t: (h.text ?? "").slice(0, 600), ...h.metadata.table_selection ? { s: h.metadata.table_selection } : {} }))
2191
+ passages: usedHits.slice(0, 8).map((h) => ({ d: h.metadata.docidentifier ?? "", a: h.metadata.clause_anchor ?? "", t: (h.text ?? "").slice(0, h.metadata.block === "table" ? 1400 : 600), b: h.metadata.block === "table" || void 0, ...h.metadata.table_selection ? { s: h.metadata.table_selection } : {} }))
2192
2192
  });
2193
2193
  telemetry(env, ctx, tier, "ask", model, true, c2.text.length, queryHash, q.lang, void 0, telemetryMeta());
2194
2194
  const canonical = c2.text;
@@ -1 +1,4 @@
1
- export declare function buildJudgeContext(passages: string[], machine?: string[]): string;
1
+ export declare function buildJudgeContext(passages: (string | {
2
+ text: string;
3
+ table?: boolean;
4
+ })[], machine?: string[]): string;
@@ -3,4 +3,7 @@ export interface FaithfulnessResult {
3
3
  score: number;
4
4
  ungrounded_claims: string[];
5
5
  }
6
- export declare function scoreFaithfulness(ai: any, model: string, answer: string, passages: string[], machine?: string[]): Promise<FaithfulnessResult | null>;
6
+ export declare function scoreFaithfulness(ai: any, model: string, answer: string, passages: (string | {
7
+ text: string;
8
+ table?: boolean;
9
+ })[], machine?: string[]): Promise<FaithfulnessResult | null>;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handleSearch
3
- } from "./chunk-5LCPKJ6V.js";
4
- import "./chunk-4G5OGWXY.js";
3
+ } from "./chunk-S7NLWPNX.js";
4
+ import "./chunk-F66NYFUK.js";
5
5
  import "./chunk-2RKYO3OC.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-JGKSUSF5.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleSearch
3
- } from "../../chunk-5LCPKJ6V.js";
3
+ } from "../../chunk-S7NLWPNX.js";
4
4
  import {
5
5
  bindModelNode,
6
6
  checkQuoteAnchors,
@@ -9,7 +9,7 @@ import {
9
9
  modelGroundingBlock,
10
10
  scoreJudge,
11
11
  standardForDocNumber
12
- } from "../../chunk-INCFGVH3.js";
12
+ } from "../../chunk-TDKNQPA2.js";
13
13
  import {
14
14
  buildMessages,
15
15
  citations,
@@ -32,7 +32,7 @@ import {
32
32
  sessionFrom,
33
33
  telemetry,
34
34
  understandQuery
35
- } from "../../chunk-4G5OGWXY.js";
35
+ } from "../../chunk-F66NYFUK.js";
36
36
  import {
37
37
  authenticate,
38
38
  corsHeaders,
@@ -393,7 +393,11 @@ function parseVerdict(text) {
393
393
 
394
394
  // workers/worker_public/src/faithfulness-context.ts
395
395
  function buildJudgeContext(passages, machine = []) {
396
- const context = passages.slice(0, 8).map((p, i) => `[${i + 1}] ${p.replace(/\s+/g, " ").slice(0, 1400)}`).join("\n");
396
+ const context = passages.slice(0, 8).map((p, i) => {
397
+ const text = typeof p === "string" ? p : p.text;
398
+ const limit = typeof p !== "string" && p.table ? 2400 : 1400;
399
+ return `[${i + 1}] ${text.replace(/\s+/g, " ").slice(0, limit)}`;
400
+ }).join("\n");
397
401
  const machineContext = machine.length ? "\n" + machine.slice(0, 6).map((m) => `[M] ${m.replace(/\s+/g, " ").slice(0, 400)}`).join("\n") : "";
398
402
  return context + machineContext;
399
403
  }
@@ -992,7 +996,7 @@ async function handleMcp(env, ctx, req, tier, key) {
992
996
  // stream:false forces the JSON lane (anon defaults to SSE)
993
997
  body: JSON.stringify({ ...args, stream: false })
994
998
  });
995
- const res = name === "ask" ? await (await import("../../ask-KELYM6RJ.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-4UK3RBEQ.js")).handleSearch(env, ctx, inner, tier, key);
999
+ const res = name === "ask" ? await (await import("../../ask-43C2WZZO.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-NHTT5DDI.js")).handleSearch(env, ctx, inner, tier, key);
996
1000
  return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
997
1001
  });
998
1002
  if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
@@ -1452,17 +1456,21 @@ async function verifyRoute(c) {
1452
1456
  try {
1453
1457
  const u = await understandQuery(env.AI, roleModel(env, "understand"), query, [], []);
1454
1458
  let lexicalBoost;
1459
+ let editionExclude = null;
1455
1460
  try {
1456
1461
  const fam = u?.doc_number ? refCodec().familyOf(u.doc_number) : null;
1457
1462
  if (fam) {
1458
1463
  const row = await env.DB.prepare("SELECT edition FROM documents WHERE family = ?1 AND active = 1 ORDER BY edition DESC LIMIT 1").bind(fam).first().catch(() => null);
1459
- if (row?.edition) lexicalBoost = String(row.edition);
1464
+ if (row?.edition) {
1465
+ lexicalBoost = String(row.edition);
1466
+ editionExclude = { doc_number: fam.split("-").pop() ?? "", edition: String(row.edition) };
1467
+ }
1460
1468
  }
1461
1469
  } catch {
1462
1470
  }
1463
1471
  const bound = await bindModelNode(env, { query, standardKeys: entitlementScope(standardKeysFrom(body)) });
1464
1472
  const modelGrounding = bound && !bound.gated ? modelGroundingBlock(bound) : null;
1465
- const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost });
1473
+ const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionExclude });
1466
1474
  const passages = retrieved.hits.map((h) => h.text);
1467
1475
  const anchors = checkQuoteAnchors(answer, passages);
1468
1476
  const refs = [...answer.matchAll(/\[\[u:([^\]]+)\]\]/g)].map((m) => m[1]);
@@ -1474,7 +1482,7 @@ async function verifyRoute(c) {
1474
1482
  ];
1475
1483
  const machine = (Array.isArray(body?.blocks) ? body.blocks : []).filter((b) => b?.type === "verdict" && b?.payload).map((b) => [b.payload.check, b.payload.meaning, b.payload.definition, b.payload.violation_meaning].filter((x) => typeof x === "string" && x).join(" \u2014 ")).filter(Boolean);
1476
1484
  if (modelGrounding) machine.unshift(modelGrounding);
1477
- const faith = await scoreFaithfulness(env.AI, roleModel(env, "grader"), answer, retrieved.hits.map((h) => h.text), machine);
1485
+ const faith = await scoreFaithfulness(env.AI, roleModel(env, "grader"), answer, retrieved.hits.map((h) => ({ text: h.text, table: h.metadata.block === "table" || void 0 })), machine);
1478
1486
  return json({
1479
1487
  checks,
1480
1488
  judged: faith ? { name: "faithfulness", deterministic: false, score: faith.score, ungrounded_claims: faith.ungrounded_claims.slice(0, 5) } : null,
package/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  "playwright": "^1.62.1",
28
28
  "yaml": "^2.9.1"
29
29
  },
30
- "version": "0.2.28",
30
+ "version": "0.2.30",
31
31
  "description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
32
32
  "license": "BSD-3-Clause",
33
33
  "type": "module",
@@ -1098,7 +1098,7 @@ async function handleAsk(
1098
1098
  // the evidence view's ground truth: the exact passages this
1099
1099
  // answer was built from, compact — cache hits carry none,
1100
1100
  // because the cache stores the answer and never the passages
1101
- passages: usedHits.slice(0, 8).map((h: Hit) => ({ d: h.metadata.docidentifier ?? "", a: h.metadata.clause_anchor ?? "", t: (h.text ?? "").slice(0, 600), ...((h.metadata as any).table_selection ? { s: (h.metadata as any).table_selection } : {}) })) });
1101
+ passages: usedHits.slice(0, 8).map((h: Hit) => ({ d: h.metadata.docidentifier ?? "", a: h.metadata.clause_anchor ?? "", t: (h.text ?? "").slice(0, (h.metadata as any).block === "table" ? 1400 : 600), b: (h.metadata as any).block === "table" || undefined, ...((h.metadata as any).table_selection ? { s: (h.metadata as any).table_selection } : {}) })) });
1102
1102
  telemetry(env, ctx, tier, "ask", model, true, c2.text.length, queryHash, q.lang, undefined, telemetryMeta());
1103
1103
  const canonical = c2.text;
1104
1104
  // streamed answers can't be regenerated mid-flight; enforcement
@@ -4,10 +4,19 @@
4
4
  // service's own machine-computed model data: a machine-grounded answer
5
5
  // quotes it legitimately, so the judge must see it.
6
6
 
7
- export function buildJudgeContext(passages: string[], machine: string[] = []): string {
7
+ export function buildJudgeContext(
8
+ passages: (string | { text: string; table?: boolean })[],
9
+ machine: string[] = [],
10
+ ): string {
8
11
  const context = passages
9
12
  .slice(0, 8)
10
- .map((p, i) => `[${i + 1}] ${p.replace(/\s+/g, " ").slice(0, 1400)}`)
13
+ .map((p, i) => {
14
+ const text = typeof p === "string" ? p : p.text;
15
+ // a table's evidence lives across its whole row set — a truncated
16
+ // table hides the very rows the answer computed from
17
+ const limit = typeof p !== "string" && p.table ? 2400 : 1400;
18
+ return `[${i + 1}] ${text.replace(/\s+/g, " ").slice(0, limit)}`;
19
+ })
11
20
  .join("\n");
12
21
  const machineContext = machine.length
13
22
  ? "\n" + machine.slice(0, 6).map((m) => `[M] ${m.replace(/\s+/g, " ").slice(0, 400)}`).join("\n")
@@ -19,7 +19,7 @@ export async function scoreFaithfulness(
19
19
  ai: any,
20
20
  model: string,
21
21
  answer: string,
22
- passages: string[],
22
+ passages: (string | { text: string; table?: boolean })[],
23
23
  machine: string[] = [],
24
24
  ): Promise<FaithfulnessResult | null> {
25
25
  if (!answer || !passages.length) return null;
@@ -234,19 +234,23 @@ async function verifyRoute(c: RouteContext): Promise<Response> {
234
234
  // grounding — a verify that grounds against different passages than
235
235
  // the answer used judges a different answer
236
236
  let lexicalBoost: string | undefined;
237
+ let editionExclude: { doc_number: string; edition: string } | null = null;
237
238
  try {
238
239
  const fam = u?.doc_number ? refCodec().familyOf(u.doc_number) : null;
239
240
  if (fam) {
240
241
  const row = await env.DB.prepare("SELECT edition FROM documents WHERE family = ?1 AND active = 1 ORDER BY edition DESC LIMIT 1")
241
242
  .bind(fam).first().catch(() => null);
242
- if (row?.edition) lexicalBoost = String(row.edition);
243
+ if (row?.edition) {
244
+ lexicalBoost = String(row.edition);
245
+ editionExclude = { doc_number: fam.split("-").pop() ?? "", edition: String(row.edition) };
246
+ }
243
247
  }
244
248
  } catch {
245
249
  // degrade to unsteered retrieval
246
250
  }
247
251
  const bound = await bindModelNode(env, { query, standardKeys: entitlementScope(standardKeysFrom(body)) });
248
252
  const modelGrounding = bound && !bound.gated ? modelGroundingBlock(bound) : null;
249
- const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost });
253
+ const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionExclude });
250
254
  const passages = retrieved.hits.map((h: Hit) => h.text);
251
255
  const anchors = checkQuoteAnchors(answer, passages);
252
256
  const refs = [...answer.matchAll(/\[\[u:([^\]]+)\]\]/g)].map((m) => m[1]);
@@ -265,7 +269,7 @@ async function verifyRoute(c: RouteContext): Promise<Response> {
265
269
  .filter((x: unknown) => typeof x === "string" && x).join(" — "))
266
270
  .filter(Boolean);
267
271
  if (modelGrounding) machine.unshift(modelGrounding);
268
- const faith = await scoreFaithfulness(env.AI, roleModel(env, "grader"), answer, retrieved.hits.map((h: Hit) => h.text), machine);
272
+ const faith = await scoreFaithfulness(env.AI, roleModel(env, "grader"), answer, retrieved.hits.map((h: Hit) => ({ text: h.text, table: (h.metadata as any).block === "table" || undefined })), machine);
269
273
  return json({
270
274
  checks,
271
275
  judged: faith ? { name: "faithfulness", deterministic: false, score: faith.score, ungrounded_claims: faith.ungrounded_claims.slice(0, 5) } : null,
@@ -6,11 +6,30 @@ import type { Stage } from "./types.ts";
6
6
 
7
7
  export const seal: Stage = {
8
8
  name: "seal",
9
- when: (c) => !!c.opts.sealScope,
9
+ when: (c) => !!c.opts.sealScope || !!c.opts.editionSteer || !!c.opts.editionExclude,
10
10
  run: (c) => {
11
11
  const before = c.hits.length;
12
12
  const scope = c.opts.sealScope!;
13
+ if (scope) {
13
14
  c.hits = c.hits.filter((h) => h.metadata.doc_number === scope.doc_number && (!scope.edition || h.metadata.edition === scope.edition));
14
- console.log("context seal:", before, "→", c.hits.length, "candidates within", `doc#${scope.doc_number}${scope.edition ? "@" + scope.edition : ""}`);
15
+ console.log("context seal:", before, "→", c.hits.length, "candidates within", `doc#${scope.doc_number}${scope.edition ? "@" + scope.edition : ""}`);
16
+ return;
17
+ }
18
+ const exclude = c.opts.editionExclude!;
19
+ if (exclude) {
20
+ const before = c.hits.length;
21
+ c.hits = c.hits.filter((h) => !(h.metadata.doc_number === exclude.doc_number && h.metadata.edition !== exclude.edition));
22
+ console.log("edition exclude:", before, "→", c.hits.length, "after dropping superseded candidates of", `doc#${exclude.doc_number}@${exclude.edition}`);
23
+ return;
24
+ }
25
+ const steer = c.opts.editionSteer!;
26
+ const current: typeof c.hits = [];
27
+ const superseded: typeof c.hits = [];
28
+ for (const h of c.hits) {
29
+ const same = h.metadata.doc_number === steer.doc_number;
30
+ (same && h.metadata.edition !== steer.edition ? superseded : current).push(h);
31
+ }
32
+ c.hits = [...current, ...superseded];
33
+ console.log("edition steer:", superseded.length, "superseded candidates demoted behind", current.length, "current-edition candidates of", `doc#${steer.doc_number}@${steer.edition}`);
15
34
  },
16
35
  };
@@ -29,6 +29,17 @@ export interface RetrieveOptions {
29
29
  * in-family passage out of the final hits, sealing the answer to
30
30
  * zero despite a healthy in-family pool. */
31
31
  sealScope?: { doc_number: string; edition?: string } | null;
32
+ /** Verify's grounding steer (demote-not-exclude): hits of the named
33
+ * document whose edition differs from the active one move to the END
34
+ * of the candidate pool — superseded passages stay servable but stop
35
+ * competing for the window against the current edition. */
36
+ editionSteer?: { doc_number: string; edition: string } | null;
37
+ /** Verify's grounding cut: hits of the named document whose edition
38
+ * differs from the active one are REMOVED — a grounding endpoint
39
+ * judges against the current edition; a superseded passage cannot
40
+ * ground a current-edition claim. Everything else (other documents,
41
+ * undated vocabulary) survives. */
42
+ editionExclude?: { doc_number: string; edition: string } | null;
32
43
  /** Option C: dense-lane results computed concurrently with
33
44
  * understanding (same folded-query vector, retrieve's exact query
34
45
  * parameters). With no filter they REPLACE the primary dense query;