@konneal/engine 0.2.76 → 0.2.77

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,12 +1,12 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-64G44J7U.js";
3
+ } from "./chunk-GOYFWC3N.js";
4
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-VGVTIPFW.js";
9
+ import "./chunk-7AQM3GSO.js";
10
10
  import "./chunk-3FYJM7LH.js";
11
11
  export {
12
12
  handleAsk
@@ -2,16 +2,18 @@ import {
2
2
  buildRegisterQuery,
3
3
  certificateLinks,
4
4
  isRegisterShaped,
5
+ printedCertificateNumber,
5
6
  queryFamily,
6
7
  registerNote,
7
8
  registerTokens,
8
9
  searchRegister
9
- } from "./chunk-VGVTIPFW.js";
10
+ } from "./chunk-7AQM3GSO.js";
10
11
  import "./chunk-3FYJM7LH.js";
11
12
  export {
12
13
  buildRegisterQuery,
13
14
  certificateLinks,
14
15
  isRegisterShaped,
16
+ printedCertificateNumber,
15
17
  queryFamily,
16
18
  registerNote,
17
19
  registerTokens,
@@ -20,6 +20,10 @@ export declare function isRegisterShaped(query: string): boolean;
20
20
  * search filters to it, so a question about R 60 never presents R 76
21
21
  * rows. */
22
22
  export declare function queryFamily(query: string): string | null;
23
+ /** A PRINTED certificate number on a nameplate ("R76/2006-A-GB1-18.08")
24
+ * names the exact register row — it must not be shredded into word
25
+ * tokens that compete with it. */
26
+ export declare function printedCertificateNumber(query: string): string | null;
23
27
  export declare function registerTokens(query: string): string[];
24
28
  /** The register query for a token set. Numeric tokens (a model number
25
29
  * like "190") must match as a STANDALONE value — `LIKE '%190%'` also
@@ -10,6 +10,10 @@ function queryFamily(query) {
10
10
  const m = /\b([RDMB])\s?-?(\d{2,3})\b/i.exec(query);
11
11
  return m ? `${m[1].toUpperCase()}${m[2].padStart(2, "0")}` : null;
12
12
  }
13
+ function printedCertificateNumber(query) {
14
+ const m = /\b([RDMB]\s?-?\d{2,3}\s?\/\s?\d{4}\s?-\s?[A-Z0-9]+\s?-\s?[^\s,;]+(?:\s?\.\s?\d+)?)/i.exec(query);
15
+ return m ? m[1].replace(/\s+/g, "").replace(/-\./, ".") : null;
16
+ }
13
17
  function registerTokens(query) {
14
18
  const stop = /* @__PURE__ */ new Set([
15
19
  "the",
@@ -90,6 +94,19 @@ function buildRegisterQuery(tokens) {
90
94
  }
91
95
  async function searchRegister(db, query, force = false) {
92
96
  if (!force && !isRegisterShaped(query)) return null;
97
+ const printed = printedCertificateNumber(query);
98
+ if (printed) {
99
+ try {
100
+ const seq = printed.replace(/\.\d+$/, "");
101
+ const res = await db.prepare(`SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE REPLACE(num, ' ', '') = ?1 OR REPLACE(num, ' ', '') LIKE ?2 || '%' ORDER BY num LIMIT 6`).bind(printed, seq).all();
102
+ if ((res.results ?? []).length) {
103
+ console.log(`register-search: printed-number hit ${printed} \u2192 ${(res.results ?? []).length} rows`);
104
+ return { rows: res.results ?? [], tokens: [printed] };
105
+ }
106
+ console.log(`register-search: printed number ${printed} missed the register \u2014 falling back to tokens`);
107
+ } catch {
108
+ }
109
+ }
93
110
  const tokens = registerTokens(query);
94
111
  if (!tokens.length) return null;
95
112
  const built = buildRegisterQuery(tokens);
@@ -129,6 +146,7 @@ ${links.join("\n")}` : "";
129
146
  export {
130
147
  isRegisterShaped,
131
148
  queryFamily,
149
+ printedCertificateNumber,
132
150
  registerTokens,
133
151
  buildRegisterQuery,
134
152
  searchRegister,
@@ -72,7 +72,7 @@ import {
72
72
  certificateLinks,
73
73
  registerNote,
74
74
  searchRegister
75
- } from "./chunk-VGVTIPFW.js";
75
+ } from "./chunk-7AQM3GSO.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-DNRUOYRF.js");
1899
+ const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-YFL4K7O3.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 };
@@ -2020,14 +2020,18 @@ ${matched.map((m) => `- ${m.text}`).join("\n")}`);
2020
2020
  }
2021
2021
 
2022
2022
  // workers/worker_public/prompts/nameplate.md
2023
- var nameplate_default = `You read instrument nameplates. From the photograph, extract the
2024
- manufacturer's name exactly as printed and the model designation exactly
2025
- as printed. Answer with ONLY a JSON object on one line:
2023
+ var nameplate_default = `You read instrument nameplates. From the photograph, extract:
2026
2024
 
2027
- {"manufacturer": "\u2026", "model": "\u2026"}
2025
+ {"manufacturer": "\u2026", "model": "\u2026", "certificate_number": "\u2026"}
2028
2026
 
2029
- If the photograph shows no manufacturer or no model, use null for the
2030
- missing value. Never guess beyond what is printed.
2027
+ - manufacturer: the manufacturer's name exactly as printed, else null.
2028
+ - model: the model designation exactly as printed, else null.
2029
+ - certificate_number: the OIML certificate number if one is printed
2030
+ (it looks like R76/2006-A-GB1-18.08 \u2014 a family, an edition year, an
2031
+ authority code, a year and a sequence), exactly as printed, else null.
2032
+
2033
+ Answer with ONLY the JSON object on one line. Never guess beyond what is
2034
+ printed.
2031
2035
  `;
2032
2036
 
2033
2037
  // workers/worker_public/src/nameplate-parse.ts
@@ -2037,15 +2041,19 @@ function parseNameplate(text) {
2037
2041
  try {
2038
2042
  const j = JSON.parse(m[0]);
2039
2043
  const clean = (v) => typeof v === "string" && v.trim() ? v.trim().slice(0, 80) : null;
2040
- const out = { manufacturer: clean(j.manufacturer), model: clean(j.model) };
2041
- return out.manufacturer || out.model ? out : null;
2044
+ const out = {
2045
+ manufacturer: clean(j.manufacturer),
2046
+ model: clean(j.model),
2047
+ certificate_number: clean(j.certificate_number)
2048
+ };
2049
+ return out.manufacturer || out.model || out.certificate_number ? out : null;
2042
2050
  } catch {
2043
2051
  return null;
2044
2052
  }
2045
2053
  }
2046
2054
  function nameplateRegisterQuery(np, question) {
2047
2055
  const fam = /\b([RDMB])\s?-?(\d{2,3})\b/i.exec(question);
2048
- return [np.manufacturer, np.model, fam ? `${fam[1]} ${fam[2]}` : null].filter(Boolean).join(" ");
2056
+ return [np.certificate_number, np.manufacturer, np.model, fam ? `${fam[1]} ${fam[2]}` : null].filter(Boolean).join(" ");
2049
2057
  }
2050
2058
 
2051
2059
  // workers/worker_public/src/nameplate.ts
@@ -2059,9 +2067,13 @@ async function extractNameplate(ai, model, image) {
2059
2067
  ],
2060
2068
  // the budget rises with the retry's effort — elevated effort under
2061
2069
  // a flat budget starves the answer on this model (measured,
2062
- // 2026-09-18: 5/38 at medium with the flat budget)
2070
+ // 2026-09-18: 5/38 at medium with the flat budget); the
2071
+ // temperature is a READING temperature — a transcription task at
2072
+ // default sampling hallucinated brands on a low-res plate
2073
+ // (observed live 2026-10-03: "Tektronix" on a CAS label)
2063
2074
  max_tokens: attempt === 0 ? 512 : 1536,
2064
- reasoning_effort: attempt === 0 ? "low" : "medium"
2075
+ reasoning_effort: attempt === 0 ? "low" : "medium",
2076
+ temperature: 0.15
2065
2077
  });
2066
2078
  const text = typeof res?.response === "string" ? res.response : res?.choices?.[0]?.message?.content;
2067
2079
  const np = parseNameplate(text ?? "");
@@ -1,8 +1,10 @@
1
1
  export interface Nameplate {
2
2
  manufacturer: string | null;
3
3
  model: string | null;
4
+ certificate_number?: string | null;
4
5
  }
5
6
  export declare function parseNameplate(text: string): Nameplate | null;
6
- /** The register query the extraction feeds: the nameplate's tokens lead,
7
- * the question's family (if named) scopes. */
7
+ /** The register query the extraction feeds: a PRINTED CERTIFICATE NUMBER
8
+ * leads (it names the exact row), then the nameplate's tokens, then the
9
+ * question's family (if named) scopes. */
8
10
  export declare function nameplateRegisterQuery(np: Nameplate, question: string): string;
@@ -1,8 +1,12 @@
1
- You read instrument nameplates. From the photograph, extract the
2
- manufacturer's name exactly as printed and the model designation exactly
3
- as printed. Answer with ONLY a JSON object on one line:
1
+ You read instrument nameplates. From the photograph, extract:
4
2
 
5
- {"manufacturer": "…", "model": "…"}
3
+ {"manufacturer": "…", "model": "…", "certificate_number": "…"}
6
4
 
7
- If the photograph shows no manufacturer or no model, use null for the
8
- missing value. Never guess beyond what is printed.
5
+ - manufacturer: the manufacturer's name exactly as printed, else null.
6
+ - model: the model designation exactly as printed, else null.
7
+ - certificate_number: the OIML certificate number if one is printed
8
+ (it looks like R76/2006-A-GB1-18.08 — a family, an edition year, an
9
+ authority code, a year and a sequence), exactly as printed, else null.
10
+
11
+ Answer with ONLY the JSON object on one line. Never guess beyond what is
12
+ printed.
@@ -6,7 +6,7 @@ import {
6
6
  modelGroundingBlock,
7
7
  scoreFaithfulness,
8
8
  scoreJudge
9
- } from "../../chunk-64G44J7U.js";
9
+ } from "../../chunk-GOYFWC3N.js";
10
10
  import {
11
11
  handleSearch
12
12
  } from "../../chunk-KO6DRI7C.js";
@@ -63,7 +63,7 @@ import {
63
63
  sha256Hex,
64
64
  today
65
65
  } from "../../chunk-2PCUAJJO.js";
66
- import "../../chunk-VGVTIPFW.js";
66
+ import "../../chunk-7AQM3GSO.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-PK75QJ66.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LVUQLA3C.js")).handleSearch(env, ctx, inner, tier, key);
962
+ const res = name === "ask" ? await (await import("../../ask-DKYIVPNV.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.76",
30
+ "version": "0.2.77",
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",
@@ -1,8 +1,12 @@
1
- You read instrument nameplates. From the photograph, extract the
2
- manufacturer's name exactly as printed and the model designation exactly
3
- as printed. Answer with ONLY a JSON object on one line:
1
+ You read instrument nameplates. From the photograph, extract:
4
2
 
5
- {"manufacturer": "…", "model": "…"}
3
+ {"manufacturer": "…", "model": "…", "certificate_number": "…"}
6
4
 
7
- If the photograph shows no manufacturer or no model, use null for the
8
- missing value. Never guess beyond what is printed.
5
+ - manufacturer: the manufacturer's name exactly as printed, else null.
6
+ - model: the model designation exactly as printed, else null.
7
+ - certificate_number: the OIML certificate number if one is printed
8
+ (it looks like R76/2006-A-GB1-18.08 — a family, an edition year, an
9
+ authority code, a year and a sequence), exactly as printed, else null.
10
+
11
+ Answer with ONLY the JSON object on one line. Never guess beyond what is
12
+ printed.
@@ -38,6 +38,14 @@ export function queryFamily(query: string): string | null {
38
38
  return m ? `${m[1].toUpperCase()}${m[2].padStart(2, "0")}` : null;
39
39
  }
40
40
 
41
+ /** A PRINTED certificate number on a nameplate ("R76/2006-A-GB1-18.08")
42
+ * names the exact register row — it must not be shredded into word
43
+ * tokens that compete with it. */
44
+ export function printedCertificateNumber(query: string): string | null {
45
+ const m = /\b([RDMB]\s?-?\d{2,3}\s?\/\s?\d{4}\s?-\s?[A-Z0-9]+\s?-\s?[^\s,;]+(?:\s?\.\s?\d+)?)/i.exec(query);
46
+ return m ? m[1].replace(/\s+/g, "").replace(/-\./, ".") : null;
47
+ }
48
+
41
49
  export function registerTokens(query: string): string[] {
42
50
  const stop = new Set([
43
51
  "the", "a", "an", "is", "are", "was", "were", "still", "currently", "in", "on", "for", "of", "and", "or",
@@ -93,6 +101,26 @@ export async function searchRegister(db: any, query: string, force = false): Pro
93
101
  // the shaping gate exists for ASK questions (is this a register
94
102
  // question?); a TOOL invocation already decided — skip the gate
95
103
  if (!force && !isRegisterShaped(query)) return null;
104
+ // a PRINTED certificate number names the row: query it first, and only
105
+ // fall back to the token arms when it misses (an extraction may drop a
106
+ // character; the prefix up to the sequence still scopes the family)
107
+ const printed = printedCertificateNumber(query);
108
+ if (printed) {
109
+ try {
110
+ const seq = printed.replace(/\.\d+$/, "");
111
+ const res = await db
112
+ .prepare(`SELECT num, family, holder, model, year, status, pdf_key FROM certificates WHERE REPLACE(num, ' ', '') = ?1 OR REPLACE(num, ' ', '') LIKE ?2 || '%' ORDER BY num LIMIT 6`)
113
+ .bind(printed, seq)
114
+ .all();
115
+ if ((res.results ?? []).length) {
116
+ console.log(`register-search: printed-number hit ${printed} → ${(res.results ?? []).length} rows`);
117
+ return { rows: (res.results ?? []) as RegisterRow[], tokens: [printed] };
118
+ }
119
+ console.log(`register-search: printed number ${printed} missed the register — falling back to tokens`);
120
+ } catch {
121
+ // fall through to the token arms
122
+ }
123
+ }
96
124
  const tokens = registerTokens(query);
97
125
  if (!tokens.length) return null;
98
126
  const built = buildRegisterQuery(tokens);
@@ -6,24 +6,30 @@
6
6
  export interface Nameplate {
7
7
  manufacturer: string | null;
8
8
  model: string | null;
9
+ certificate_number?: string | null;
9
10
  }
10
11
 
11
12
  export function parseNameplate(text: string): Nameplate | null {
12
13
  const m = /\{[^{}]*\}/.exec(String(text ?? ""));
13
14
  if (!m) return null;
14
15
  try {
15
- const j = JSON.parse(m[0]) as { manufacturer?: unknown; model?: unknown };
16
+ const j = JSON.parse(m[0]) as { manufacturer?: unknown; model?: unknown; certificate_number?: unknown };
16
17
  const clean = (v: unknown) => (typeof v === "string" && v.trim() ? v.trim().slice(0, 80) : null);
17
- const out = { manufacturer: clean(j.manufacturer), model: clean(j.model) };
18
- return out.manufacturer || out.model ? out : null;
18
+ const out = {
19
+ manufacturer: clean(j.manufacturer),
20
+ model: clean(j.model),
21
+ certificate_number: clean(j.certificate_number),
22
+ };
23
+ return out.manufacturer || out.model || out.certificate_number ? out : null;
19
24
  } catch {
20
25
  return null;
21
26
  }
22
27
  }
23
28
 
24
- /** The register query the extraction feeds: the nameplate's tokens lead,
25
- * the question's family (if named) scopes. */
29
+ /** The register query the extraction feeds: a PRINTED CERTIFICATE NUMBER
30
+ * leads (it names the exact row), then the nameplate's tokens, then the
31
+ * question's family (if named) scopes. */
26
32
  export function nameplateRegisterQuery(np: Nameplate, question: string): string {
27
33
  const fam = /\b([RDMB])\s?-?(\d{2,3})\b/i.exec(question);
28
- return [np.manufacturer, np.model, fam ? `${fam[1]} ${fam[2]}` : null].filter(Boolean).join(" ");
34
+ return [np.certificate_number, np.manufacturer, np.model, fam ? `${fam[1]} ${fam[2]}` : null].filter(Boolean).join(" ");
29
35
  }
@@ -28,9 +28,13 @@ export async function extractNameplate(ai: any, model: string, image: string): P
28
28
  ],
29
29
  // the budget rises with the retry's effort — elevated effort under
30
30
  // a flat budget starves the answer on this model (measured,
31
- // 2026-09-18: 5/38 at medium with the flat budget)
31
+ // 2026-09-18: 5/38 at medium with the flat budget); the
32
+ // temperature is a READING temperature — a transcription task at
33
+ // default sampling hallucinated brands on a low-res plate
34
+ // (observed live 2026-10-03: "Tektronix" on a CAS label)
32
35
  max_tokens: attempt === 0 ? 512 : 1536,
33
36
  reasoning_effort: attempt === 0 ? "low" : "medium",
37
+ temperature: 0.15,
34
38
  });
35
39
  const text = typeof res?.response === "string" ? res.response : res?.choices?.[0]?.message?.content;
36
40
  const np = parseNameplate(text ?? "");