@konneal/engine 0.2.37 → 0.2.38

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-SEOQWW77.js";
4
- import "./chunk-37FSCXBQ.js";
3
+ } from "./chunk-CNFQNCWA.js";
4
+ import "./chunk-6GAYBMUM.js";
5
5
  import "./chunk-R2V3X6SQ.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-4GJGBGJK.js";
@@ -61,6 +61,27 @@ async function generateOnce(env, model, messages, effort) {
61
61
  return null;
62
62
  }
63
63
 
64
+ // workers/worker_public/src/quality.ts
65
+ var ORDER = { verified: 0, curated: 1, ocr: 2 };
66
+ function hitQuality(m) {
67
+ if (m.model_node || m.model_version || m.producer === "primmel") return "verified";
68
+ if (m.tier === "curated") return "curated";
69
+ return "ocr";
70
+ }
71
+ function answerQuality(qualities) {
72
+ let worst = null;
73
+ for (const q of qualities) {
74
+ if (!q) continue;
75
+ if (!worst || ORDER[q] > ORDER[worst]) worst = q;
76
+ }
77
+ return worst;
78
+ }
79
+ function qualityNote(q) {
80
+ if (q === "verified") return "Grounded in verified sources: this publication's requirements ride a machine-checkable data model.";
81
+ if (q === "curated") return "Grounded in the edited corpus: Metanorma-authored documents, chunked at clause boundaries.";
82
+ return "Partly grounded in experimental OCR text: verify quotations and table values against the official publication.";
83
+ }
84
+
64
85
  // workers/worker_public/src/lexical.ts
65
86
  var LEXICAL_K = 40;
66
87
  function ftsMatchQuery(query) {
@@ -23515,6 +23536,7 @@ function citations(hits) {
23515
23536
  status: h.metadata.status ?? "unknown",
23516
23537
  superseded_by: h.metadata.superseded_by || void 0,
23517
23538
  corpus: h.metadata.corpus || P().publisher.id,
23539
+ quality: hitQuality(h.metadata),
23518
23540
  url: publicationUrl(h.metadata),
23519
23541
  snippet: h.text.slice(0, 400),
23520
23542
  score: h.rerank_score ?? h.score
@@ -24560,6 +24582,8 @@ async function editionNote(env, u) {
24560
24582
  export {
24561
24583
  embed,
24562
24584
  generateOnce,
24585
+ answerQuality,
24586
+ qualityNote,
24563
24587
  ftsMatchQuery,
24564
24588
  refCodec,
24565
24589
  NO_CONTEXT,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  NO_CONTEXT,
3
+ answerQuality,
3
4
  appliedContext,
4
5
  buildMessages,
5
6
  checkQuota,
@@ -21,6 +22,7 @@ import {
21
22
  parseContext,
22
23
  portModelRunner,
23
24
  portStore,
25
+ qualityNote,
24
26
  rawSessionToken,
25
27
  refCodec,
26
28
  resolveDocScope,
@@ -32,7 +34,7 @@ import {
32
34
  syntheticUnderstanding,
33
35
  telemetry,
34
36
  understandQuery
35
- } from "./chunk-37FSCXBQ.js";
37
+ } from "./chunk-6GAYBMUM.js";
36
38
  import {
37
39
  corsHeaders,
38
40
  err,
@@ -497,6 +499,7 @@ function modelCitation(node) {
497
499
  clause_title: `${node.kind.replace(/_/g, " ")} \u2014 ${node.name} (${node.node_id})`,
498
500
  status: "in-force",
499
501
  corpus: "smart-model",
502
+ quality: "verified",
500
503
  url: void 0,
501
504
  snippet: `${node.node_id}${node.clause ? ` \xB7 ${node.clause.urn}` : ""}${node.content?.statement ? ` \u2014 ${clip(node.content.statement, 240)}` : ""}`,
502
505
  score: 1
@@ -2471,7 +2474,8 @@ Answer account questions from these records ONLY: name the record when you use i
2471
2474
 
2472
2475
  `));
2473
2476
  send({ type: "read", read: readAs() });
2474
- send({ type: "citations", citations: cites, context_applied: ctxApplied, ...liveRecords ? { records: liveRecords } : {}, quota });
2477
+ const sourceQuality = answerQuality(cites.map((c) => c.quality));
2478
+ send({ type: "citations", citations: cites, context_applied: ctxApplied, ...sourceQuality ? { source_quality: sourceQuality, quality_note: qualityNote(sourceQuality) } : {}, ...liveRecords ? { records: liveRecords } : {}, quota });
2475
2479
  let full = "";
2476
2480
  try {
2477
2481
  for await (const tok of sseTokens(stream)) {
@@ -2604,7 +2608,8 @@ Answer account questions from these records ONLY: name the record when you use i
2604
2608
  if (completionBlocks.length) console.log("contract completion:", completionBlocks.length, "table block(s) attached server-side");
2605
2609
  }
2606
2610
  completionBlocks.push(...await completeFigures(env.DB, answer, [...c2ns.blocks, ...completionBlocks], used));
2607
- const out = { answer, citations: finalCites, model: MODELS.member, query_hash: queryHash, follow_ups: understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...verdictBlock ? [verdictBlock] : [], ...conditionBlock ? [conditionBlock] : [], ...aggregationBlock ? [aggregationBlock] : [], ...completionBlocks], context_applied: ctxApplied, ...liveRecords ? { records: liveRecords } : {} };
2611
+ const jsonQuality = answerQuality(finalCites.map((c) => c.quality));
2612
+ const out = { answer, citations: finalCites, ...jsonQuality ? { source_quality: jsonQuality, quality_note: qualityNote(jsonQuality) } : {}, model: MODELS.member, query_hash: queryHash, follow_ups: understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...verdictBlock ? [verdictBlock] : [], ...conditionBlock ? [conditionBlock] : [], ...aggregationBlock ? [aggregationBlock] : [], ...completionBlocks], context_applied: ctxApplied, ...liveRecords ? { records: liveRecords } : {} };
2608
2613
  const cacheable = !contextual && !declaredCtx && !answer.includes(refusalAnswer()) && finalAnchors.violations.length === 0;
2609
2614
  if (cacheable) {
2610
2615
  const warmVec = await warmEmbed ?? null;
@@ -7,7 +7,7 @@ import {
7
7
  sessionFrom,
8
8
  telemetry,
9
9
  understandQuery
10
- } from "./chunk-37FSCXBQ.js";
10
+ } from "./chunk-6GAYBMUM.js";
11
11
  import {
12
12
  corsHeaders,
13
13
  err,
@@ -87,6 +87,7 @@ export declare function modelCitation(node: BoundModelNode): {
87
87
  clause_title: string;
88
88
  status: string;
89
89
  corpus: string;
90
+ quality: string;
90
91
  url: undefined;
91
92
  snippet: string;
92
93
  score: number;
@@ -61,6 +61,7 @@ export declare function citations(hits: Hit[]): {
61
61
  status: string;
62
62
  superseded_by: string | undefined;
63
63
  corpus: any;
64
+ quality: import("./quality").SourceQuality;
64
65
  url: string | undefined;
65
66
  snippet: string;
66
67
  score: number;
@@ -0,0 +1,6 @@
1
+ import type { ChunkMeta } from "../../shared/chunk.ts";
2
+ export type SourceQuality = "verified" | "curated" | "ocr";
3
+ export declare function hitQuality(m: ChunkMeta): SourceQuality;
4
+ export declare function answerQuality(qualities: (SourceQuality | undefined)[]): SourceQuality | null;
5
+ /** The one-line posture the interface renders under the answer. */
6
+ export declare function qualityNote(q: SourceQuality): string;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handleSearch
3
- } from "./chunk-YEAAGFHB.js";
4
- import "./chunk-37FSCXBQ.js";
3
+ } from "./chunk-GDQOFP6B.js";
4
+ import "./chunk-6GAYBMUM.js";
5
5
  import "./chunk-R2V3X6SQ.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-4GJGBGJK.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleSearch
3
- } from "../../chunk-YEAAGFHB.js";
3
+ } from "../../chunk-GDQOFP6B.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-SEOQWW77.js";
12
+ } from "../../chunk-CNFQNCWA.js";
13
13
  import {
14
14
  buildMessages,
15
15
  citations,
@@ -35,7 +35,7 @@ import {
35
35
  sessionFrom,
36
36
  telemetry,
37
37
  understandQuery
38
- } from "../../chunk-37FSCXBQ.js";
38
+ } from "../../chunk-6GAYBMUM.js";
39
39
  import {
40
40
  authenticate,
41
41
  corsHeaders,
@@ -999,7 +999,7 @@ async function handleMcp(env, ctx, req, tier, key) {
999
999
  // stream:false forces the JSON lane (anon defaults to SSE)
1000
1000
  body: JSON.stringify({ ...args, stream: false })
1001
1001
  });
1002
- const res = name === "ask" ? await (await import("../../ask-J6NGXPOD.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-V6M4KASL.js")).handleSearch(env, ctx, inner, tier, key);
1002
+ const res = name === "ask" ? await (await import("../../ask-VXV2BFNC.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-HBE47CK6.js")).handleSearch(env, ctx, inner, tier, key);
1003
1003
  return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
1004
1004
  });
1005
1005
  if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
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.37",
30
+ "version": "0.2.38",
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",
@@ -25,6 +25,7 @@ import { evaluate as machineEvaluate, verdictNote } from "./verdict";
25
25
  import { evaluateConditionSets, quantitiesIn, type ConditionVerdict } from "./conditions";
26
26
  import { evaluateAggregation, type AggregationVerdict } from "./aggregation";
27
27
  import { matchLicensedTopic, boundaryNoteText } from "./boundary";
28
+ import { answerQuality, qualityNote } from "./quality";
28
29
  import { detectDraftIntent, prepareDraft } from "./drafts";
29
30
  import { detectApiCallIntent, prepareApiCall } from "./apicalls";
30
31
  import { memoryNote } from "./memories";
@@ -1145,7 +1146,8 @@ async function handleAsk(
1145
1146
  // the reading arrives first: the interpretation that steered
1146
1147
  // retrieval, before a single token of the answer
1147
1148
  send({ type: "read", read: readAs() });
1148
- send({ type: "citations", citations: cites, context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}), quota, });
1149
+ const sourceQuality = answerQuality(cites.map((c: any) => c.quality));
1150
+ send({ type: "citations", citations: cites, context_applied: ctxApplied, ...(sourceQuality ? { source_quality: sourceQuality, quality_note: qualityNote(sourceQuality) } : {}), ...(liveRecords ? { records: liveRecords } : {}), quota, });
1149
1151
  let full = "";
1150
1152
  try {
1151
1153
  for await (const tok of sseTokens(stream)) {
@@ -1339,7 +1341,8 @@ async function handleAsk(
1339
1341
  // figure completion (#172) — see ./completion for the rationale
1340
1342
  completionBlocks.push(...(await completeFigures(env.DB, answer, [...c2ns.blocks, ...completionBlocks], used)));
1341
1343
 
1342
- const out = { answer, citations: finalCites, model: MODELS.member, query_hash: queryHash, follow_ups: understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...(verdictBlock ? [verdictBlock] : []), ...(conditionBlock ? [conditionBlock] : []), ...(aggregationBlock ? [aggregationBlock] : []), ...completionBlocks], context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}) };
1344
+ const jsonQuality = answerQuality(finalCites.map((c: any) => c.quality));
1345
+ const out = { answer, citations: finalCites, ...(jsonQuality ? { source_quality: jsonQuality, quality_note: qualityNote(jsonQuality) } : {}), model: MODELS.member, query_hash: queryHash, follow_ups: understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...(verdictBlock ? [verdictBlock] : []), ...(conditionBlock ? [conditionBlock] : []), ...(aggregationBlock ? [aggregationBlock] : []), ...completionBlocks], context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}) };
1343
1346
  const cacheable = !contextual && !declaredCtx && !answer.includes(refusalAnswer()) && finalAnchors.violations.length === 0;
1344
1347
  if (cacheable) {
1345
1348
  const warmVec = (await warmEmbed) ?? null;
@@ -287,6 +287,7 @@ export function modelCitation(node: BoundModelNode) {
287
287
  clause_title: `${node.kind.replace(/_/g, " ")} — ${node.name} (${node.node_id})`,
288
288
  status: "in-force",
289
289
  corpus: "smart-model",
290
+ quality: "verified",
290
291
  url: undefined,
291
292
  snippet: `${node.node_id}${node.clause ? ` · ${node.clause.urn}` : ""}${node.content?.statement ? ` — ${clip(node.content.statement, 240)}` : ""}`,
292
293
  score: 1,
@@ -33,6 +33,7 @@ export function fill(template: string, vars: Record<string, string>): string {
33
33
  return template.replace(/\{\{(\w+)\}\}/g, (_m, k: string) => (k in vars ? vars[k] : ""));
34
34
  }
35
35
  import { QueryFilters, toVectorizeFilter, standardKeyAllowed } from "./selfquery";
36
+ import { hitQuality } from "./quality";
36
37
  import { lexicalPrefilter } from "./lexical";
37
38
  import { positionOrder } from "./structural";
38
39
  import { STAGES, runStages } from "./stages";
@@ -404,6 +405,7 @@ export function citations(hits: Hit[]) {
404
405
  status: h.metadata.status ?? "unknown",
405
406
  superseded_by: h.metadata.superseded_by || undefined,
406
407
  corpus: h.metadata.corpus || P().publisher.id,
408
+ quality: hitQuality(h.metadata),
407
409
  url: publicationUrl(h.metadata),
408
410
  snippet: h.text.slice(0, 400),
409
411
  score: h.rerank_score ?? h.score,
@@ -0,0 +1,48 @@
1
+ // The answer-quality ladder (TODO.rag/13): every citation states what
2
+ // kind of text grounds it, so the answer states how far to trust it.
3
+ // Three rungs, derived from provenance the chunk wire already carries —
4
+ // no new ingest state:
5
+ //
6
+ // verified — the model plane: the chunk rides a primmel data model
7
+ // (model_node/model_version), the Recommendation's semantics are
8
+ // machine-checkable and the golden set measures them (R 60 is the
9
+ // gauge). The answer can be WRONG, but it is wrong against a
10
+ // contract we can see.
11
+ // curated — the edited corpus: Metanorma-authored documents
12
+ // (mn-samples-oiml and the editors' own), clause-boundary chunks,
13
+ // enrichment applied.
14
+ // ocr — the experimental OCR corpus: the broad coverage lane,
15
+ // text recovered from scans; quote anchors may wobble, tables can
16
+ // be mangled. Useful, and marked as experimental on purpose.
17
+ //
18
+ // The answer's tier is the WORST rung its citations stand on: one OCR
19
+ // citation makes the whole answer experimental, because the reader
20
+ // cannot tell which sentence leaned on which chunk.
21
+
22
+ import type { ChunkMeta } from "../../shared/chunk.ts";
23
+
24
+ export type SourceQuality = "verified" | "curated" | "ocr";
25
+
26
+ const ORDER: Record<SourceQuality, number> = { verified: 0, curated: 1, ocr: 2 };
27
+
28
+ export function hitQuality(m: ChunkMeta): SourceQuality {
29
+ if (m.model_node || m.model_version || m.producer === "primmel") return "verified";
30
+ if (m.tier === "curated") return "curated";
31
+ return "ocr";
32
+ }
33
+
34
+ export function answerQuality(qualities: (SourceQuality | undefined)[]): SourceQuality | null {
35
+ let worst: SourceQuality | null = null;
36
+ for (const q of qualities) {
37
+ if (!q) continue;
38
+ if (!worst || ORDER[q] > ORDER[worst]) worst = q;
39
+ }
40
+ return worst;
41
+ }
42
+
43
+ /** The one-line posture the interface renders under the answer. */
44
+ export function qualityNote(q: SourceQuality): string {
45
+ if (q === "verified") return "Grounded in verified sources: this publication's requirements ride a machine-checkable data model.";
46
+ if (q === "curated") return "Grounded in the edited corpus: Metanorma-authored documents, chunked at clause boundaries.";
47
+ return "Partly grounded in experimental OCR text: verify quotations and table values against the official publication.";
48
+ }