@konneal/engine 0.2.73 → 0.2.75
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-MFFOHVQ2.js → ask-AJJCWGW6.js} +3 -3
- package/dist/{certificates-EMHFAUWC.js → certificates-DNRUOYRF.js} +1 -1
- package/dist/{chunk-LL3LWBZH.js → chunk-7HWJOFTQ.js} +10 -7
- package/dist/{chunk-OXTRNMTZ.js → chunk-KO6DRI7C.js} +1 -1
- package/dist/{chunk-3IRX4B7Q.js → chunk-VGVTIPFW.js} +1 -1
- package/dist/{chunk-V7PEVNNO.js → chunk-XJPJO376.js} +43 -13
- package/dist/operations.d.ts +18 -1
- package/dist/{search-LN5Q7OSZ.js → search-LVUQLA3C.js} +2 -2
- package/dist/worker_public/src/index.js +5 -5
- package/package.json +1 -1
- package/workers/worker_public/src/ai.ts +13 -7
- package/workers/worker_public/src/ask.ts +31 -5
- package/workers/worker_public/src/certificates.ts +1 -1
- package/workers/worker_public/src/operations.ts +35 -9
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleAsk
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-XJPJO376.js";
|
|
4
|
+
import "./chunk-7HWJOFTQ.js";
|
|
5
5
|
import "./chunk-SPKPH54J.js";
|
|
6
6
|
import "./chunk-A3QHHUN5.js";
|
|
7
7
|
import "./chunk-LVZTWUVJ.js";
|
|
8
8
|
import "./chunk-2PCUAJJO.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-VGVTIPFW.js";
|
|
10
10
|
import "./chunk-3FYJM7LH.js";
|
|
11
11
|
export {
|
|
12
12
|
handleAsk
|
|
@@ -45,13 +45,16 @@ async function rerank(ai, model, query, texts) {
|
|
|
45
45
|
async function generateOnce(env, model, messages, effort) {
|
|
46
46
|
for (let attempt2 = 0; attempt2 < 2; attempt2++) {
|
|
47
47
|
try {
|
|
48
|
-
const res = await
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
const res = await Promise.race([
|
|
49
|
+
env.AI.run(model, {
|
|
50
|
+
messages,
|
|
51
|
+
max_tokens: effortBudget(effort ?? answerEffort(env)),
|
|
52
|
+
reasoning_effort: effort ?? answerEffort(env),
|
|
53
|
+
temperature: 0.6,
|
|
54
|
+
top_p: 0.95
|
|
55
|
+
}),
|
|
56
|
+
new Promise((r) => setTimeout(() => r(null), 12e4))
|
|
57
|
+
]);
|
|
55
58
|
if (typeof res?.response === "string" && res.response.trim()) return res.response;
|
|
56
59
|
if (typeof res?.choices?.[0]?.message?.content === "string" && res.choices[0].message.content.trim()) return res.choices[0].message.content;
|
|
57
60
|
if (attempt2 === 0) console.error("generate returned empty:", model);
|
|
@@ -121,7 +121,7 @@ function registerNote(rows) {
|
|
|
121
121
|
].join("\n");
|
|
122
122
|
}
|
|
123
123
|
function certificateLinks(rows) {
|
|
124
|
-
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.
|
|
124
|
+
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.oimlsmart.org/cert-pdf/${r.pdf_key}`);
|
|
125
125
|
return links.length ? `The certificates' documents:
|
|
126
126
|
${links.join("\n")}` : "";
|
|
127
127
|
}
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
tokenBudget,
|
|
40
40
|
understandQuery,
|
|
41
41
|
usageTotal
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-7HWJOFTQ.js";
|
|
43
43
|
import {
|
|
44
44
|
corsHeaders,
|
|
45
45
|
err,
|
|
@@ -72,7 +72,7 @@ import {
|
|
|
72
72
|
certificateLinks,
|
|
73
73
|
registerNote,
|
|
74
74
|
searchRegister
|
|
75
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-VGVTIPFW.js";
|
|
76
76
|
import {
|
|
77
77
|
P
|
|
78
78
|
} from "./chunk-3FYJM7LH.js";
|
|
@@ -1896,7 +1896,7 @@ async function runTool(db, call) {
|
|
|
1896
1896
|
if (call.name !== "register_search") return null;
|
|
1897
1897
|
const query = String(call.args?.query ?? "").trim().slice(0, 160);
|
|
1898
1898
|
if (!query) return null;
|
|
1899
|
-
const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-
|
|
1899
|
+
const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-DNRUOYRF.js");
|
|
1900
1900
|
const reg = await searchRegister2(db, query, true);
|
|
1901
1901
|
const output = reg?.rows?.length ? registerNote2(reg.rows) : `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.`;
|
|
1902
1902
|
return { name: call.name, query, output };
|
|
@@ -1992,11 +1992,18 @@ function catalogNote(anchors) {
|
|
|
1992
1992
|
return `The COMPLETE name index of the platform's operations:
|
|
1993
1993
|
${anchors.map((a) => `- ${a}`).join("\n")}`;
|
|
1994
1994
|
}
|
|
1995
|
-
async function
|
|
1996
|
-
if (!isOperationIntent(query)) return
|
|
1995
|
+
async function operationsLane(db, query) {
|
|
1996
|
+
if (!isOperationIntent(query)) return { matched: [] };
|
|
1997
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1998
|
+
const lane = await operationsLaneOnce(db, query, attempt > 0);
|
|
1999
|
+
if (lane !== void 0) return lane;
|
|
2000
|
+
}
|
|
2001
|
+
return { matched: [] };
|
|
2002
|
+
}
|
|
2003
|
+
async function operationsLaneOnce(db, query, retried) {
|
|
1997
2004
|
try {
|
|
1998
|
-
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
1999
|
-
const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
|
|
2005
|
+
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text, docidentifier FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
2006
|
+
const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? ""), docidentifier: String(r.docidentifier ?? "Platform API") })).filter((r) => r.anchor);
|
|
2000
2007
|
const matched = matchOperations(query, opRows);
|
|
2001
2008
|
const parts = [];
|
|
2002
2009
|
if (matched.length) {
|
|
@@ -2004,8 +2011,10 @@ async function operationsCatalogNote(db, query) {
|
|
|
2004
2011
|
${matched.map((m) => `- ${m.text}`).join("\n")}`);
|
|
2005
2012
|
}
|
|
2006
2013
|
parts.push(catalogNote(opRows.map((r) => r.anchor)));
|
|
2007
|
-
|
|
2008
|
-
|
|
2014
|
+
const note = parts.filter(Boolean).join("\n\n") || void 0;
|
|
2015
|
+
return { note, matched };
|
|
2016
|
+
} catch (e) {
|
|
2017
|
+
console.error(`operations-catalog: D1 read failed${retried ? " twice" : ""} \u2014 the note is dropped`, String(e).slice(0, 120));
|
|
2009
2018
|
return void 0;
|
|
2010
2019
|
}
|
|
2011
2020
|
}
|
|
@@ -2191,7 +2200,10 @@ async function* sseTokens(stream, stats) {
|
|
|
2191
2200
|
const decoder = new TextDecoder();
|
|
2192
2201
|
let buf = "";
|
|
2193
2202
|
while (true) {
|
|
2194
|
-
const { done, value } = await
|
|
2203
|
+
const { done, value } = await Promise.race([
|
|
2204
|
+
reader.read(),
|
|
2205
|
+
new Promise((r) => setTimeout(() => r({ done: true }), 12e4))
|
|
2206
|
+
]);
|
|
2195
2207
|
if (done) break;
|
|
2196
2208
|
buf += decoder.decode(value, { stream: true });
|
|
2197
2209
|
const lines = buf.split("\n");
|
|
@@ -2450,7 +2462,7 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
2450
2462
|
}
|
|
2451
2463
|
console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
|
|
2452
2464
|
const regNote = register ? [registerNote(register.rows), certificateLinks(register.rows)].filter(Boolean).join("\n") : void 0;
|
|
2453
|
-
const
|
|
2465
|
+
const ops = await operationsLane(env.DB, q.query);
|
|
2454
2466
|
if (understanding?.intent === "conversational") {
|
|
2455
2467
|
const queryHash2 = await sha256Hex(q.query);
|
|
2456
2468
|
const messages2 = [
|
|
@@ -2796,13 +2808,31 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
2796
2808
|
return matched.length ? matched : g;
|
|
2797
2809
|
})();
|
|
2798
2810
|
const vocabNote = glossaryForNote.length ? "Vocabulary binding \u2014 defined terms in the indexed corpus that may name this question's subject:\n" + glossaryForNote.map((g) => `- ${g.term} (${g.docidentifier}): ${g.definition}`).join("\n") + "\nIf the question describes a symptom or behavior in everyday words, OPEN the answer by naming the matching defined term, quote its definition, and cite its defining publication; keep using that term throughout. Match TIME SCALE carefully: change under a constant load over minutes/hours is creep; change over months/years of use is span stability or durability \u2014 do not call long-term drift creep." : void 0;
|
|
2811
|
+
const opsHits = ops.matched.map((m) => ({
|
|
2812
|
+
id: `ops:${m.anchor}`,
|
|
2813
|
+
score: 0.999,
|
|
2814
|
+
metadata: {
|
|
2815
|
+
doc_id: "smart-ops:catalog",
|
|
2816
|
+
docidentifier: m.docidentifier ?? "Platform API",
|
|
2817
|
+
doctype: "api-operation",
|
|
2818
|
+
doc_number: "",
|
|
2819
|
+
edition: "",
|
|
2820
|
+
language: "en",
|
|
2821
|
+
clause_anchor: m.anchor,
|
|
2822
|
+
clause_title: `Platform operation ${m.anchor}`,
|
|
2823
|
+
tier: "curated",
|
|
2824
|
+
corpus: "smart-ops",
|
|
2825
|
+
text_ref: ""
|
|
2826
|
+
},
|
|
2827
|
+
text: `${m.anchor} \u2014 ${m.text}`
|
|
2828
|
+
}));
|
|
2799
2829
|
const { messages, usedHits } = buildMessages(
|
|
2800
2830
|
q.query,
|
|
2801
|
-
hits,
|
|
2831
|
+
[...opsHits, ...hits],
|
|
2802
2832
|
q.lang,
|
|
2803
2833
|
keptHistory,
|
|
2804
2834
|
// stage-extracted graph facts (GraphRAG) ride the same note channel
|
|
2805
|
-
[
|
|
2835
|
+
[ops.note, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
|
|
2806
2836
|
summary,
|
|
2807
2837
|
budget
|
|
2808
2838
|
);
|
package/dist/operations.d.ts
CHANGED
|
@@ -6,9 +6,26 @@ export declare function anchorWords(anchor: string): string[];
|
|
|
6
6
|
export declare function matchOperations(query: string, rows: {
|
|
7
7
|
anchor: string;
|
|
8
8
|
text: string;
|
|
9
|
+
docidentifier?: string;
|
|
9
10
|
}[]): {
|
|
10
11
|
anchor: string;
|
|
11
12
|
text: string;
|
|
13
|
+
docidentifier?: string;
|
|
12
14
|
}[];
|
|
13
15
|
export declare function catalogNote(anchors: string[]): string;
|
|
14
|
-
|
|
16
|
+
/** The lane's whole result: the authoritative note (matched lines +
|
|
17
|
+
* name index) AND the matched rows themselves — the ask path rides the
|
|
18
|
+
* matched operations as CITABLE PASSAGES, because the refusal logic
|
|
19
|
+
* keys on passages and a side-note cannot outrank the passages'
|
|
20
|
+
* silence (observed live 2026-09-29: the note was present and the
|
|
21
|
+
* model still refused "the only API operation in the current context
|
|
22
|
+
* is the blob download"). */
|
|
23
|
+
export interface OperationsLane {
|
|
24
|
+
note?: string;
|
|
25
|
+
matched: {
|
|
26
|
+
anchor: string;
|
|
27
|
+
text: string;
|
|
28
|
+
docidentifier?: string;
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare function operationsLane(db: any, query: string): Promise<OperationsLane>;
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
modelGroundingBlock,
|
|
7
7
|
scoreFaithfulness,
|
|
8
8
|
scoreJudge
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-XJPJO376.js";
|
|
10
10
|
import {
|
|
11
11
|
handleSearch
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-KO6DRI7C.js";
|
|
13
13
|
import {
|
|
14
14
|
buildMessages,
|
|
15
15
|
cfBlobs,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
sessionFrom,
|
|
36
36
|
telemetry,
|
|
37
37
|
understandQuery
|
|
38
|
-
} from "../../chunk-
|
|
38
|
+
} from "../../chunk-7HWJOFTQ.js";
|
|
39
39
|
import {
|
|
40
40
|
authenticate,
|
|
41
41
|
corsHeaders,
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
sha256Hex,
|
|
64
64
|
today
|
|
65
65
|
} from "../../chunk-2PCUAJJO.js";
|
|
66
|
-
import "../../chunk-
|
|
66
|
+
import "../../chunk-VGVTIPFW.js";
|
|
67
67
|
import {
|
|
68
68
|
P,
|
|
69
69
|
setProfile
|
|
@@ -959,7 +959,7 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
959
959
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
960
960
|
body: JSON.stringify({ ...args, stream: false })
|
|
961
961
|
});
|
|
962
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
962
|
+
const res = name === "ask" ? await (await import("../../ask-AJJCWGW6.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LVUQLA3C.js")).handleSearch(env, ctx, inner, tier, key);
|
|
963
963
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
964
964
|
});
|
|
965
965
|
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.75",
|
|
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",
|
|
@@ -54,13 +54,19 @@ export async function generateOnce(env: any, model: string, messages: any[], eff
|
|
|
54
54
|
// silently blindfolds figure answers)
|
|
55
55
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
56
56
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
// bounded: a STALLED call (no error, no response — observed live
|
|
58
|
+
// 2026-09-29, asks hanging at zero bytes for minutes) must fall
|
|
59
|
+
// through to the retry/fallback, never hang the ask forever
|
|
60
|
+
const res: any = await Promise.race([
|
|
61
|
+
env.AI.run(model, {
|
|
62
|
+
messages,
|
|
63
|
+
max_tokens: effortBudget(effort ?? answerEffort(env)),
|
|
64
|
+
reasoning_effort: effort ?? answerEffort(env),
|
|
65
|
+
temperature: 0.6,
|
|
66
|
+
top_p: 0.95,
|
|
67
|
+
}),
|
|
68
|
+
new Promise<null>((r) => setTimeout(() => r(null), 120_000)),
|
|
69
|
+
]);
|
|
64
70
|
// an EMPTY string is a failed generation, not an answer: the
|
|
65
71
|
// platform intermittently returns empty bodies with a 200 (observed
|
|
66
72
|
// live 2026-09-20 — answers went blank platform-wide while retrieval
|
|
@@ -41,7 +41,7 @@ import { chargeTokens, checkQuota, clientIp, estimateTokens, telemetry, tokenBud
|
|
|
41
41
|
import { graphExpand, editionNote } from "./graph";
|
|
42
42
|
import { registerNote, searchRegister, certificateLinks } from "./certificates";
|
|
43
43
|
import { TOOL_DECLARATION, parseToolCall, runTool, toolNote } from "./tools";
|
|
44
|
-
import {
|
|
44
|
+
import { operationsLane } from "./operations.ts";
|
|
45
45
|
import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
|
|
46
46
|
import { P } from "./profile.ts";
|
|
47
47
|
|
|
@@ -232,7 +232,12 @@ async function* sseTokens(stream: ReadableStream<Uint8Array>, stats?: { usage?:
|
|
|
232
232
|
const decoder = new TextDecoder();
|
|
233
233
|
let buf = "";
|
|
234
234
|
while (true) {
|
|
235
|
-
|
|
235
|
+
// bounded read: a stalled stream (no error, no chunk) delivers what
|
|
236
|
+
// it has instead of hanging the ask at zero tokens
|
|
237
|
+
const { done, value } = await Promise.race([
|
|
238
|
+
reader.read(),
|
|
239
|
+
new Promise<{ done: true; value?: undefined }>((r) => setTimeout(() => r({ done: true }), 120_000)),
|
|
240
|
+
]);
|
|
236
241
|
if (done) break;
|
|
237
242
|
buf += decoder.decode(value, { stream: true });
|
|
238
243
|
const lines = buf.split("\n");
|
|
@@ -672,7 +677,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
672
677
|
}
|
|
673
678
|
console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
|
|
674
679
|
const regNote = register ? [registerNote(register.rows), certificateLinks(register.rows)].filter(Boolean).join("\n") : undefined;
|
|
675
|
-
const
|
|
680
|
+
const ops = await operationsLane(env.DB, q.query);
|
|
676
681
|
|
|
677
682
|
// Conversational route, decided by query UNDERSTANDING (any language, any
|
|
678
683
|
// phrasing) — not string matching. No retrieval: nothing in the corpus
|
|
@@ -1177,13 +1182,34 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1177
1182
|
glossaryForNote.map((g) => `- ${g.term} (${g.docidentifier}): ${g.definition}`).join("\n") +
|
|
1178
1183
|
"\nIf the question describes a symptom or behavior in everyday words, OPEN the answer by naming the matching defined term, quote its definition, and cite its defining publication; keep using that term throughout. Match TIME SCALE carefully: change under a constant load over minutes/hours is creep; change over months/years of use is span stability or durability — do not call long-term drift creep."
|
|
1179
1184
|
: undefined;
|
|
1185
|
+
// the matched operations ride as CITABLE PASSAGES ahead of the
|
|
1186
|
+
// retrieved hits — the refusal logic and the citation machinery key on
|
|
1187
|
+
// passages, and a side-note cannot outrank the passages' silence
|
|
1188
|
+
const opsHits: Hit[] = ops.matched.map((m) => ({
|
|
1189
|
+
id: `ops:${m.anchor}`,
|
|
1190
|
+
score: 0.999,
|
|
1191
|
+
metadata: {
|
|
1192
|
+
doc_id: "smart-ops:catalog",
|
|
1193
|
+
docidentifier: m.docidentifier ?? "Platform API",
|
|
1194
|
+
doctype: "api-operation",
|
|
1195
|
+
doc_number: "",
|
|
1196
|
+
edition: "",
|
|
1197
|
+
language: "en",
|
|
1198
|
+
clause_anchor: m.anchor,
|
|
1199
|
+
clause_title: `Platform operation ${m.anchor}`,
|
|
1200
|
+
tier: "curated",
|
|
1201
|
+
corpus: "smart-ops",
|
|
1202
|
+
text_ref: "",
|
|
1203
|
+
},
|
|
1204
|
+
text: `${m.anchor} — ${m.text}`,
|
|
1205
|
+
}));
|
|
1180
1206
|
const { messages, usedHits } = buildMessages(
|
|
1181
1207
|
q.query,
|
|
1182
|
-
hits,
|
|
1208
|
+
[...opsHits, ...hits],
|
|
1183
1209
|
q.lang,
|
|
1184
1210
|
keptHistory,
|
|
1185
1211
|
// stage-extracted graph facts (GraphRAG) ride the same note channel
|
|
1186
|
-
[
|
|
1212
|
+
[ops.note, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
|
|
1187
1213
|
summary,
|
|
1188
1214
|
budget,
|
|
1189
1215
|
);
|
|
@@ -134,7 +134,7 @@ export function registerNote(rows: RegisterRow[]): string {
|
|
|
134
134
|
* is in the R2 plane (TODO 7) — the user gets the document, not just
|
|
135
135
|
* the fact. */
|
|
136
136
|
export function certificateLinks(rows: RegisterRow[]): string {
|
|
137
|
-
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.
|
|
137
|
+
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.oimlsmart.org/cert-pdf/${r.pdf_key}`);
|
|
138
138
|
return links.length ? `The certificates' documents:\n${links.join("\n")}` : "";
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -38,7 +38,7 @@ export function anchorWords(anchor: string): string[] {
|
|
|
38
38
|
|
|
39
39
|
const singular = (w: string) => (w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w);
|
|
40
40
|
|
|
41
|
-
export function matchOperations(query: string, rows: { anchor: string; text: string }[]): { anchor: string; text: string }[] {
|
|
41
|
+
export function matchOperations(query: string, rows: { anchor: string; text: string; docidentifier?: string }[]): { anchor: string; text: string; docidentifier?: string }[] {
|
|
42
42
|
const q = query.toLowerCase();
|
|
43
43
|
const stems = ACTION_STEMS.filter((s) => q.includes(s));
|
|
44
44
|
const objects = query
|
|
@@ -68,19 +68,45 @@ export function catalogNote(anchors: string[]): string {
|
|
|
68
68
|
return `The COMPLETE name index of the platform's operations:\n${anchors.map((a) => `- ${a}`).join("\n")}`;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
/** The lane's whole result: the authoritative note (matched lines +
|
|
72
|
+
* name index) AND the matched rows themselves — the ask path rides the
|
|
73
|
+
* matched operations as CITABLE PASSAGES, because the refusal logic
|
|
74
|
+
* keys on passages and a side-note cannot outrank the passages'
|
|
75
|
+
* silence (observed live 2026-09-29: the note was present and the
|
|
76
|
+
* model still refused "the only API operation in the current context
|
|
77
|
+
* is the blob download"). */
|
|
78
|
+
export interface OperationsLane {
|
|
79
|
+
note?: string;
|
|
80
|
+
matched: { anchor: string; text: string; docidentifier?: string }[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function operationsLane(db: any, query: string): Promise<OperationsLane> {
|
|
84
|
+
if (!isOperationIntent(query)) return { matched: [] };
|
|
85
|
+
// one retry, and the failure is LOUD: a silently dropped note answers
|
|
86
|
+
// an operation question with the OIML refusal (observed live 2026-09-29
|
|
87
|
+
// — a transient D1 read failed into the catch and the lane vanished)
|
|
88
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
89
|
+
const lane = await operationsLaneOnce(db, query, attempt > 0);
|
|
90
|
+
if (lane !== undefined) return lane;
|
|
91
|
+
}
|
|
92
|
+
return { matched: [] };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function operationsLaneOnce(db: any, query: string, retried: boolean): Promise<OperationsLane | undefined> {
|
|
96
|
+
try {
|
|
97
|
+
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text, docidentifier FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
98
|
+
const opRows = (rows as any[]).map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? ""), docidentifier: String(r.docidentifier ?? "Platform API") })).filter((r) => r.anchor);
|
|
76
99
|
const matched = matchOperations(query, opRows);
|
|
77
100
|
const parts: string[] = [];
|
|
78
101
|
if (matched.length) {
|
|
79
102
|
parts.push(`Operations whose name or description matches the question (AUTHORITATIVE — if one matches, the answer MUST name it and cite its line; never answer that the information is missing):\n${matched.map((m) => `- ${m.text}`).join("\n")}`);
|
|
80
103
|
}
|
|
81
104
|
parts.push(catalogNote(opRows.map((r) => r.anchor)));
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
const note = parts.filter(Boolean).join("\n\n") || undefined;
|
|
106
|
+
return { note, matched };
|
|
107
|
+
} catch (e) {
|
|
108
|
+
// the lane is optional — but never silent (the blind-failure lesson)
|
|
109
|
+
console.error(`operations-catalog: D1 read failed${retried ? " twice" : ""} — the note is dropped`, String(e).slice(0, 120));
|
|
110
|
+
return undefined;
|
|
85
111
|
}
|
|
86
112
|
}
|