@konneal/engine 0.2.16 → 0.2.18
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.
|
@@ -931,19 +931,40 @@ function cellValue(row, cols, col) {
|
|
|
931
931
|
const raw = row[idx] ?? "";
|
|
932
932
|
return num2(raw) ?? String(raw).trim();
|
|
933
933
|
}
|
|
934
|
-
function
|
|
934
|
+
function classValueExists(rows, cols, token) {
|
|
935
|
+
const cc = classColumn(cols);
|
|
936
|
+
if (!cc) return false;
|
|
937
|
+
const idx = cols.indexOf(cc);
|
|
938
|
+
return rows.some((r) => String(r[idx] ?? "").trim().toLowerCase() === token);
|
|
939
|
+
}
|
|
940
|
+
function statedUnits(query) {
|
|
941
|
+
const out = /* @__PURE__ */ new Set();
|
|
942
|
+
const re = new RegExp(`(${NUM2})\\s*([%\xB0a-zA-Z][a-zA-Z/.%\xB0]*\\b)`, "g");
|
|
943
|
+
for (const m of query.matchAll(re)) {
|
|
944
|
+
if (m[2]) out.add(m[2].replace("\u2062", "").trim());
|
|
945
|
+
}
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
function pickTable(nodes, query) {
|
|
949
|
+
const queryLower = query.toLowerCase();
|
|
950
|
+
const stated = statedUnits(query);
|
|
951
|
+
const cToken = classToken(query);
|
|
935
952
|
let best = null;
|
|
936
953
|
for (const n of nodes) {
|
|
937
954
|
const c = n.content ?? {};
|
|
938
955
|
const payload = c.payload ?? {};
|
|
939
956
|
if (!Array.isArray(payload.rows) || !payload.rows.length) continue;
|
|
957
|
+
const cols = Array.isArray(payload.columns) ? payload.columns : [];
|
|
958
|
+
const rows = (Array.isArray(payload.rows) ? payload.rows : []).filter((r) => Array.isArray(r));
|
|
940
959
|
let score = 0;
|
|
941
960
|
for (const t of tokens(n.node_id.replace("/table/", ""))) {
|
|
942
961
|
if (t.length >= 3 && queryLower.includes(t)) score += 2;
|
|
943
962
|
}
|
|
944
|
-
for (const w of String(c.name ?? "").toLowerCase().split(/[^a-z0-9.]+/)) {
|
|
963
|
+
for (const w of String(c.name ?? c.definition ?? "").toLowerCase().split(/[^a-z0-9.]+/)) {
|
|
945
964
|
if (w.length >= 4 && queryLower.includes(w)) score += 1;
|
|
946
965
|
}
|
|
966
|
+
if (intervalPairs(cols).some((p) => p.low.unit && stated.has(p.low.unit))) score += 3;
|
|
967
|
+
if (cToken && (classAsColumn(cols, cToken) || classValueExists(rows, cols, cToken))) score += 2;
|
|
947
968
|
if (!best || score > best.score) best = { node: n, score };
|
|
948
969
|
}
|
|
949
970
|
if (best && best.score > 0) return best.node;
|
|
@@ -953,14 +974,16 @@ function evaluateAggregation(nodes, query) {
|
|
|
953
974
|
const qLower = query.toLowerCase();
|
|
954
975
|
const operation = /\bhow many\b|\bnumber of\b/.test(qLower) ? "count" : /\b(minimum|smallest|shortest|lowest|least)\b/.test(qLower) ? "min" : /\b(maximum|largest|longest|highest|greatest)\b/.test(qLower) ? "max" : "lookup";
|
|
955
976
|
if (!nodes.length) return null;
|
|
956
|
-
const node = pickTable(nodes,
|
|
977
|
+
const node = pickTable(nodes, query);
|
|
957
978
|
if (!node) return null;
|
|
958
979
|
const content = node.content && typeof node.content === "object" ? node.content : {};
|
|
959
980
|
const payload = content.payload ?? {};
|
|
960
981
|
const cols = Array.isArray(payload.columns) ? payload.columns : [];
|
|
961
982
|
const rows = (Array.isArray(payload.rows) ? payload.rows : []).filter((r) => Array.isArray(r));
|
|
962
983
|
if (!cols.length || !rows.length) return null;
|
|
963
|
-
const
|
|
984
|
+
const rawTitle = String(content.name ?? content.definition ?? node.node_id.replace("/table/", ""));
|
|
985
|
+
const cut = rawTitle.indexOf(" (");
|
|
986
|
+
const tableTitle = cut > 0 ? rawTitle.slice(0, cut) : rawTitle.slice(0, 120);
|
|
964
987
|
const cite = (what) => `COMPUTED (${operation}) \u2014 ${what}, read from the typed table "${tableTitle}" (${node.node_id}). Present this result and cite the table's clause; the value is machine-computed from the table payload, do not recompute or round it differently.`;
|
|
965
988
|
if (operation === "count") {
|
|
966
989
|
const cc2 = classColumn(cols);
|
|
@@ -1843,6 +1866,17 @@ ${summary}` }] : [],
|
|
|
1843
1866
|
const modelNote = boundModel && !boundModel.gated ? modelGroundingBlock(boundModel) : void 0;
|
|
1844
1867
|
const machineVerdict = boundModel && !boundModel.gated ? evaluate(boundModel.content, q.query) : null;
|
|
1845
1868
|
const machineNote = machineVerdict && boundModel ? verdictNote(machineVerdict, boundModel) : void 0;
|
|
1869
|
+
const modelNodeRows = async (kind, docNum) => {
|
|
1870
|
+
const attempt = (num3) => {
|
|
1871
|
+
const sql = num3 ? `SELECT standard, node_id, content FROM model_nodes WHERE kind = '${kind}' AND standard LIKE '%' || ?1` : `SELECT standard, node_id, content FROM model_nodes WHERE kind = '${kind}'`;
|
|
1872
|
+
return num3 ? env.DB.prepare(sql).bind(num3) : env.DB.prepare(sql);
|
|
1873
|
+
};
|
|
1874
|
+
let rows = await attempt(docNum).all().catch(() => ({ results: [] }));
|
|
1875
|
+
if (!(rows.results ?? []).length && docNum && docNum.includes("-")) {
|
|
1876
|
+
rows = await attempt(docNum.replace(/-\d+$/, "")).all().catch(() => ({ results: [] }));
|
|
1877
|
+
}
|
|
1878
|
+
return rows;
|
|
1879
|
+
};
|
|
1846
1880
|
let conditionVerdict = null;
|
|
1847
1881
|
let conditionStandard = null;
|
|
1848
1882
|
if (!machineVerdict && !boundModel && P().publisher.features?.model_plane) {
|
|
@@ -1851,9 +1885,7 @@ ${summary}` }] : [],
|
|
|
1851
1885
|
const stated = quantitiesIn(q.query);
|
|
1852
1886
|
if (severityWord && Object.keys(stated).length >= 1) {
|
|
1853
1887
|
const docNum = modelDocHint?.doc_number;
|
|
1854
|
-
const
|
|
1855
|
-
const stmt = docNum ? env.DB.prepare(sql).bind(docNum) : env.DB.prepare(sql);
|
|
1856
|
-
const rows = await stmt.all().catch(() => ({ results: [] }));
|
|
1888
|
+
const rows = await modelNodeRows("condition_set", docNum);
|
|
1857
1889
|
const candidates = (rows.results ?? []).filter((r) => {
|
|
1858
1890
|
const entry = licensedEntryForPackage(String(r.standard));
|
|
1859
1891
|
return !entry || (standardKeys?.has(entry.key) ?? false);
|
|
@@ -1902,9 +1934,7 @@ ${summary}` }] : [],
|
|
|
1902
1934
|
let aggregationStandard = null;
|
|
1903
1935
|
if (!machineVerdict && !boundModel && !conditionVerdict && P().publisher.features?.model_plane) {
|
|
1904
1936
|
const docNum = modelDocHint?.doc_number;
|
|
1905
|
-
const
|
|
1906
|
-
const stmt = docNum ? env.DB.prepare(sql).bind(docNum) : env.DB.prepare(sql);
|
|
1907
|
-
const rows = await stmt.all().catch(() => ({ results: [] }));
|
|
1937
|
+
const rows = await modelNodeRows("table", docNum);
|
|
1908
1938
|
const candidates = (rows.results ?? []).filter((r) => {
|
|
1909
1939
|
const entry = licensedEntryForPackage(String(r.standard));
|
|
1910
1940
|
return !entry || (standardKeys?.has(entry.key) ?? false);
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
handleMemories,
|
|
8
8
|
scoreJudge,
|
|
9
9
|
standardForDocNumber
|
|
10
|
-
} from "../../chunk-
|
|
10
|
+
} from "../../chunk-PBXE2LFD.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-
|
|
982
|
+
const res = name === "ask" ? await (await import("../../ask-4O753Y4T.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/package.json
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@oimlsmart/oiml-pubid": "^1.2.1"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {},
|
|
37
|
-
"version": "0.2.
|
|
37
|
+
"version": "0.2.18",
|
|
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",
|
|
@@ -140,19 +140,48 @@ function cellValue(row: string[], cols: Column[], col: Column): number | string
|
|
|
140
140
|
return num(raw) ?? String(raw).trim();
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
/** The named class exists as a row VALUE of the table's class column
|
|
144
|
+
* (mpe_tiers' accuracy_class A/B/C/D rows). */
|
|
145
|
+
function classValueExists(rows: string[][], cols: Column[], token: string): boolean {
|
|
146
|
+
const cc = classColumn(cols);
|
|
147
|
+
if (!cc) return false;
|
|
148
|
+
const idx = cols.indexOf(cc);
|
|
149
|
+
return rows.some((r) => String(r[idx] ?? "").trim().toLowerCase() === token);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function statedUnits(query: string): Set<string> {
|
|
153
|
+
const out = new Set<string>();
|
|
154
|
+
const re = new RegExp(`(${NUM})\\s*([%°a-zA-Z][a-zA-Z/.%°]*\\b)`,"g");
|
|
155
|
+
for (const m of query.matchAll(re)) {
|
|
156
|
+
if (m[2]) out.add(m[2].replace("\u2062", "").trim());
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function pickTable(nodes: { node_id: string; content: any }[], query: string): { node_id: string; content: any } | null {
|
|
162
|
+
const queryLower = query.toLowerCase();
|
|
163
|
+
const stated = statedUnits(query);
|
|
164
|
+
const cToken = classToken(query);
|
|
144
165
|
let best: { node: { node_id: string; content: any }; score: number } | null = null;
|
|
145
166
|
for (const n of nodes) {
|
|
146
167
|
const c = n.content ?? {};
|
|
147
168
|
const payload = (c.payload ?? {}) as { columns?: Column[]; rows?: unknown[] };
|
|
148
169
|
if (!Array.isArray(payload.rows) || !payload.rows.length) continue;
|
|
170
|
+
const cols = (Array.isArray(payload.columns) ? payload.columns : []) as Column[];
|
|
171
|
+
const rows = (Array.isArray(payload.rows) ? payload.rows : []).filter((r) => Array.isArray(r)) as string[][];
|
|
149
172
|
let score = 0;
|
|
150
173
|
for (const t of tokens(n.node_id.replace("/table/", ""))) {
|
|
151
174
|
if (t.length >= 3 && queryLower.includes(t)) score += 2;
|
|
152
175
|
}
|
|
153
|
-
|
|
176
|
+
// the D1 projection keeps the title under `definition`
|
|
177
|
+
for (const w of String(c.name ?? c.definition ?? "").toLowerCase().split(/[^a-z0-9.]+/)) {
|
|
154
178
|
if (w.length >= 4 && queryLower.includes(w)) score += 1;
|
|
155
179
|
}
|
|
180
|
+
// the question's stated unit existing as this table's interval unit,
|
|
181
|
+
// and the named class resolving here, are the strongest signals —
|
|
182
|
+
// a table without either cannot hold the lookup
|
|
183
|
+
if (intervalPairs(cols).some((p) => p.low.unit && stated.has(p.low.unit))) score += 3;
|
|
184
|
+
if (cToken && (classAsColumn(cols, cToken) || classValueExists(rows, cols, cToken))) score += 2;
|
|
156
185
|
if (!best || score > best.score) best = { node: n, score };
|
|
157
186
|
}
|
|
158
187
|
// an un-scored pick across several tables would be a guess: refuse
|
|
@@ -177,14 +206,19 @@ export function evaluateAggregation(
|
|
|
177
206
|
? "max"
|
|
178
207
|
: "lookup";
|
|
179
208
|
if (!nodes.length) return null;
|
|
180
|
-
const node = pickTable(nodes,
|
|
209
|
+
const node = pickTable(nodes, query);
|
|
210
|
+
|
|
181
211
|
if (!node) return null;
|
|
182
212
|
const content = (node.content && typeof node.content === "object" ? node.content : {}) as Record<string, any>;
|
|
183
213
|
const payload = (content.payload ?? {}) as { columns?: Column[]; rows?: unknown[] };
|
|
184
214
|
const cols = Array.isArray(payload.columns) ? payload.columns : [];
|
|
185
215
|
const rows = (Array.isArray(payload.rows) ? payload.rows : []).filter((r) => Array.isArray(r)) as string[][];
|
|
186
216
|
if (!cols.length || !rows.length) return null;
|
|
187
|
-
const
|
|
217
|
+
const rawTitle = String(content.name ?? content.definition ?? node.node_id.replace("/table/", ""));
|
|
218
|
+
// display titles are cut before the normative parenthetical and at a
|
|
219
|
+
// word boundary — the full definition stays in the payload
|
|
220
|
+
const cut = rawTitle.indexOf(" (");
|
|
221
|
+
const tableTitle = cut > 0 ? rawTitle.slice(0, cut) : rawTitle.slice(0, 120);
|
|
188
222
|
|
|
189
223
|
const cite = (what: string) =>
|
|
190
224
|
`COMPUTED (${operation}) — ${what}, read from the typed table "${tableTitle}" (${node.node_id}). Present this result and cite the table's clause; the value is machine-computed from the table payload, do not recompute or round it differently.`;
|
|
@@ -725,6 +725,22 @@ async function handleAsk(
|
|
|
725
725
|
// quantities alongside severity vocabulary; the candidate sets come
|
|
726
726
|
// from the model node store (kind condition_set), license-gated like
|
|
727
727
|
// every model lane.
|
|
728
|
+
// the model lanes' node store read: the doc hint joins the STANDARD id
|
|
729
|
+
// (iec-60068-2-78 carries the part; oiml-r60 does not) — when the hint
|
|
730
|
+
// names a part of a part-less package ("R 60-1"), retry on the stem
|
|
731
|
+
const modelNodeRows = async (kind: string, docNum: string | undefined) => {
|
|
732
|
+
const attempt = (num: string | undefined) => {
|
|
733
|
+
const sql = num
|
|
734
|
+
? `SELECT standard, node_id, content FROM model_nodes WHERE kind = '${kind}' AND standard LIKE '%' || ?1`
|
|
735
|
+
: `SELECT standard, node_id, content FROM model_nodes WHERE kind = '${kind}'`;
|
|
736
|
+
return num ? env.DB.prepare(sql).bind(num) : env.DB.prepare(sql);
|
|
737
|
+
};
|
|
738
|
+
let rows = await attempt(docNum).all().catch(() => ({ results: [] }));
|
|
739
|
+
if (!(rows.results ?? []).length && docNum && docNum.includes("-")) {
|
|
740
|
+
rows = await attempt(docNum.replace(/-\d+$/, "")).all().catch(() => ({ results: [] }));
|
|
741
|
+
}
|
|
742
|
+
return rows;
|
|
743
|
+
};
|
|
728
744
|
let conditionVerdict: ConditionVerdict | null = null;
|
|
729
745
|
let conditionStandard: string | null = null;
|
|
730
746
|
if (!machineVerdict && !boundModel && P().publisher.features?.model_plane) {
|
|
@@ -735,11 +751,7 @@ async function handleAsk(
|
|
|
735
751
|
const docNum = modelDocHint?.doc_number;
|
|
736
752
|
// the doc number joins the STANDARD id (iec-60068-2-78), which is
|
|
737
753
|
// the doc number prefixed with the package-family prefix
|
|
738
|
-
const
|
|
739
|
-
? "SELECT standard, node_id, content FROM model_nodes WHERE kind = 'condition_set' AND standard LIKE '%' || ?1"
|
|
740
|
-
: "SELECT standard, node_id, content FROM model_nodes WHERE kind = 'condition_set'";
|
|
741
|
-
const stmt = docNum ? env.DB.prepare(sql).bind(docNum) : env.DB.prepare(sql);
|
|
742
|
-
const rows = await stmt.all().catch(() => ({ results: [] }));
|
|
754
|
+
const rows = await modelNodeRows("condition_set", docNum);
|
|
743
755
|
const candidates = (rows.results ?? []).filter((r: any) => {
|
|
744
756
|
const entry = licensedEntryForPackage(String(r.standard));
|
|
745
757
|
return !entry || (standardKeys?.has(entry.key) ?? false);
|
|
@@ -801,11 +813,7 @@ async function handleAsk(
|
|
|
801
813
|
let aggregationStandard: string | null = null;
|
|
802
814
|
if (!machineVerdict && !boundModel && !conditionVerdict && P().publisher.features?.model_plane) {
|
|
803
815
|
const docNum = modelDocHint?.doc_number;
|
|
804
|
-
const
|
|
805
|
-
? "SELECT standard, node_id, content FROM model_nodes WHERE kind = 'table' AND standard LIKE '%' || ?1"
|
|
806
|
-
: "SELECT standard, node_id, content FROM model_nodes WHERE kind = 'table'";
|
|
807
|
-
const stmt = docNum ? env.DB.prepare(sql).bind(docNum) : env.DB.prepare(sql);
|
|
808
|
-
const rows = await stmt.all().catch(() => ({ results: [] }));
|
|
816
|
+
const rows = await modelNodeRows("table", docNum);
|
|
809
817
|
const candidates = (rows.results ?? []).filter((r: any) => {
|
|
810
818
|
const entry = licensedEntryForPackage(String(r.standard));
|
|
811
819
|
return !entry || (standardKeys?.has(entry.key) ?? false);
|