@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.
- package/dist/{ask-3LIAXR4S.js → ask-72UZSXFM.js} +1 -1
- package/dist/certificates.d.ts +23 -0
- package/dist/{chunk-JG5GRJCZ.js → chunk-IEM2XBLP.js} +93 -1
- package/dist/worker_public/src/index.js +2 -2
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +8 -1
- package/workers/worker_public/src/certificates.ts +84 -0
|
@@ -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-
|
|
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-
|
|
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.
|
|
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
|
+
}
|