@konneal/engine 0.2.10 → 0.2.11

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-ZAEUQFC2.js";
3
+ } from "./chunk-KMSUJI75.js";
4
4
  import "./chunk-7UT76RQO.js";
5
5
  import "./chunk-EFQALN2Z.js";
6
6
  import "./chunk-DBBGOOMZ.js";
@@ -764,39 +764,40 @@ var NUM = String.raw`-?\d+(?:[.,]\d+)?`;
764
764
  function quantitiesIn(query) {
765
765
  const out = {};
766
766
  const num2 = (s) => Number(s.replace(",", "."));
767
- const temp = query.match(new RegExp(`(${NUM})\\s*(?:\xB0\\s*)?(?:C\\b|degC\\b|celsius)`, "i"));
768
- if (temp) out.temperature = num2(temp[1]);
767
+ const put = (kind, stated, stated_unit, si) => {
768
+ if (Number.isFinite(si)) out[kind] = { stated, stated_unit, si };
769
+ };
770
+ const tempC = query.match(new RegExp(`(${NUM})\\s*(?:\xB0\\s*)?C\\b`));
771
+ if (tempC) put("temperature", num2(tempC[1]), "degC", num2(tempC[1]) + 273.15);
772
+ const tempK = query.match(new RegExp(`(${NUM})\\s*K\\b`));
773
+ if (tempK && out.temperature === void 0) put("temperature", num2(tempK[1]), "K", num2(tempK[1]));
769
774
  const rh = query.match(new RegExp(`(${NUM})\\s*%\\s*(?:RH\\b|relative\\s+humidity)?`, "i"));
770
- if (rh) out.relative_humidity = num2(rh[1]);
775
+ if (rh) put("relative_humidity", num2(rh[1]), "%", num2(rh[1]) / 100);
771
776
  const hours = query.match(new RegExp(`(${NUM})\\s*h\\b`, "i"));
772
- if (hours) out.duration = num2(hours[1]);
777
+ if (hours) put("duration", num2(hours[1]), "h", num2(hours[1]) * 3600);
773
778
  const days = query.match(new RegExp(`(${NUM})\\s*days?\\b`, "i"));
774
- if (days && out.duration === void 0) out.duration = num2(days[1]) * 24;
779
+ if (days && out.duration === void 0) put("duration", num2(days[1]), "d", num2(days[1]) * 86400);
775
780
  return out;
776
781
  }
777
- function entryBand(e) {
778
- const value = Number(String(e.value).replace(",", "."));
779
- const tolerance = Number(String(e.tolerance ?? "0").replace(",", "."));
780
- if (!Number.isFinite(value)) return null;
781
- return { value, tolerance: Number.isFinite(tolerance) ? tolerance : 0, lo: value - tolerance, hi: value + tolerance };
782
- }
783
782
  function scoreSet(entries, q) {
784
783
  const checks = [];
785
784
  let distance = 0;
786
785
  let stated = 0;
787
786
  for (const e of entries) {
788
- const statedValue = q[e.quantity_kind];
789
- if (statedValue === void 0) continue;
790
- const band = entryBand(e);
791
- if (!band) continue;
792
- const in_band = statedValue >= band.lo && statedValue <= band.hi;
793
- const gap = Math.max(0, statedValue - band.hi, band.lo - statedValue);
794
- distance += gap / Math.max(band.tolerance, 1);
787
+ const siEntry = e;
788
+ const statedQ = q[e.quantity_kind];
789
+ if (!statedQ || !siEntry?.si) continue;
790
+ const tol = Number(String(siEntry.tolerance ?? "0").replace(",", "."));
791
+ const tolSi = (Number.isFinite(tol) ? tol : 0) * (siEntry.si.unit === "K" ? 1 : siEntry.si.unit === "1" ? 0.01 : 1);
792
+ const in_band = statedQ.si >= siEntry.si.value - tolSi && statedQ.si <= siEntry.si.value + tolSi;
793
+ const gap = Math.max(0, statedQ.si - (siEntry.si.value + tolSi), siEntry.si.value - tolSi - statedQ.si);
794
+ distance += gap / Math.max(tolSi, 1);
795
795
  stated += 1;
796
796
  checks.push({
797
797
  quantity_kind: e.quantity_kind,
798
- band: `${band.value} ${e.unit} \xB1${band.tolerance}`,
799
- stated: statedValue,
798
+ band: `${siEntry.si.value} ${siEntry.si.unit} \xB1${tolSi}`,
799
+ stated: statedQ.stated,
800
+ stated_unit: statedQ.stated_unit,
800
801
  in_band
801
802
  });
802
803
  }
@@ -831,7 +832,7 @@ function evaluateConditionSets(nodes, query) {
831
832
  verdict: "fail",
832
833
  matched: [],
833
834
  nearest: { node_id: nearest.node_id, distance: Number(nearest.distance.toFixed(2)), bands: nearest.checks.map((c) => c.band) },
834
- checks: scored[0].checks,
835
+ checks: nearest.checks,
835
836
  note: `VERDICT: FAIL \u2014 no severity set admits the stated combination. The nearest set is ${nearest.node_id} (bands: ${nearest.checks.map((c) => c.band).join("; ")}). Say the combination is outside the menu and name the nearest set; never soften it.`
836
837
  };
837
838
  }
@@ -2,6 +2,7 @@ export interface ConditionCheck {
2
2
  quantity_kind: string;
3
3
  band: string;
4
4
  stated: number;
5
+ stated_unit: string;
5
6
  in_band: boolean;
6
7
  }
7
8
  export interface ConditionVerdict {
@@ -15,11 +16,14 @@ export interface ConditionVerdict {
15
16
  checks: ConditionCheck[];
16
17
  note: string;
17
18
  }
18
- /** The question's stated quantities, normalized onto the entries'
19
- * quantity kinds by UNIT (degC/K → temperature; % / RH →
20
- * relative_humidity; h/days → duration). Units, never words: "12
21
- * months" is not a 12 h duration. */
22
- export declare function quantitiesIn(query: string): Record<string, number>;
19
+ /** The question's stated quantities with their SI normalization
20
+ * (temperature → K, relative_humidity → the ratio unit, duration → s).
21
+ * Units, never words: "12 months" is not a 12 h duration. */
22
+ export declare function quantitiesIn(query: string): Record<string, {
23
+ stated: number;
24
+ stated_unit: string;
25
+ si: number;
26
+ }>;
23
27
  /** Evaluate the candidate condition_set nodes against the question's
24
28
  * stated quantities. PASS when at least one set admits every stated
25
29
  * quantity within its band (the set is named); FAIL names the nearest
@@ -7,7 +7,7 @@ import {
7
7
  handleMemories,
8
8
  scoreJudge,
9
9
  standardForDocNumber
10
- } from "../../chunk-ZAEUQFC2.js";
10
+ } from "../../chunk-KMSUJI75.js";
11
11
  import {
12
12
  buildMessages,
13
13
  citations,
@@ -979,7 +979,7 @@ async function handleMcp(env, ctx, req, tier, key) {
979
979
  // stream:false forces the JSON lane (anon defaults to SSE)
980
980
  body: JSON.stringify({ ...args, stream: false })
981
981
  });
982
- const res = name === "ask" ? await (await import("../../ask-PKHGS2DZ.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-RFKT7ZZY.js")).handleSearch(env, ctx, inner, tier, key);
982
+ const res = name === "ask" ? await (await import("../../ask-AAEPGPJI.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-RFKT7ZZY.js")).handleSearch(env, ctx, inner, tier, key);
983
983
  return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
984
984
  });
985
985
  if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
package/docs/ADOPTION.md CHANGED
@@ -42,6 +42,15 @@ request cannot reach internal indexes at all: the public surface holds
42
42
  no binding to them, and the internal worker re-verifies every session
43
43
  itself.
44
44
 
45
+ ## Grounded in the literature
46
+
47
+ The execution pattern (a deterministic evaluator computes; the model
48
+ narrates) adopts the program-aided line — PAL (arXiv:2211.10435) and
49
+ Program of Thoughts (arXiv:2211.12588) — and the structured-numeric
50
+ retrieval lessons of TableRAG (arXiv:2410.04739, arXiv:2506.10380).
51
+ The deployment's research notes map every technique to its source and
52
+ to the code path that implements it.
53
+
45
54
  ## The proof
46
55
 
47
56
  Your golden suite gates every promotion: content witnesses, refusal
package/package.json CHANGED
@@ -34,7 +34,7 @@
34
34
  "@oimlsmart/oiml-pubid": "^1.2.1"
35
35
  },
36
36
  "dependencies": {},
37
- "version": "0.2.10",
37
+ "version": "0.2.11",
38
38
  "description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
39
39
  "license": "BSD-3-Clause",
40
40
  "type": "module",
@@ -2,14 +2,16 @@
2
2
  // the test-method packages as machine-verifiable membership. A
3
3
  // condition_set node's payload.entries carry the kernel's quantity
4
4
  // doctrine — value + unit inseparable, tolerance the SPECIFIED band —
5
- // so "is 85 % RH at 40 °C a valid Test Cab severity?" computes: find
6
- // the sets whose temperature AND relative_humidity bands admit the
7
- // stated pair. The verdict is executed data; the model narrates it.
5
+ // and each entry resolves to SI through the package's own quantity
6
+ // register (the export ships si { value, unit }). The stated quantities
7
+ // normalize through the same SI targets, so 313 K and 40 °C are the
8
+ // same stated temperature.
8
9
 
9
10
  export interface ConditionCheck {
10
11
  quantity_kind: string;
11
12
  band: string;
12
13
  stated: number;
14
+ stated_unit: string;
13
15
  in_band: boolean;
14
16
  }
15
17
 
@@ -26,55 +28,62 @@ interface Entry {
26
28
  value: string | number;
27
29
  unit: string;
28
30
  tolerance: string | number;
31
+ si?: { value: number; unit: string };
29
32
  }
30
33
 
31
34
  const NUM = String.raw`-?\d+(?:[.,]\d+)?`;
32
35
 
33
- /** The question's stated quantities, normalized onto the entries'
34
- * quantity kinds by UNIT (degC/K → temperature; % / RH →
35
- * relative_humidity; h/days → duration). Units, never words: "12
36
- * months" is not a 12 h duration. */
37
- export function quantitiesIn(query: string): Record<string, number> {
38
- const out: Record<string, number> = {};
36
+ /** The question's stated quantities with their SI normalization
37
+ * (temperature → K, relative_humidity → the ratio unit, duration → s).
38
+ * Units, never words: "12 months" is not a 12 h duration. */
39
+ export function quantitiesIn(query: string): Record<string, { stated: number; stated_unit: string; si: number }> {
40
+ const out: Record<string, { stated: number; stated_unit: string; si: number }> = {};
39
41
  const num = (s: string) => Number(s.replace(",", "."));
40
- const temp = query.match(new RegExp(`(${NUM})\\s*(?:°\\s*)?(?:C\\b|degC\\b|celsius)`, "i"));
41
- if (temp) out.temperature = num(temp[1]!);
42
+ const put = (kind: string, stated: number, stated_unit: string, si: number) => {
43
+ if (Number.isFinite(si)) out[kind] = { stated, stated_unit, si };
44
+ };
45
+ const tempC = query.match(new RegExp(`(${NUM})\\s*(?:°\\s*)?C\\b`));
46
+ if (tempC) put("temperature", num(tempC[1]!), "degC", num(tempC[1]!) + 273.15);
47
+ const tempK = query.match(new RegExp(`(${NUM})\\s*K\\b`));
48
+ if (tempK && out.temperature === undefined) put("temperature", num(tempK[1]!), "K", num(tempK[1]!));
42
49
  const rh = query.match(new RegExp(`(${NUM})\\s*%\\s*(?:RH\\b|relative\\s+humidity)?`, "i"));
43
- if (rh) out.relative_humidity = num(rh[1]!);
50
+ if (rh) put("relative_humidity", num(rh[1]!), "%", num(rh[1]!) / 100);
44
51
  const hours = query.match(new RegExp(`(${NUM})\\s*h\\b`, "i"));
45
- if (hours) out.duration = num(hours[1]!);
52
+ if (hours) put("duration", num(hours[1]!), "h", num(hours[1]!) * 3600);
46
53
  const days = query.match(new RegExp(`(${NUM})\\s*days?\\b`, "i"));
47
- if (days && out.duration === undefined) out.duration = num(days[1]!) * 24;
54
+ if (days && out.duration === undefined) put("duration", num(days[1]!), "d", num(days[1]!) * 86400);
48
55
  return out;
49
56
  }
50
57
 
51
- function entryBand(e: Entry): { lo: number; hi: number; value: number; tolerance: number } | null {
52
- const value = Number(String(e.value).replace(",", "."));
53
- const tolerance = Number(String(e.tolerance ?? "0").replace(",", "."));
54
- if (!Number.isFinite(value)) return null;
55
- return { value, tolerance: Number.isFinite(tolerance) ? tolerance : 0, lo: value - tolerance, hi: value + tolerance };
58
+ interface SiEntry {
59
+ quantity_kind: string;
60
+ unit: string;
61
+ tolerance: string | number;
62
+ si: { value: number; unit: string };
56
63
  }
57
64
 
58
- /** Evaluate ONE set: every stated quantity in-band? Sets with entries
59
- * whose kind the question never states are skipped for that kind (a
60
- * question stating one quantity does not fail a two-entry set). */
61
- function scoreSet(entries: Entry[], q: Record<string, number>): { checks: ConditionCheck[]; distance: number; stated: number } | null {
65
+ /** Evaluate ONE set: every stated quantity's SI value inside the
66
+ * entry's SI band (the tolerance converts with the factor, never the
67
+ * offset — a difference has no affine part). */
68
+ function scoreSet(entries: (Entry | SiEntry)[], q: Record<string, { stated: number; stated_unit: string; si: number }>): { checks: ConditionCheck[]; distance: number; stated: number } | null {
62
69
  const checks: ConditionCheck[] = [];
63
70
  let distance = 0;
64
71
  let stated = 0;
65
72
  for (const e of entries) {
66
- const statedValue = q[e.quantity_kind];
67
- if (statedValue === undefined) continue;
68
- const band = entryBand(e);
69
- if (!band) continue;
70
- const in_band = statedValue >= band.lo && statedValue <= band.hi;
71
- const gap = Math.max(0, statedValue - band.hi, band.lo - statedValue);
72
- distance += gap / Math.max(band.tolerance, 1);
73
+ const siEntry = e as SiEntry;
74
+ const statedQ = q[e.quantity_kind];
75
+ if (!statedQ || !siEntry?.si) continue;
76
+ const tol = Number(String(siEntry.tolerance ?? "0").replace(",", "."));
77
+ const tolSi = (Number.isFinite(tol) ? tol : 0) * (siEntry.si.unit === "K" ? 1 : siEntry.si.unit === "1" ? 0.01 : 1);
78
+ const in_band = statedQ.si >= siEntry.si.value - tolSi && statedQ.si <= siEntry.si.value + tolSi;
79
+ const gap = Math.max(0, statedQ.si - (siEntry.si.value + tolSi), siEntry.si.value - tolSi - statedQ.si);
80
+ distance += gap / Math.max(tolSi, 1);
73
81
  stated += 1;
74
82
  checks.push({
75
83
  quantity_kind: e.quantity_kind,
76
- band: `${band.value} ${e.unit} ±${band.tolerance}`,
77
- stated: statedValue,
84
+ band: `${siEntry.si.value} ${siEntry.si.unit} ±${tolSi}`,
85
+ stated: statedQ.stated,
86
+ stated_unit: statedQ.stated_unit,
78
87
  in_band,
79
88
  });
80
89
  }
@@ -95,7 +104,7 @@ export function evaluateConditionSets(
95
104
  const scored: { node_id: string; checks: ConditionCheck[]; distance: number }[] = [];
96
105
  for (const n of nodes) {
97
106
  const c = (n.content && typeof n.content === "object" ? n.content : {}) as Record<string, any>;
98
- const payload = (c.payload ?? {}) as { role?: string; entries?: Entry[] };
107
+ const payload = (c.payload ?? {}) as { entries?: (Entry | SiEntry)[] };
99
108
  const entries = Array.isArray(payload.entries) ? payload.entries : [];
100
109
  if (!entries.length) continue;
101
110
  const s = scoreSet(entries, q);
@@ -117,7 +126,7 @@ export function evaluateConditionSets(
117
126
  verdict: "fail",
118
127
  matched: [],
119
128
  nearest: { node_id: nearest.node_id, distance: Number(nearest.distance.toFixed(2)), bands: nearest.checks.map((c) => c.band) },
120
- checks: scored[0]!.checks,
129
+ checks: nearest.checks,
121
130
  note: `VERDICT: FAIL — no severity set admits the stated combination. The nearest set is ${nearest.node_id} (bands: ${nearest.checks.map((c) => c.band).join("; ")}). Say the combination is outside the menu and name the nearest set; never soften it.`,
122
131
  };
123
132
  }