@konneal/engine 0.2.176 → 0.2.178
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-ZWZFZBKZ.js → ask-64HJTVPS.js} +3 -3
- package/dist/{certificates-F5G7GRWK.js → certificates-V6KO2L6P.js} +5 -1
- package/dist/certificates.d.ts +17 -0
- package/dist/{chunk-ECOYO7EX.js → chunk-IRCDOFP4.js} +28 -5
- package/dist/{chunk-B2MU4ZWM.js → chunk-ROXOL4GF.js} +1 -1
- package/dist/{chunk-T6T2QUO6.js → chunk-V2QW6EZB.js} +7 -5
- package/dist/{tools-VHN54DWH.js → tools-UCUNOJR4.js} +1 -1
- package/dist/worker_public/src/index.js +5 -5
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +5 -4
- package/workers/worker_public/src/certificates.ts +51 -5
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleAsk
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-V2QW6EZB.js";
|
|
4
|
+
import "./chunk-IRCDOFP4.js";
|
|
5
|
+
import "./chunk-ROXOL4GF.js";
|
|
6
6
|
import "./chunk-CLDMP5YH.js";
|
|
7
7
|
import "./chunk-ZHT4456M.js";
|
|
8
8
|
import "./chunk-D7MO4RVQ.js";
|
|
@@ -6,12 +6,14 @@ import {
|
|
|
6
6
|
isRegisterShaped,
|
|
7
7
|
printedCertificateNumber,
|
|
8
8
|
queryFamily,
|
|
9
|
+
rankRegisterRows,
|
|
9
10
|
registerAggregate,
|
|
10
11
|
registerAggregateIntent,
|
|
12
|
+
registerBypassesCache,
|
|
11
13
|
registerNote,
|
|
12
14
|
registerTokens,
|
|
13
15
|
searchRegister
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-IRCDOFP4.js";
|
|
15
17
|
import "./chunk-3FYJM7LH.js";
|
|
16
18
|
export {
|
|
17
19
|
aggregateBlock,
|
|
@@ -21,8 +23,10 @@ export {
|
|
|
21
23
|
isRegisterShaped,
|
|
22
24
|
printedCertificateNumber,
|
|
23
25
|
queryFamily,
|
|
26
|
+
rankRegisterRows,
|
|
24
27
|
registerAggregate,
|
|
25
28
|
registerAggregateIntent,
|
|
29
|
+
registerBypassesCache,
|
|
26
30
|
registerNote,
|
|
27
31
|
registerTokens,
|
|
28
32
|
searchRegister
|
package/dist/certificates.d.ts
CHANGED
|
@@ -20,6 +20,14 @@ export declare function isRegisterShaped(query: string): boolean;
|
|
|
20
20
|
* narrates it (TODO.sota/04's machine-checkable ground truth, applied
|
|
21
21
|
* to the register plane 2026-10-11). */
|
|
22
22
|
export declare function registerAggregateIntent(query: string): "count" | "latest" | null;
|
|
23
|
+
/** The register's answers derive from the D1 snapshot, not the indexed
|
|
24
|
+
* corpus — but the answer cache (namespaced by INDEX_VERSION +
|
|
25
|
+
* corpus_gen) knows nothing about D1-plane loads, so a cached
|
|
26
|
+
* pre-register refusal preempted register-shaped questions the register
|
|
27
|
+
* answers (measured live 2026-10-28: cached refusals served over
|
|
28
|
+
* register-shaped asks until fresh). Such asks skip both cache reads
|
|
29
|
+
* and writes. */
|
|
30
|
+
export declare function registerBypassesCache(query: string): boolean;
|
|
23
31
|
export interface RegisterAggregate {
|
|
24
32
|
family: string;
|
|
25
33
|
count: number;
|
|
@@ -84,6 +92,15 @@ export declare function searchRegister(db: any, query: string, force?: boolean):
|
|
|
84
92
|
rows: RegisterRow[];
|
|
85
93
|
tokens: string[];
|
|
86
94
|
} | null>;
|
|
95
|
+
/** Rank the OR-matched candidates so the note's six rows carry the
|
|
96
|
+
* QUESTION's model, not just its holder: the live probes showed a
|
|
97
|
+
* distinctive model token losing its place to generic holder words
|
|
98
|
+
* ("Rice Lake Weighing Systems" matched 6 rows before "RL116XP" was
|
|
99
|
+
* ever consulted — the answer then denied a certificate the register
|
|
100
|
+
* holds). A model-token hit outranks a holder-token hit; numbers must
|
|
101
|
+
* match as standalone values (the "190 must not catch XK3190" rule).
|
|
102
|
+
* Measured 2026-10-28, the register-lane eval. */
|
|
103
|
+
export declare function rankRegisterRows(rows: RegisterRow[], tokens: string[]): RegisterRow[];
|
|
87
104
|
export declare function registerNote(rows: RegisterRow[]): string;
|
|
88
105
|
/** Each certificate's location rides the note as a link when its PDF
|
|
89
106
|
* is in the R2 plane (TODO 7) — the user gets the document, not just
|
|
@@ -12,6 +12,9 @@ function registerAggregateIntent(query) {
|
|
|
12
12
|
if (/\b(how many|count|number of|total|how much)\b/i.test(query)) return "count";
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
|
+
function registerBypassesCache(query) {
|
|
16
|
+
return isRegisterShaped(query) || registerAggregateIntent(query) !== null;
|
|
17
|
+
}
|
|
15
18
|
async function registerAggregate(db, family) {
|
|
16
19
|
try {
|
|
17
20
|
const digits = family.replace(/^[A-Za-z]+/, "");
|
|
@@ -139,7 +142,7 @@ function registerTokens(query) {
|
|
|
139
142
|
if (/^\d+$/.test(w) && w.length < 2) continue;
|
|
140
143
|
if (!tokens.includes(w)) tokens.push(w);
|
|
141
144
|
}
|
|
142
|
-
return tokens.slice(0,
|
|
145
|
+
return tokens.slice(0, 12);
|
|
143
146
|
}
|
|
144
147
|
function buildRegisterQuery(tokens) {
|
|
145
148
|
const params = [];
|
|
@@ -156,7 +159,7 @@ function buildRegisterQuery(tokens) {
|
|
|
156
159
|
return `(holder LIKE ?${n} OR model LIKE ?${n} OR num LIKE ?${n})`;
|
|
157
160
|
});
|
|
158
161
|
return {
|
|
159
|
-
sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT
|
|
162
|
+
sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT 40`,
|
|
160
163
|
params
|
|
161
164
|
};
|
|
162
165
|
}
|
|
@@ -190,18 +193,36 @@ async function searchRegister(db, query, force = false) {
|
|
|
190
193
|
}
|
|
191
194
|
try {
|
|
192
195
|
const res = await db.prepare(sql).bind(...params).all();
|
|
193
|
-
|
|
196
|
+
const rows = rankRegisterRows(res.results ?? [], tokens);
|
|
197
|
+
return { rows, tokens };
|
|
194
198
|
} catch {
|
|
195
199
|
return { rows: [], tokens };
|
|
196
200
|
}
|
|
197
201
|
}
|
|
202
|
+
function rankRegisterRows(rows, tokens) {
|
|
203
|
+
const score = (row) => {
|
|
204
|
+
const model = (row.model || "").toLowerCase();
|
|
205
|
+
const holder = (row.holder || "").toLowerCase();
|
|
206
|
+
let s = 0;
|
|
207
|
+
for (const t of tokens) {
|
|
208
|
+
const tl = t.toLowerCase();
|
|
209
|
+
if (/^\d+$/.test(t)) {
|
|
210
|
+
if (new RegExp(`(^|[^a-z0-9])${tl}([^a-z0-9]|$)`).test(model)) s += 3;
|
|
211
|
+
else if (holder.includes(tl)) s += 1;
|
|
212
|
+
} else if (model.includes(tl)) s += 2;
|
|
213
|
+
else if (holder.includes(tl)) s += 1;
|
|
214
|
+
}
|
|
215
|
+
return s;
|
|
216
|
+
};
|
|
217
|
+
return [...rows].sort((a, b) => score(b) - score(a) || Number(b.year) - Number(a.year)).slice(0, 6);
|
|
218
|
+
}
|
|
198
219
|
function registerNote(rows) {
|
|
199
220
|
if (!rows.length) {
|
|
200
|
-
return `Certificate register
|
|
221
|
+
return `Certificate register \u2014 THIS NOTE ANSWERS THE QUESTION. NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. Answer from THIS note: state plainly that no such certificate is in this register \u2014 never fall back to the indexed publications' absence phrasing \u2014 and say the register is a snapshot rather than the live certification status.`;
|
|
201
222
|
}
|
|
202
223
|
const lines = rows.map((r) => `- ${r.num}: holder ${r.holder}, model "${r.model}"${r.year ? `, issued ${r.year}` : ""} \u2014 status ${r.status}`);
|
|
203
224
|
return [
|
|
204
|
-
"Certificate register \u2014
|
|
225
|
+
"Certificate register \u2014 THIS NOTE ANSWERS THE QUESTION. The following certificates matched the asked holder or model; answer from THIS note. A model the note does not list is not among the matched rows \u2014 say so plainly instead of falling back to the indexed publications. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
|
|
205
226
|
...lines
|
|
206
227
|
].join("\n");
|
|
207
228
|
}
|
|
@@ -219,6 +240,7 @@ ${absent.join("\n")}`);
|
|
|
219
240
|
export {
|
|
220
241
|
isRegisterShaped,
|
|
221
242
|
registerAggregateIntent,
|
|
243
|
+
registerBypassesCache,
|
|
222
244
|
registerAggregate,
|
|
223
245
|
aggregateNote,
|
|
224
246
|
aggregateBlock,
|
|
@@ -227,6 +249,7 @@ export {
|
|
|
227
249
|
registerTokens,
|
|
228
250
|
buildRegisterQuery,
|
|
229
251
|
searchRegister,
|
|
252
|
+
rankRegisterRows,
|
|
230
253
|
registerNote,
|
|
231
254
|
certificateLinks
|
|
232
255
|
};
|
|
@@ -1565,7 +1565,7 @@ var certificatesSearch = {
|
|
|
1565
1565
|
handler: async (env, args) => {
|
|
1566
1566
|
const query = String(args?.query ?? "").trim().slice(0, 160);
|
|
1567
1567
|
if (!query) return null;
|
|
1568
|
-
const { searchRegister, registerNote, certificateLinks } = await import("./certificates-
|
|
1568
|
+
const { searchRegister, registerNote, certificateLinks } = await import("./certificates-V6KO2L6P.js");
|
|
1569
1569
|
const reg = await searchRegister(env.DB, query, true);
|
|
1570
1570
|
const output = reg?.rows?.length ? [registerNote(reg.rows), certificateLinks(reg.rows)].filter(Boolean).join("\n") : `No certificate was found for "${query}" in the certificates database (the register snapshot). State this as the search's result, with the searched string visible.`;
|
|
1571
1571
|
return { name: "certificates.search", query, output };
|
|
@@ -5,9 +5,10 @@ import {
|
|
|
5
5
|
queryFamily,
|
|
6
6
|
registerAggregate,
|
|
7
7
|
registerAggregateIntent,
|
|
8
|
+
registerBypassesCache,
|
|
8
9
|
registerNote,
|
|
9
10
|
searchRegister
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IRCDOFP4.js";
|
|
11
12
|
import {
|
|
12
13
|
TOOL_DECLARATION,
|
|
13
14
|
bindConstraintByQuantities,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
tableRetyped,
|
|
33
34
|
toolNote,
|
|
34
35
|
verdictNote
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ROXOL4GF.js";
|
|
36
37
|
import {
|
|
37
38
|
NO_CONTEXT,
|
|
38
39
|
STAGE_NAMES,
|
|
@@ -2221,7 +2222,8 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
2221
2222
|
const multiTurn = body?.multi_turn !== false;
|
|
2222
2223
|
const gen = await corpusGen(env.CACHE);
|
|
2223
2224
|
const withImage = effectiveImage || storedImage;
|
|
2224
|
-
const
|
|
2225
|
+
const registerBypass = registerBypassesCache(q.query);
|
|
2226
|
+
const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate || registerBypass ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
|
|
2225
2227
|
let probeTokensUsed = 0;
|
|
2226
2228
|
const pipeline = async (channel, qq) => {
|
|
2227
2229
|
const live2 = channel;
|
|
@@ -3080,7 +3082,7 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
3080
3082
|
} catch {
|
|
3081
3083
|
}
|
|
3082
3084
|
}
|
|
3083
|
-
if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
|
|
3085
|
+
if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && !registerBypass && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
|
|
3084
3086
|
ctx.waitUntil(
|
|
3085
3087
|
env.CACHE.put(exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q2.query, q2.lang, salt))), JSON.stringify({ answer: canonical, citations: cites, model, query_hash: queryHash, ...sourceQuality ? { source_quality: sourceQuality, confidence_note: streamedNote || qualityNote(sourceQuality) } : {}, ...streamedEntailment ? { entailment: streamedEntailment } : {} }), { expirationTtl: LIMITS.cacheTtlSec })
|
|
3086
3088
|
);
|
|
@@ -3240,7 +3242,7 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
3240
3242
|
telemetryDecisions(env, ctx, tier, queryHash, decisionsOut ?? null, Date.now() - tStart);
|
|
3241
3243
|
}
|
|
3242
3244
|
const out = { answer, citations: finalCites, ...jsonQuality ? { source_quality: jsonQuality, confidence_note: entailmentNote || qualityNote(jsonQuality), ...entailment ? { entailment } : {}, ...jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {} } : {}, model: MODELS.member, query_hash: queryHash, follow_ups: refusalShaped(answer, refusalScope(answerCites(answer))) ? [] : understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...verdictBlock ? [verdictBlock] : [], ...conditionBlock ? [conditionBlock] : [], ...aggregationBlock ? [aggregationBlock] : [], ...registerBlock ? [registerBlock] : [], ...completionBlocks], context_applied: ctxApplied, ...liveRecords ? { records: liveRecords } : {}, ...decisionsOut ? { decisions: decisionsOut } : {}, ...ablate ? { ablate } : {}, route: { route: routed.route, features: routed.features } };
|
|
3243
|
-
const cacheable = !contextual && !declaredCtx && !ablate && cacheableAnswer(answer, refusalScope(answerCites(answer))) && finalAnchors.violations.length === 0;
|
|
3245
|
+
const cacheable = !contextual && !declaredCtx && !ablate && !registerBypass && cacheableAnswer(answer, refusalScope(answerCites(answer))) && finalAnchors.violations.length === 0;
|
|
3244
3246
|
if (cacheable) {
|
|
3245
3247
|
const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q2.query, q2.lang, salt)));
|
|
3246
3248
|
ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
handleMemories,
|
|
5
5
|
scoreFaithfulness,
|
|
6
6
|
scoreJudge
|
|
7
|
-
} from "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
7
|
+
} from "../../chunk-V2QW6EZB.js";
|
|
8
|
+
import "../../chunk-IRCDOFP4.js";
|
|
9
9
|
import {
|
|
10
10
|
TOOLS_REGISTRY,
|
|
11
11
|
bindModelNode,
|
|
12
12
|
modelGroundingBlock
|
|
13
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-ROXOL4GF.js";
|
|
14
14
|
import {
|
|
15
15
|
handleSearch
|
|
16
16
|
} from "../../chunk-MUWO2EV6.js";
|
|
@@ -1175,13 +1175,13 @@ async function callToolOnce(env, ctx, _req, tier, key, name, args, t0) {
|
|
|
1175
1175
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
1176
1176
|
body: JSON.stringify({ ...args, stream: false })
|
|
1177
1177
|
});
|
|
1178
|
-
const { runTool, TOOLS_REGISTRY: TOOLS_REGISTRY2 } = await import("../../tools-
|
|
1178
|
+
const { runTool, TOOLS_REGISTRY: TOOLS_REGISTRY2 } = await import("../../tools-UCUNOJR4.js");
|
|
1179
1179
|
let payload;
|
|
1180
1180
|
if (TOOLS_REGISTRY2.some((t) => t.name === name && t.audiences.includes("mcp"))) {
|
|
1181
1181
|
const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
|
|
1182
1182
|
payload = r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
|
|
1183
1183
|
} else {
|
|
1184
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
1184
|
+
const res = name === "ask" ? await (await import("../../ask-64HJTVPS.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-KNPPTHMG.js")).handleSearch(env, ctx, inner, tier, key);
|
|
1185
1185
|
payload = await res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
1186
1186
|
}
|
|
1187
1187
|
telemetry(env, ctx, tier, `mcp:${name}`, null, !payload?.error, JSON.stringify(payload).length, await sha256Hex(`${name}:${JSON.stringify(args ?? {})}`), void 0, void 0, { durationMs: Date.now() - t0 });
|
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.178",
|
|
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",
|
|
@@ -46,7 +46,7 @@ export type { Env };
|
|
|
46
46
|
import { json, err, corsHeaders, readJson, validateQuery, type ApiKey } from "./lib/http";
|
|
47
47
|
import { chargeTokens, checkQuota, clientIp, creditBudget, creditEstimate, estimateTokens, telemetry, tokenBudget, usageTotal } from "./quota";
|
|
48
48
|
import { graphExpand, editionNote } from "./graph";
|
|
49
|
-
import { registerNote, searchRegister, certificateLinks, registerAggregateIntent, registerAggregate, aggregateNote, aggregateBlock, queryFamily } from "./certificates";
|
|
49
|
+
import { registerNote, searchRegister, certificateLinks, registerAggregateIntent, registerAggregate, aggregateNote, aggregateBlock, queryFamily, registerBypassesCache } from "./certificates";
|
|
50
50
|
import { TOOL_DECLARATION, parseToolCall, runTool, toolNote } from "./tools";
|
|
51
51
|
import { operationsLane } from "./operations.ts";
|
|
52
52
|
import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
|
|
@@ -594,7 +594,8 @@ async function handleAsk(
|
|
|
594
594
|
// cache.py) and old-generation entries miss (oimlsmart/rag#72)
|
|
595
595
|
const gen = await corpusGen(env.CACHE);
|
|
596
596
|
const withImage = effectiveImage || storedImage;
|
|
597
|
-
const
|
|
597
|
+
const registerBypass = registerBypassesCache(q.query);
|
|
598
|
+
const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate || registerBypass ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
|
|
598
599
|
let probeTokensUsed = 0;
|
|
599
600
|
|
|
600
601
|
// THE PIPELINE: one body, two deliveries. Every `return` inside is an
|
|
@@ -1837,7 +1838,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1837
1838
|
} });
|
|
1838
1839
|
} catch { /* the client may have closed; never block the close */ }
|
|
1839
1840
|
}
|
|
1840
|
-
if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
|
|
1841
|
+
if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && !registerBypass && cacheableAnswer(canonical, refusalScope(answerCites(canonical))) && !ablate) {
|
|
1841
1842
|
ctx.waitUntil(
|
|
1842
1843
|
env.CACHE.put(exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt))), JSON.stringify({ answer: canonical, citations: cites, model, query_hash: queryHash, ...(sourceQuality ? { source_quality: sourceQuality, confidence_note: streamedNote || qualityNote(sourceQuality) } : {}), ...(streamedEntailment ? { entailment: streamedEntailment } : {}) }), { expirationTtl: LIMITS.cacheTtlSec }),
|
|
1843
1844
|
);
|
|
@@ -2096,7 +2097,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
2096
2097
|
telemetryDecisions(env, ctx, tier, queryHash, decisionsOut ?? null, Date.now() - tStart);
|
|
2097
2098
|
}
|
|
2098
2099
|
const out = { answer, citations: finalCites, ...(jsonQuality ? { source_quality: jsonQuality!, confidence_note: entailmentNote || qualityNote(jsonQuality!), ...(entailment ? { entailment } : {}), ...(jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {}) } : {}), model: MODELS.member, query_hash: queryHash, follow_ups: refusalShaped(answer!, refusalScope(answerCites(answer!))) ? [] : (understanding?.follow_ups ?? []), blocks: [...c2ns.blocks, ...(verdictBlock ? [verdictBlock] : []), ...(conditionBlock ? [conditionBlock] : []), ...(aggregationBlock ? [aggregationBlock] : []), ...(registerBlock ? [registerBlock] : []), ...completionBlocks], context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}), ...(decisionsOut ? { decisions: decisionsOut } : {}), ...(ablate ? { ablate } : {}), route: { route: routed.route, features: routed.features } };
|
|
2099
|
-
const cacheable = !contextual && !declaredCtx && !ablate && cacheableAnswer(answer!, refusalScope(answerCites(answer!))) && finalAnchors.violations.length === 0;
|
|
2100
|
+
const cacheable = !contextual && !declaredCtx && !ablate && !registerBypass && cacheableAnswer(answer!, refusalScope(answerCites(answer!))) && finalAnchors.violations.length === 0;
|
|
2100
2101
|
if (cacheable) {
|
|
2101
2102
|
const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt)));
|
|
2102
2103
|
ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
|
|
@@ -40,6 +40,17 @@ export function registerAggregateIntent(query: string): "count" | "latest" | nul
|
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** The register's answers derive from the D1 snapshot, not the indexed
|
|
44
|
+
* corpus — but the answer cache (namespaced by INDEX_VERSION +
|
|
45
|
+
* corpus_gen) knows nothing about D1-plane loads, so a cached
|
|
46
|
+
* pre-register refusal preempted register-shaped questions the register
|
|
47
|
+
* answers (measured live 2026-10-28: cached refusals served over
|
|
48
|
+
* register-shaped asks until fresh). Such asks skip both cache reads
|
|
49
|
+
* and writes. */
|
|
50
|
+
export function registerBypassesCache(query: string): boolean {
|
|
51
|
+
return isRegisterShaped(query) || registerAggregateIntent(query) !== null;
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
export interface RegisterAggregate {
|
|
44
55
|
family: string;
|
|
45
56
|
count: number;
|
|
@@ -178,7 +189,13 @@ export function registerTokens(query: string): string[] {
|
|
|
178
189
|
if (/^\d+$/.test(w) && w.length < 2) continue;
|
|
179
190
|
if (!tokens.includes(w)) tokens.push(w);
|
|
180
191
|
}
|
|
181
|
-
|
|
192
|
+
// the budget is 12, not 6: a long holder name consumed the six slots
|
|
193
|
+
// before the distinctive model token was ever consulted (the live
|
|
194
|
+
// probe: "Endress + Hauser GmbH + Co., KG Automatic level gauge -
|
|
195
|
+
// Type: NMR81 and NMR84" cut "NMR84" — the question's WHOLE POINT —
|
|
196
|
+
// at position 7). rankRegisterRows discounts holder-only rows, so the
|
|
197
|
+
// wider OR net costs nothing in note quality.
|
|
198
|
+
return tokens.slice(0, 12);
|
|
182
199
|
}
|
|
183
200
|
|
|
184
201
|
/** The register query for a token set. Numeric tokens (a model number
|
|
@@ -208,7 +225,7 @@ export function buildRegisterQuery(tokens: string[]): { sql: string; params: str
|
|
|
208
225
|
return `(holder LIKE ?${n} OR model LIKE ?${n} OR num LIKE ?${n})`;
|
|
209
226
|
});
|
|
210
227
|
return {
|
|
211
|
-
sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT
|
|
228
|
+
sql: `SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE family = ?FAMILY AND (${clauses.join(" OR ")}) LIMIT 40`,
|
|
212
229
|
params,
|
|
213
230
|
};
|
|
214
231
|
}
|
|
@@ -257,20 +274,49 @@ export async function searchRegister(db: any, query: string, force = false): Pro
|
|
|
257
274
|
}
|
|
258
275
|
try {
|
|
259
276
|
const res = await db.prepare(sql).bind(...params).all();
|
|
260
|
-
|
|
277
|
+
const rows = rankRegisterRows((res.results ?? []) as RegisterRow[], tokens);
|
|
278
|
+
return { rows, tokens };
|
|
261
279
|
} catch {
|
|
262
280
|
// a missing table or a D1 hiccup degrades to no-note honestly
|
|
263
281
|
return { rows: [], tokens };
|
|
264
282
|
}
|
|
265
283
|
}
|
|
266
284
|
|
|
285
|
+
/** Rank the OR-matched candidates so the note's six rows carry the
|
|
286
|
+
* QUESTION's model, not just its holder: the live probes showed a
|
|
287
|
+
* distinctive model token losing its place to generic holder words
|
|
288
|
+
* ("Rice Lake Weighing Systems" matched 6 rows before "RL116XP" was
|
|
289
|
+
* ever consulted — the answer then denied a certificate the register
|
|
290
|
+
* holds). A model-token hit outranks a holder-token hit; numbers must
|
|
291
|
+
* match as standalone values (the "190 must not catch XK3190" rule).
|
|
292
|
+
* Measured 2026-10-28, the register-lane eval. */
|
|
293
|
+
export function rankRegisterRows(rows: RegisterRow[], tokens: string[]): RegisterRow[] {
|
|
294
|
+
const score = (row: RegisterRow): number => {
|
|
295
|
+
const model = (row.model || "").toLowerCase();
|
|
296
|
+
const holder = (row.holder || "").toLowerCase();
|
|
297
|
+
let s = 0;
|
|
298
|
+
for (const t of tokens) {
|
|
299
|
+
const tl = t.toLowerCase();
|
|
300
|
+
if (/^\d+$/.test(t)) {
|
|
301
|
+
if (new RegExp(`(^|[^a-z0-9])${tl}([^a-z0-9]|$)`).test(model)) s += 3;
|
|
302
|
+
else if (holder.includes(tl)) s += 1;
|
|
303
|
+
} else if (model.includes(tl)) s += 2;
|
|
304
|
+
else if (holder.includes(tl)) s += 1;
|
|
305
|
+
}
|
|
306
|
+
return s;
|
|
307
|
+
};
|
|
308
|
+
return [...rows]
|
|
309
|
+
.sort((a, b) => score(b) - score(a) || Number(b.year) - Number(a.year))
|
|
310
|
+
.slice(0, 6);
|
|
311
|
+
}
|
|
312
|
+
|
|
267
313
|
export function registerNote(rows: RegisterRow[]): string {
|
|
268
314
|
if (!rows.length) {
|
|
269
|
-
return `Certificate register
|
|
315
|
+
return `Certificate register — THIS NOTE ANSWERS THE QUESTION. NO certificate matching the asked holder or model appears in the ${P().publisher.name}-CS register snapshot. Answer from THIS note: state plainly that no such certificate is in this register — never fall back to the indexed publications' absence phrasing — and say the register is a snapshot rather than the live certification status.`;
|
|
270
316
|
}
|
|
271
317
|
const lines = rows.map((r) => `- ${r.num}: holder ${r.holder}, model "${r.model}"${r.year ? `, issued ${r.year}` : ""} — status ${r.status}`);
|
|
272
318
|
return [
|
|
273
|
-
"Certificate register —
|
|
319
|
+
"Certificate register — THIS NOTE ANSWERS THE QUESTION. The following certificates matched the asked holder or model; answer from THIS note. A model the note does not list is not among the matched rows — say so plainly instead of falling back to the indexed publications. Quote the certificate number, the holder and the status verbatim; the register is a snapshot, so qualify any statement about current certification accordingly.",
|
|
274
320
|
...lines,
|
|
275
321
|
].join("\n");
|
|
276
322
|
}
|