@konneal/engine 0.2.27 → 0.2.29
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/dist/{ask-Y7L6MDAV.js → ask-UAJHC6U7.js} +2 -2
- package/dist/{chunk-4G5OGWXY.js → chunk-BHWG556Q.js} +15 -3
- package/dist/{chunk-JVN6SQAT.js → chunk-ODHIVW3M.js} +4 -2
- package/dist/{chunk-5LCPKJ6V.js → chunk-RI2SRLZY.js} +1 -1
- package/dist/faithfulness-context.d.ts +4 -1
- package/dist/faithfulness.d.ts +4 -1
- package/dist/{search-4UK3RBEQ.js → search-ICNVGXUY.js} +2 -2
- package/dist/worker_public/src/index.js +30 -7
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +1 -1
- package/workers/worker_public/src/faithfulness-context.ts +11 -2
- package/workers/worker_public/src/faithfulness.ts +1 -1
- package/workers/worker_public/src/index.ts +26 -3
- package/workers/worker_public/src/stages/seal.ts +14 -2
- package/workers/worker_public/src/stages/types.ts +5 -0
|
@@ -22747,12 +22747,24 @@ 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,
|
|
22751
22751
|
run: (c) => {
|
|
22752
22752
|
const before = c.hits.length;
|
|
22753
22753
|
const scope = c.opts.sealScope;
|
|
22754
|
-
|
|
22755
|
-
|
|
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 steer = c.opts.editionSteer;
|
|
22760
|
+
const current = [];
|
|
22761
|
+
const superseded = [];
|
|
22762
|
+
for (const h of c.hits) {
|
|
22763
|
+
const same = h.metadata.doc_number === steer.doc_number;
|
|
22764
|
+
(same && h.metadata.edition !== steer.edition ? superseded : current).push(h);
|
|
22765
|
+
}
|
|
22766
|
+
c.hits = [...current, ...superseded];
|
|
22767
|
+
console.log("edition steer:", superseded.length, "superseded candidates demoted behind", current.length, "current-edition candidates of", `doc#${steer.doc_number}@${steer.edition}`);
|
|
22756
22768
|
}
|
|
22757
22769
|
};
|
|
22758
22770
|
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
syntheticUnderstanding,
|
|
29
29
|
telemetry,
|
|
30
30
|
understandQuery
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-BHWG556Q.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;
|
|
@@ -2367,6 +2367,8 @@ export {
|
|
|
2367
2367
|
handleMemories,
|
|
2368
2368
|
checkQuoteAnchors,
|
|
2369
2369
|
standardForDocNumber,
|
|
2370
|
+
bindModelNode,
|
|
2371
|
+
modelGroundingBlock,
|
|
2370
2372
|
scoreJudge,
|
|
2371
2373
|
handleAsk
|
|
2372
2374
|
};
|
package/dist/faithfulness.d.ts
CHANGED
|
@@ -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
|
|
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,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleSearch
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-RI2SRLZY.js";
|
|
4
4
|
import {
|
|
5
|
+
bindModelNode,
|
|
5
6
|
checkQuoteAnchors,
|
|
6
7
|
handleAsk,
|
|
7
8
|
handleMemories,
|
|
9
|
+
modelGroundingBlock,
|
|
8
10
|
scoreJudge,
|
|
9
11
|
standardForDocNumber
|
|
10
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-ODHIVW3M.js";
|
|
11
13
|
import {
|
|
12
14
|
buildMessages,
|
|
13
15
|
citations,
|
|
@@ -25,11 +27,12 @@ import {
|
|
|
25
27
|
parseAppliedContext,
|
|
26
28
|
portModelRunner,
|
|
27
29
|
promptVars,
|
|
30
|
+
refCodec,
|
|
28
31
|
retrieve,
|
|
29
32
|
sessionFrom,
|
|
30
33
|
telemetry,
|
|
31
34
|
understandQuery
|
|
32
|
-
} from "../../chunk-
|
|
35
|
+
} from "../../chunk-BHWG556Q.js";
|
|
33
36
|
import {
|
|
34
37
|
authenticate,
|
|
35
38
|
corsHeaders,
|
|
@@ -390,7 +393,11 @@ function parseVerdict(text) {
|
|
|
390
393
|
|
|
391
394
|
// workers/worker_public/src/faithfulness-context.ts
|
|
392
395
|
function buildJudgeContext(passages, machine = []) {
|
|
393
|
-
const context = passages.slice(0, 8).map((p, i) =>
|
|
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");
|
|
394
401
|
const machineContext = machine.length ? "\n" + machine.slice(0, 6).map((m) => `[M] ${m.replace(/\s+/g, " ").slice(0, 400)}`).join("\n") : "";
|
|
395
402
|
return context + machineContext;
|
|
396
403
|
}
|
|
@@ -989,7 +996,7 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
989
996
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
990
997
|
body: JSON.stringify({ ...args, stream: false })
|
|
991
998
|
});
|
|
992
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
999
|
+
const res = name === "ask" ? await (await import("../../ask-UAJHC6U7.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-ICNVGXUY.js")).handleSearch(env, ctx, inner, tier, key);
|
|
993
1000
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
994
1001
|
});
|
|
995
1002
|
if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
|
|
@@ -1448,7 +1455,22 @@ async function verifyRoute(c) {
|
|
|
1448
1455
|
if (!answer || !query) return err(400, "invalid_input", "answer and query are required");
|
|
1449
1456
|
try {
|
|
1450
1457
|
const u = await understandQuery(env.AI, roleModel(env, "understand"), query, [], []);
|
|
1451
|
-
|
|
1458
|
+
let lexicalBoost;
|
|
1459
|
+
let editionSteer = null;
|
|
1460
|
+
try {
|
|
1461
|
+
const fam = u?.doc_number ? refCodec().familyOf(u.doc_number) : null;
|
|
1462
|
+
if (fam) {
|
|
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);
|
|
1464
|
+
if (row?.edition) {
|
|
1465
|
+
lexicalBoost = String(row.edition);
|
|
1466
|
+
editionSteer = { doc_number: fam.split("-").pop() ?? "", edition: String(row.edition) };
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
} catch {
|
|
1470
|
+
}
|
|
1471
|
+
const bound = await bindModelNode(env, { query, standardKeys: entitlementScope(standardKeysFrom(body)) });
|
|
1472
|
+
const modelGrounding = bound && !bound.gated ? modelGroundingBlock(bound) : null;
|
|
1473
|
+
const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionSteer });
|
|
1452
1474
|
const passages = retrieved.hits.map((h) => h.text);
|
|
1453
1475
|
const anchors = checkQuoteAnchors(answer, passages);
|
|
1454
1476
|
const refs = [...answer.matchAll(/\[\[u:([^\]]+)\]\]/g)].map((m) => m[1]);
|
|
@@ -1459,7 +1481,8 @@ async function verifyRoute(c) {
|
|
|
1459
1481
|
{ name: "citations_present", deterministic: true, pass: new RegExp(`\\[[^\\]]*(${P().publisher.name})[^\\]]*\\]`).test(answer), detail: "normative claims should carry a passage citation" }
|
|
1460
1482
|
];
|
|
1461
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);
|
|
1462
|
-
|
|
1484
|
+
if (modelGrounding) machine.unshift(modelGrounding);
|
|
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);
|
|
1463
1486
|
return json({
|
|
1464
1487
|
checks,
|
|
1465
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.
|
|
30
|
+
"version": "0.2.29",
|
|
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(
|
|
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) =>
|
|
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;
|
|
@@ -13,7 +13,8 @@ import { ftsMatchQuery } from "./lexical";
|
|
|
13
13
|
import { scoreFaithfulness } from "./faithfulness";
|
|
14
14
|
import { checkQuoteAnchors } from "./anchors";
|
|
15
15
|
import { namedDocumentIn } from "./context";
|
|
16
|
-
import { standardForDocNumber } from "./modelplane";
|
|
16
|
+
import { standardForDocNumber, bindModelNode, modelGroundingBlock } from "./modelplane";
|
|
17
|
+
import { refCodec } from "./codecs";
|
|
17
18
|
import { entitlementScope, standardKeysFrom } from "./requestScope";
|
|
18
19
|
import type { Env } from "./env";
|
|
19
20
|
export type { Env };
|
|
@@ -228,7 +229,28 @@ async function verifyRoute(c: RouteContext): Promise<Response> {
|
|
|
228
229
|
if (!answer || !query) return err(400, "invalid_input", "answer and query are required");
|
|
229
230
|
try {
|
|
230
231
|
const u = await understandQuery(env.AI, roleModel(env, "understand"), query, [], []);
|
|
231
|
-
|
|
232
|
+
// the grounding must MIRROR the ask path's: the edition boost (the
|
|
233
|
+
// publication's active edition, the codec's family) and the model
|
|
234
|
+
// grounding — a verify that grounds against different passages than
|
|
235
|
+
// the answer used judges a different answer
|
|
236
|
+
let lexicalBoost: string | undefined;
|
|
237
|
+
let editionSteer: { doc_number: string; edition: string } | null = null;
|
|
238
|
+
try {
|
|
239
|
+
const fam = u?.doc_number ? refCodec().familyOf(u.doc_number) : null;
|
|
240
|
+
if (fam) {
|
|
241
|
+
const row = await env.DB.prepare("SELECT edition FROM documents WHERE family = ?1 AND active = 1 ORDER BY edition DESC LIMIT 1")
|
|
242
|
+
.bind(fam).first().catch(() => null);
|
|
243
|
+
if (row?.edition) {
|
|
244
|
+
lexicalBoost = String(row.edition);
|
|
245
|
+
editionSteer = { doc_number: fam.split("-").pop() ?? "", edition: String(row.edition) };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} catch {
|
|
249
|
+
// degrade to unsteered retrieval
|
|
250
|
+
}
|
|
251
|
+
const bound = await bindModelNode(env, { query, standardKeys: entitlementScope(standardKeysFrom(body)) });
|
|
252
|
+
const modelGrounding = bound && !bound.gated ? modelGroundingBlock(bound) : null;
|
|
253
|
+
const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionSteer });
|
|
232
254
|
const passages = retrieved.hits.map((h: Hit) => h.text);
|
|
233
255
|
const anchors = checkQuoteAnchors(answer, passages);
|
|
234
256
|
const refs = [...answer.matchAll(/\[\[u:([^\]]+)\]\]/g)].map((m) => m[1]);
|
|
@@ -246,7 +268,8 @@ async function verifyRoute(c: RouteContext): Promise<Response> {
|
|
|
246
268
|
.map((b: any) => [b.payload.check, b.payload.meaning, b.payload.definition, b.payload.violation_meaning]
|
|
247
269
|
.filter((x: unknown) => typeof x === "string" && x).join(" — "))
|
|
248
270
|
.filter(Boolean);
|
|
249
|
-
|
|
271
|
+
if (modelGrounding) machine.unshift(modelGrounding);
|
|
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);
|
|
250
273
|
return json({
|
|
251
274
|
checks,
|
|
252
275
|
judged: faith ? { name: "faithfulness", deterministic: false, score: faith.score, ungrounded_claims: faith.ungrounded_claims.slice(0, 5) } : null,
|
|
@@ -6,11 +6,23 @@ 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,
|
|
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
|
-
|
|
15
|
+
console.log("context seal:", before, "→", c.hits.length, "candidates within", `doc#${scope.doc_number}${scope.edition ? "@" + scope.edition : ""}`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const steer = c.opts.editionSteer!;
|
|
19
|
+
const current: typeof c.hits = [];
|
|
20
|
+
const superseded: typeof c.hits = [];
|
|
21
|
+
for (const h of c.hits) {
|
|
22
|
+
const same = h.metadata.doc_number === steer.doc_number;
|
|
23
|
+
(same && h.metadata.edition !== steer.edition ? superseded : current).push(h);
|
|
24
|
+
}
|
|
25
|
+
c.hits = [...current, ...superseded];
|
|
26
|
+
console.log("edition steer:", superseded.length, "superseded candidates demoted behind", current.length, "current-edition candidates of", `doc#${steer.doc_number}@${steer.edition}`);
|
|
15
27
|
},
|
|
16
28
|
};
|
|
@@ -29,6 +29,11 @@ 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;
|
|
32
37
|
/** Option C: dense-lane results computed concurrently with
|
|
33
38
|
* understanding (same folded-query vector, retrieve's exact query
|
|
34
39
|
* parameters). With no filter they REPLACE the primary dense query;
|