@konneal/engine 0.2.41 → 0.2.43

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,6 +1,6 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-JG5GRJCZ.js";
3
+ } from "./chunk-IEM2XBLP.js";
4
4
  import "./chunk-N2UOENDY.js";
5
5
  import "./chunk-R2V3X6SQ.js";
6
6
  import "./chunk-A3QHHUN5.js";
@@ -0,0 +1,23 @@
1
+ export interface RegisterRow {
2
+ num: string;
3
+ family: string;
4
+ holder: string;
5
+ model: string;
6
+ year: string;
7
+ status: string;
8
+ }
9
+ /** A question is register-shaped when it asks about certification
10
+ * standing of an identifiable model or holder — the deterministic
11
+ * trigger the citation-graph notes already use (query-shaped notes,
12
+ * never a router). */
13
+ export declare function isRegisterShaped(query: string): boolean;
14
+ /** Search tokens: words that can identify a holder or a model — words of
15
+ * 2+ characters that are not register question words, plus bare model
16
+ * numbers ("190", "HM14H1"). Upper-cased tokens match case-insensitively
17
+ * in SQL LIKE. */
18
+ export declare function registerTokens(query: string): string[];
19
+ export declare function searchRegister(db: any, query: string): Promise<{
20
+ rows: RegisterRow[];
21
+ tokens: string[];
22
+ } | null>;
23
+ export declare function registerNote(rows: RegisterRow[]): string;
@@ -1747,6 +1747,96 @@ function semanticCacheKey(indexVersion, gen, signature) {
1747
1747
  return `sc:${indexVersion}:g${gen}:${signature}`;
1748
1748
  }
1749
1749
 
1750
+ // workers/worker_public/src/certificates.ts
1751
+ function isRegisterShaped(query) {
1752
+ return /\bcertif(ied|icates?|ication)s?\b/i.test(query) && /\b(is|are|still|currently|valid|status|suspended|revoked|was|were)\b/i.test(query);
1753
+ }
1754
+ function registerTokens(query) {
1755
+ const stop = /* @__PURE__ */ new Set([
1756
+ "the",
1757
+ "a",
1758
+ "an",
1759
+ "is",
1760
+ "are",
1761
+ "was",
1762
+ "were",
1763
+ "still",
1764
+ "currently",
1765
+ "in",
1766
+ "on",
1767
+ "for",
1768
+ "of",
1769
+ "and",
1770
+ "or",
1771
+ "certificate",
1772
+ "certificates",
1773
+ "certified",
1774
+ "certification",
1775
+ "register",
1776
+ "status",
1777
+ "valid",
1778
+ "suspended",
1779
+ "revoked",
1780
+ "recommendation",
1781
+ "per",
1782
+ "under",
1783
+ "by",
1784
+ "with",
1785
+ "what",
1786
+ "which",
1787
+ "who",
1788
+ "does",
1789
+ "do",
1790
+ "load",
1791
+ "cell",
1792
+ "model",
1793
+ "manufacturer",
1794
+ "holder",
1795
+ "company",
1796
+ "r",
1797
+ "how",
1798
+ "to",
1799
+ "list",
1800
+ "show"
1801
+ ]);
1802
+ const tokens2 = [];
1803
+ const publisherWord = P().publisher.name.toLowerCase();
1804
+ for (const w of query.split(/[^A-Za-z0-9&-]+/)) {
1805
+ if (w.length < 2) continue;
1806
+ if (stop.has(w.toLowerCase())) continue;
1807
+ if (w.toLowerCase() === publisherWord) continue;
1808
+ if (/^\d+$/.test(w) && w.length < 2) continue;
1809
+ if (!tokens2.includes(w)) tokens2.push(w);
1810
+ }
1811
+ return tokens2.slice(0, 6);
1812
+ }
1813
+ async function searchRegister(db, query) {
1814
+ if (!isRegisterShaped(query)) return null;
1815
+ const tokens2 = registerTokens(query);
1816
+ if (!tokens2.length) return null;
1817
+ const clauses = tokens2.map((_, i) => {
1818
+ const n = i + 1;
1819
+ return `(holder LIKE ?${n} OR model LIKE ?${n} OR num LIKE ?${n})`;
1820
+ }).join(" OR ");
1821
+ const params = tokens2.map((t) => `%${t}%`);
1822
+ try {
1823
+ const res = await db.prepare(`SELECT num, family, holder, model, year, status FROM certificates WHERE ${clauses} LIMIT 6`).bind(...params).all();
1824
+ return { rows: res.results ?? [], tokens: tokens2 };
1825
+ } catch {
1826
+ return { rows: [], tokens: tokens2 };
1827
+ }
1828
+ }
1829
+ function registerNote(rows) {
1830
+ if (!rows.length) {
1831
+ return `Certificate register: NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. State plainly that no such certificate is in this register, and that the register is a snapshot rather than the live certification status.`;
1832
+ }
1833
+ const lines = rows.map((r) => `- ${r.num}: holder ${r.holder}, model "${r.model}"${r.year ? `, issued ${r.year}` : ""} \u2014 status ${r.status}`);
1834
+ return [
1835
+ "Certificate register \u2014 the following certificates matched the asked holder or model. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
1836
+ ...lines
1837
+ ].join("\n");
1838
+ }
1839
+
1750
1840
  // workers/worker_public/src/ask.ts
1751
1841
  function userImageDataUrl(body) {
1752
1842
  const img = body?.image;
@@ -2077,6 +2167,8 @@ async function handleAsk(env, ctx, req, tier, key) {
2077
2167
  console.log("understand:", understanding?.intent ?? "null", understanding?.doc_number ? `doc#${understanding.doc_number}${understanding.edition ? "@" + understanding.edition : ""}` : "nodoc", "|", q.query.slice(0, 50));
2078
2168
  const graphDocNumbers = await graphExpand(env, understanding);
2079
2169
  const eNote = await editionNote(env, understanding);
2170
+ const register = await searchRegister(env.DB, q.query);
2171
+ const regNote = register ? registerNote(register.rows) : void 0;
2080
2172
  if (understanding?.intent !== "conversational" && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !userImage && !fresh) {
2081
2173
  const warmVec = await warmEmbed ?? null;
2082
2174
  if (warmVec) {
@@ -2443,7 +2535,7 @@ Answer account questions from these records ONLY: name the record when you use i
2443
2535
  q.lang,
2444
2536
  keptHistory,
2445
2537
  // stage-extracted graph facts (GraphRAG) ride the same note channel
2446
- [processNote, eNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
2538
+ [processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
2447
2539
  summary,
2448
2540
  budget
2449
2541
  );
@@ -9,7 +9,7 @@ import {
9
9
  modelGroundingBlock,
10
10
  scoreJudge,
11
11
  standardForDocNumber
12
- } from "../../chunk-JG5GRJCZ.js";
12
+ } from "../../chunk-IEM2XBLP.js";
13
13
  import {
14
14
  buildMessages,
15
15
  citations,
@@ -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-3LIAXR4S.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-QZDQVB57.js")).handleSearch(env, ctx, inner, tier, key);
1002
+ const res = name === "ask" ? await (await import("../../ask-72UZSXFM.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-QZDQVB57.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.41",
30
+ "version": "0.2.43",
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",
@@ -37,6 +37,7 @@ export type { Env };
37
37
  import { json, err, corsHeaders, readJson, validateQuery, type ApiKey } from "./lib/http";
38
38
  import { clientIp, checkQuota, telemetry } from "./quota";
39
39
  import { graphExpand, editionNote } from "./graph";
40
+ import { registerNote, searchRegister } from "./certificates";
40
41
  import { P } from "./profile.ts";
41
42
 
42
43
  /** User-uploaded image for multimodal questions: a data URL
@@ -577,6 +578,12 @@ async function handleAsk(
577
578
  console.log("understand:", understanding?.intent ?? "null", understanding?.doc_number ? `doc#${understanding.doc_number}${understanding.edition ? "@" + understanding.edition : ""}` : "nodoc", "|", q.query.slice(0, 50));
578
579
  const graphDocNumbers = await graphExpand(env, understanding);
579
580
  const eNote = await editionNote(env, understanding);
581
+ // the certificate register as a database (TODO.new-era/6): a
582
+ // register-shaped question queries the D1 table directly and the rows
583
+ // join the prompt as an authoritative note — exact matches, never a
584
+ // similarity guess about certification standing
585
+ const register = await searchRegister(env.DB, q.query);
586
+ const regNote = register ? registerNote(register.rows) : undefined;
580
587
 
581
588
  // semantic cache: near-duplicate of a recently answered question —
582
589
  // serves the stored answer with a `similar: true` marker (checked only
@@ -1108,7 +1115,7 @@ async function handleAsk(
1108
1115
  q.lang,
1109
1116
  keptHistory,
1110
1117
  // stage-extracted graph facts (GraphRAG) ride the same note channel
1111
- [processNote, eNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
1118
+ [processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
1112
1119
  summary,
1113
1120
  budget,
1114
1121
  );
@@ -0,0 +1,84 @@
1
+ // The certificate register as a STRUCTURED search surface (TODO.new-era/6):
2
+ // "is model X from holder Y still certified?" is an exact-match question,
3
+ // not a similarity question. The register rows live in D1 (the
4
+ // certificates plane's `certificates` table); this stage decides when a
5
+ // question is register-shaped, queries the table, and returns the rows as
6
+ // an authoritative note for the prompt. A miss is a MISS: "not in this
7
+ // register snapshot" is stated verbatim, never softened into a guess.
8
+
9
+ import { P } from './profile.ts';
10
+
11
+ export interface RegisterRow {
12
+ num: string;
13
+ family: string;
14
+ holder: string;
15
+ model: string;
16
+ year: string;
17
+ status: string;
18
+ }
19
+
20
+ /** A question is register-shaped when it asks about certification
21
+ * standing of an identifiable model or holder — the deterministic
22
+ * trigger the citation-graph notes already use (query-shaped notes,
23
+ * never a router). */
24
+ export function isRegisterShaped(query: string): boolean {
25
+ return /\bcertif(ied|icates?|ication)s?\b/i.test(query) && /\b(is|are|still|currently|valid|status|suspended|revoked|was|were)\b/i.test(query);
26
+ }
27
+
28
+ /** Search tokens: words that can identify a holder or a model — words of
29
+ * 2+ characters that are not register question words, plus bare model
30
+ * numbers ("190", "HM14H1"). Upper-cased tokens match case-insensitively
31
+ * in SQL LIKE. */
32
+ export function registerTokens(query: string): string[] {
33
+ const stop = new Set([
34
+ "the", "a", "an", "is", "are", "was", "were", "still", "currently", "in", "on", "for", "of", "and", "or",
35
+ "certificate", "certificates", "certified", "certification", "register", "status", "valid", "suspended",
36
+ "revoked", "recommendation", "per", "under", "by", "with", "what", "which", "who", "does", "do",
37
+ "load", "cell", "model", "manufacturer", "holder", "company", "r", "how", "to", "list", "show",
38
+ ]);
39
+ const tokens: string[] = [];
40
+ const publisherWord = P().publisher.name.toLowerCase();
41
+ for (const w of query.split(/[^A-Za-z0-9&-]+/)) {
42
+ if (w.length < 2) continue;
43
+ if (stop.has(w.toLowerCase())) continue;
44
+ if (w.toLowerCase() === publisherWord) continue;
45
+ if (/^\d+$/.test(w) && w.length < 2) continue;
46
+ if (!tokens.includes(w)) tokens.push(w);
47
+ }
48
+ return tokens.slice(0, 6);
49
+ }
50
+
51
+ export async function searchRegister(db: any, query: string): Promise<{ rows: RegisterRow[]; tokens: string[] } | null> {
52
+ if (!isRegisterShaped(query)) return null;
53
+ const tokens = registerTokens(query);
54
+ if (!tokens.length) return null;
55
+ // each token takes its own numbered placeholder, referenced three
56
+ // times (holder, model, number) — a shared ?1 would silently search
57
+ // the first token only
58
+ const clauses = tokens.map((_, i) => {
59
+ const n = i + 1;
60
+ return `(holder LIKE ?${n} OR model LIKE ?${n} OR num LIKE ?${n})`;
61
+ }).join(" OR ");
62
+ const params = tokens.map((t) => `%${t}%`);
63
+ try {
64
+ const res = await db
65
+ .prepare(`SELECT num, family, holder, model, year, status FROM certificates WHERE ${clauses} LIMIT 6`)
66
+ .bind(...params)
67
+ .all();
68
+ return { rows: (res.results ?? []) as RegisterRow[], tokens };
69
+ } catch {
70
+ // a missing table or a D1 hiccup degrades to no-note honestly
71
+ return { rows: [], tokens };
72
+ }
73
+ }
74
+
75
+ export function registerNote(rows: RegisterRow[]): string {
76
+ if (!rows.length) {
77
+ return `Certificate register: NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. State plainly that no such certificate is in this register, and that the register is a snapshot rather than the live certification status.`;
78
+ }
79
+ const lines = rows.map((r) => `- ${r.num}: holder ${r.holder}, model "${r.model}"${r.year ? `, issued ${r.year}` : ""} — status ${r.status}`);
80
+ return [
81
+ "Certificate register — the following certificates matched the asked holder or model. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
82
+ ...lines,
83
+ ].join("\n");
84
+ }