@konneal/engine 0.2.71 → 0.2.73
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-OUNG3JIC.js → ask-MFFOHVQ2.js} +3 -3
- package/dist/{certificates-DNRUOYRF.js → certificates-EMHFAUWC.js} +1 -1
- package/dist/{chunk-VGVTIPFW.js → chunk-3IRX4B7Q.js} +1 -1
- package/dist/{chunk-TPV5GPY2.js → chunk-LL3LWBZH.js} +73 -42
- package/dist/{chunk-FLNX4O7C.js → chunk-OXTRNMTZ.js} +1 -1
- package/dist/{chunk-EOQVQBS6.js → chunk-V7PEVNNO.js} +94 -16
- package/dist/operations.d.ts +4 -0
- package/dist/quota.d.ts +8 -0
- package/dist/{search-PBOODCMQ.js → search-LN5Q7OSZ.js} +2 -2
- package/dist/worker_public/src/index.js +5 -5
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +46 -7
- package/workers/worker_public/src/certificates.ts +1 -1
- package/workers/worker_public/src/operations.ts +44 -12
- package/workers/worker_public/src/quota.ts +42 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleAsk
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-V7PEVNNO.js";
|
|
4
|
+
import "./chunk-LL3LWBZH.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-3IRX4B7Q.js";
|
|
10
10
|
import "./chunk-3FYJM7LH.js";
|
|
11
11
|
export {
|
|
12
12
|
handleAsk
|
|
@@ -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.ommisa.org/cert-pdf/${r.pdf_key}`);
|
|
125
125
|
return links.length ? `The certificates' documents:
|
|
126
126
|
${links.join("\n")}` : "";
|
|
127
127
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
THRESHOLDS,
|
|
10
10
|
answerEffort,
|
|
11
11
|
effortBudget,
|
|
12
|
+
num,
|
|
12
13
|
processExpansion,
|
|
13
14
|
sha256Hex,
|
|
14
15
|
today
|
|
@@ -817,8 +818,8 @@ var BaseUrnGenerator = class {
|
|
|
817
818
|
const ed = this.maybe("edition");
|
|
818
819
|
if (ed === void 0 || ed === null)
|
|
819
820
|
return void 0;
|
|
820
|
-
const
|
|
821
|
-
return
|
|
821
|
+
const num2 = typeof ed === "object" && "number" in ed ? ed.number : ed;
|
|
822
|
+
return num2 === void 0 || num2 === null || num2 === "" ? void 0 : `ed.${String(num2)}`;
|
|
822
823
|
}
|
|
823
824
|
urnLanguage() {
|
|
824
825
|
const langs = this.maybe("languages");
|
|
@@ -7139,9 +7140,9 @@ var IsoIdentifier = class _IsoIdentifier extends BaseIdentifier {
|
|
|
7139
7140
|
}
|
|
7140
7141
|
}
|
|
7141
7142
|
}
|
|
7142
|
-
const
|
|
7143
|
-
if (
|
|
7144
|
-
result += result === "" ?
|
|
7143
|
+
const num2 = this.numberPortion(true);
|
|
7144
|
+
if (num2 !== "")
|
|
7145
|
+
result += result === "" ? num2 : ` ${num2}`;
|
|
7145
7146
|
result += this.languagePortion(this.languageSingle());
|
|
7146
7147
|
if (this.all_parts === true)
|
|
7147
7148
|
result += " (all parts)";
|
|
@@ -7306,9 +7307,9 @@ var IsoSingleIdentifier = class extends IsoIdentifier {
|
|
|
7306
7307
|
result += `${sep}${stage}`;
|
|
7307
7308
|
}
|
|
7308
7309
|
}
|
|
7309
|
-
const
|
|
7310
|
-
if (
|
|
7311
|
-
result += result === "" ?
|
|
7310
|
+
const num2 = this.numberPortion(true);
|
|
7311
|
+
if (num2 !== "")
|
|
7312
|
+
result += result === "" ? num2 : ` ${num2}`;
|
|
7312
7313
|
result += this.languagePortion(this.languageSingle());
|
|
7313
7314
|
if (this.all_parts === true)
|
|
7314
7315
|
result += " (all parts)";
|
|
@@ -7611,11 +7612,11 @@ var IsoSupplementIdentifier = class extends IsoSingleIdentifier {
|
|
|
7611
7612
|
const ts = this.typedStageState;
|
|
7612
7613
|
const stage = ts === void 0 ? "" : stageAbbr(ts.entry, long, ts.originalAbbr);
|
|
7613
7614
|
let result = `${baseStr}/${stage}`;
|
|
7614
|
-
const
|
|
7615
|
-
if (
|
|
7616
|
-
if (!
|
|
7615
|
+
const num2 = this.numberPortion(true);
|
|
7616
|
+
if (num2 !== "") {
|
|
7617
|
+
if (!num2.startsWith(":") && !stage.endsWith("."))
|
|
7617
7618
|
result += " ";
|
|
7618
|
-
result +=
|
|
7619
|
+
result += num2;
|
|
7619
7620
|
}
|
|
7620
7621
|
result += this.languagePortion(this.languageSingle());
|
|
7621
7622
|
return result;
|
|
@@ -7670,20 +7671,20 @@ var IsoDirectives = class extends IsoSingleIdentifier {
|
|
|
7670
7671
|
const abbr = this.typedStageState === void 0 ? "" : stageAbbr(this.typedStageState.entry, false);
|
|
7671
7672
|
if (abbr !== "")
|
|
7672
7673
|
head += ` ${abbr}`;
|
|
7673
|
-
let
|
|
7674
|
+
let num2 = this.number ?? "";
|
|
7674
7675
|
if (this.part !== void 0)
|
|
7675
|
-
|
|
7676
|
+
num2 += ` ${this.part}`;
|
|
7676
7677
|
if (this.subpart !== void 0)
|
|
7677
|
-
|
|
7678
|
+
num2 += `-${this.subpart}`;
|
|
7678
7679
|
if (this.stage_iteration !== void 0)
|
|
7679
|
-
|
|
7680
|
+
num2 += `.${this.stage_iteration.render() ?? ""}`;
|
|
7680
7681
|
if (this.date !== void 0 && this.date.present())
|
|
7681
|
-
|
|
7682
|
-
if (
|
|
7682
|
+
num2 += `:${this.date.render()}`;
|
|
7683
|
+
if (num2 === "")
|
|
7683
7684
|
return head;
|
|
7684
|
-
if (
|
|
7685
|
-
return head +
|
|
7686
|
-
return `${head} ${
|
|
7685
|
+
if (num2.startsWith(":"))
|
|
7686
|
+
return head + num2;
|
|
7687
|
+
return `${head} ${num2}`;
|
|
7687
7688
|
}
|
|
7688
7689
|
};
|
|
7689
7690
|
registerType(IsoDirectives);
|
|
@@ -8606,9 +8607,9 @@ function preprocessNist(input) {
|
|
|
8606
8607
|
cleaned = cleaned.replace(/(?<!e)(\d)(rev\d{4})/g, "$1 $2");
|
|
8607
8608
|
cleaned = cleaned.replace(/(\d+e\d+)\.([A-Za-z]{3,9}\d{4})/g, "$1rev$2");
|
|
8608
8609
|
if (/\bIR\b/.test(cleaned) && !cleaned.includes("CIRC")) {
|
|
8609
|
-
cleaned = cleaned.replace(/(\d)r(\d{1,2})\/(\d{2,4})/g, (_m,
|
|
8610
|
+
cleaned = cleaned.replace(/(\d)r(\d{1,2})\/(\d{2,4})/g, (_m, num2, mon, yr) => {
|
|
8610
8611
|
const yyyy = yr.length === 2 ? `19${yr}` : yr;
|
|
8611
|
-
return `${
|
|
8612
|
+
return `${num2}/Upd1-${yyyy}${mon.padStart(2, "0")}`;
|
|
8612
8613
|
});
|
|
8613
8614
|
}
|
|
8614
8615
|
if (!cleaned.includes("LCIRC") && !cleaned.includes("CIRC")) {
|
|
@@ -11321,17 +11322,17 @@ function normalizeJointStageSpellings(input) {
|
|
|
11321
11322
|
let cleaned = input;
|
|
11322
11323
|
const pubs = "((?:ISO/IEC/IEEE|IEEE/ISO/IEC|IEEE/IEC/ISO|ISO/IEEE|IEC/IEEE|IEEE/IEC|ISO/IEC)(?:/ ?| ))";
|
|
11323
11324
|
const stage = "(FDIS|FCD|CDV|DIS\\d?|CD\\d?|WD|PWI|NP)";
|
|
11324
|
-
const
|
|
11325
|
+
const num2 = "(P?\\d+(?:[.-]\\d+)?)";
|
|
11325
11326
|
const tail = "(,? (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)[a-z]* \\d{4}|$|-\\d{4}(?:-\\d\\d)?)";
|
|
11326
11327
|
cleaned = cleaned.replace(/(\d)_D(\d)/g, "$1/D$2");
|
|
11327
11328
|
cleaned = cleaned.replace(/,(?=[A-Za-z])/, ", ");
|
|
11328
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${
|
|
11329
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${
|
|
11330
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${
|
|
11331
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${
|
|
11332
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${
|
|
11333
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${
|
|
11334
|
-
cleaned = cleaned.replace(new RegExp(`^${pubs}${stage} ${
|
|
11329
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${num2}/(D[\\d.]+)/ ?${stage}\\b`), "$1$4 $2/$3");
|
|
11330
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${num2}/ ${stage}${tail}`), "$1$3 $2$4");
|
|
11331
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${num2}/${stage} (\\d{4})\\b`), "$1$3 $2-$4");
|
|
11332
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${num2} ${stage}${tail}`), "$1$3 $2$4");
|
|
11333
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${num2} ${stage} (D\\d+)\\b`), "$1$3 $2/$4");
|
|
11334
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${num2}/ ?${stage}(-(?:19|20)\\d\\d(?:-\\d\\d)?)`), "$1$3 $2$4");
|
|
11335
|
+
cleaned = cleaned.replace(new RegExp(`^${pubs}${stage} ${num2}/? ?(D\\d+)\\b`), "$1$2 $3/$4");
|
|
11335
11336
|
return cleaned;
|
|
11336
11337
|
}
|
|
11337
11338
|
function preprocessIeee(input) {
|
|
@@ -14556,8 +14557,8 @@ var CenBuilder = class {
|
|
|
14556
14557
|
const numStr = (str3(d["number"]) ?? "").trim();
|
|
14557
14558
|
if (!/^\d{1,6}$/.test(numStr))
|
|
14558
14559
|
return void 0;
|
|
14559
|
-
const
|
|
14560
|
-
if (!(
|
|
14560
|
+
const num2 = Number.parseInt(numStr, 10);
|
|
14561
|
+
if (!(num2 >= 6e4 && num2 <= 79999))
|
|
14561
14562
|
return void 0;
|
|
14562
14563
|
const impl = grammarImplementation("iec");
|
|
14563
14564
|
if (impl === void 0)
|
|
@@ -19006,9 +19007,9 @@ var CsaAdoptedClass = (() => {
|
|
|
19006
19007
|
baseStr = baseStr.replace(`:${year}`, `:${year.slice(2)}`);
|
|
19007
19008
|
}
|
|
19008
19009
|
}
|
|
19009
|
-
baseStr = baseStr.replace(/\/Amd\s+(\d+)([:/-])(\d{2,4})\b/g, (_m,
|
|
19010
|
+
baseStr = baseStr.replace(/\/Amd\s+(\d+)([:/-])(\d{2,4})\b/g, (_m, num2, sep, year) => {
|
|
19010
19011
|
const short = year.length === 4 && (year.startsWith("20") || year.startsWith("19")) ? year.slice(2) : year;
|
|
19011
|
-
return `/A${
|
|
19012
|
+
return `/A${num2}${sep}${short}`;
|
|
19012
19013
|
});
|
|
19013
19014
|
let result;
|
|
19014
19015
|
if (this.publisher_prefix !== void 0 && this.publisher_prefix.endsWith("-")) {
|
|
@@ -19635,9 +19636,9 @@ function parseCsa(input) {
|
|
|
19635
19636
|
const full = Number(two) < 50 ? `20${two}` : `19${two}`;
|
|
19636
19637
|
wrapped = wrapped.replace(new RegExp(`:${two}\\b`), `:${full}`);
|
|
19637
19638
|
}
|
|
19638
|
-
wrapped = wrapped.replace(/\/A(\d+)([:/-])(\d{2,4})\b/g, (_m,
|
|
19639
|
+
wrapped = wrapped.replace(/\/A(\d+)([:/-])(\d{2,4})\b/g, (_m, num2, sep, year) => {
|
|
19639
19640
|
const full = year.length === 2 ? Number(year) < 50 ? `20${year}` : `19${year}` : year;
|
|
19640
|
-
return `/Amd ${
|
|
19641
|
+
return `/Amd ${num2}${sep}${full}`;
|
|
19641
19642
|
});
|
|
19642
19643
|
const base = parseExternalStandard(wrapped);
|
|
19643
19644
|
if (base === void 0) {
|
|
@@ -21908,13 +21909,13 @@ function parseOimlSpine(display) {
|
|
|
21908
21909
|
const kind = String(h._type ?? "").split(":").pop() ?? "";
|
|
21909
21910
|
const letter = TYPE_LETTER[kind] ?? "";
|
|
21910
21911
|
if (!letter) return null;
|
|
21911
|
-
const
|
|
21912
|
+
const num2 = String(Number(h.number));
|
|
21912
21913
|
const part = h.part !== void 0 ? String(h.part) : void 0;
|
|
21913
21914
|
const ed = h.year !== void 0 ? String(h.year) : h.edition !== void 0 ? String(h.edition) : void 0;
|
|
21914
21915
|
return {
|
|
21915
|
-
doc_number:
|
|
21916
|
+
doc_number: num2,
|
|
21916
21917
|
...ed ? { edition: ed } : {},
|
|
21917
|
-
label: `OIML ${letter} ${
|
|
21918
|
+
label: `OIML ${letter} ${num2}${part ? `-${part}` : ""}${ed ? `:${ed}` : ""}`
|
|
21918
21919
|
};
|
|
21919
21920
|
} catch {
|
|
21920
21921
|
return null;
|
|
@@ -21949,9 +21950,9 @@ var oimlPubid = {
|
|
|
21949
21950
|
for (const m of query.matchAll(re)) {
|
|
21950
21951
|
const [, oimlPrefix, letter, gap, digits, edition] = m;
|
|
21951
21952
|
if (digits.length === 1 && !oimlPrefix && !gap) continue;
|
|
21952
|
-
const
|
|
21953
|
+
const num2 = String(Number(digits));
|
|
21953
21954
|
const type = letter.toUpperCase();
|
|
21954
|
-
return { doc_number:
|
|
21955
|
+
return { doc_number: num2, ...edition ? { edition } : {}, label: `OIML ${type} ${num2}${edition ? `:${edition}` : ""}` };
|
|
21955
21956
|
}
|
|
21956
21957
|
return null;
|
|
21957
21958
|
},
|
|
@@ -24578,6 +24579,32 @@ function telemetry(env, ctx, tier, route, model, ok, answerChars, queryHash, lan
|
|
|
24578
24579
|
])
|
|
24579
24580
|
);
|
|
24580
24581
|
}
|
|
24582
|
+
function estimateTokens(chars) {
|
|
24583
|
+
return Math.ceil(chars / 4);
|
|
24584
|
+
}
|
|
24585
|
+
function tokenTier(bucketId) {
|
|
24586
|
+
return bucketId.startsWith("key:") ? "key" : bucketId.startsWith("sub:") ? "member" : "anon";
|
|
24587
|
+
}
|
|
24588
|
+
function tokenLimit(env, bucketId) {
|
|
24589
|
+
const tier = tokenTier(bucketId);
|
|
24590
|
+
return tier === "key" ? num(env, "KEY_DAY_TOKENS", 4e6) : tier === "member" ? num(env, "MEMBER_DAY_TOKENS", 15e5) : num(env, "ANON_DAY_TOKENS", 3e5);
|
|
24591
|
+
}
|
|
24592
|
+
async function tokenBudget(env, bucketId) {
|
|
24593
|
+
const used = Number(await env.CACHE.get(`t:${today()}:ask:${await sha256Hex(bucketId)}`) ?? "0");
|
|
24594
|
+
return { used, limit: tokenLimit(env, bucketId) };
|
|
24595
|
+
}
|
|
24596
|
+
async function chargeTokens(env, bucketId, tokens) {
|
|
24597
|
+
if (!(tokens > 0)) return;
|
|
24598
|
+
await kvIncr(env.CACHE, `t:${today()}:ask:${await sha256Hex(bucketId)}`, tokens);
|
|
24599
|
+
}
|
|
24600
|
+
function usageTotal(usage) {
|
|
24601
|
+
if (!usage || typeof usage !== "object") return null;
|
|
24602
|
+
const u = usage;
|
|
24603
|
+
const p = Number(u.prompt_tokens ?? 0);
|
|
24604
|
+
const c = Number(u.completion_tokens ?? 0);
|
|
24605
|
+
const total = p + c;
|
|
24606
|
+
return Number.isFinite(total) && total > 0 ? total : null;
|
|
24607
|
+
}
|
|
24581
24608
|
|
|
24582
24609
|
// workers/worker_public/src/graph.ts
|
|
24583
24610
|
function docNumberOf(nodeId) {
|
|
@@ -24662,6 +24689,10 @@ export {
|
|
|
24662
24689
|
clientIp,
|
|
24663
24690
|
checkQuota,
|
|
24664
24691
|
telemetry,
|
|
24692
|
+
estimateTokens,
|
|
24693
|
+
tokenBudget,
|
|
24694
|
+
chargeTokens,
|
|
24695
|
+
usageTotal,
|
|
24665
24696
|
graphExpand,
|
|
24666
24697
|
editionNote
|
|
24667
24698
|
};
|
|
@@ -3,12 +3,14 @@ import {
|
|
|
3
3
|
answerQuality,
|
|
4
4
|
appliedContext,
|
|
5
5
|
buildMessages,
|
|
6
|
+
chargeTokens,
|
|
6
7
|
checkQuota,
|
|
7
8
|
citations,
|
|
8
9
|
clientIp,
|
|
9
10
|
contextNote,
|
|
10
11
|
editionNote,
|
|
11
12
|
embed,
|
|
13
|
+
estimateTokens,
|
|
12
14
|
experimentalSourceLabels,
|
|
13
15
|
generateOnce,
|
|
14
16
|
graphExpand,
|
|
@@ -34,8 +36,10 @@ import {
|
|
|
34
36
|
splitHistory,
|
|
35
37
|
syntheticUnderstanding,
|
|
36
38
|
telemetry,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
tokenBudget,
|
|
40
|
+
understandQuery,
|
|
41
|
+
usageTotal
|
|
42
|
+
} from "./chunk-LL3LWBZH.js";
|
|
39
43
|
import {
|
|
40
44
|
corsHeaders,
|
|
41
45
|
err,
|
|
@@ -68,7 +72,7 @@ import {
|
|
|
68
72
|
certificateLinks,
|
|
69
73
|
registerNote,
|
|
70
74
|
searchRegister
|
|
71
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-3IRX4B7Q.js";
|
|
72
76
|
import {
|
|
73
77
|
P
|
|
74
78
|
} from "./chunk-3FYJM7LH.js";
|
|
@@ -1892,7 +1896,7 @@ async function runTool(db, call) {
|
|
|
1892
1896
|
if (call.name !== "register_search") return null;
|
|
1893
1897
|
const query = String(call.args?.query ?? "").trim().slice(0, 160);
|
|
1894
1898
|
if (!query) return null;
|
|
1895
|
-
const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-
|
|
1899
|
+
const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-EMHFAUWC.js");
|
|
1896
1900
|
const reg = await searchRegister2(db, query, true);
|
|
1897
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.`;
|
|
1898
1902
|
return { name: call.name, query, output };
|
|
@@ -1929,17 +1933,59 @@ var ACTION_STEMS = [
|
|
|
1929
1933
|
"announc",
|
|
1930
1934
|
"download"
|
|
1931
1935
|
];
|
|
1936
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
1937
|
+
"which",
|
|
1938
|
+
"what",
|
|
1939
|
+
"where",
|
|
1940
|
+
"when",
|
|
1941
|
+
"how",
|
|
1942
|
+
"why",
|
|
1943
|
+
"who",
|
|
1944
|
+
"does",
|
|
1945
|
+
"did",
|
|
1946
|
+
"can",
|
|
1947
|
+
"the",
|
|
1948
|
+
"and",
|
|
1949
|
+
"for",
|
|
1950
|
+
"with",
|
|
1951
|
+
"that",
|
|
1952
|
+
"this",
|
|
1953
|
+
"are",
|
|
1954
|
+
"was",
|
|
1955
|
+
"were",
|
|
1956
|
+
"has",
|
|
1957
|
+
"have",
|
|
1958
|
+
"had",
|
|
1959
|
+
"not",
|
|
1960
|
+
"but",
|
|
1961
|
+
"all",
|
|
1962
|
+
"any",
|
|
1963
|
+
"platform",
|
|
1964
|
+
"operation",
|
|
1965
|
+
"operations",
|
|
1966
|
+
"endpoint",
|
|
1967
|
+
"endpoints",
|
|
1968
|
+
"api"
|
|
1969
|
+
]);
|
|
1970
|
+
function anchorWords(anchor) {
|
|
1971
|
+
return anchor.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
1972
|
+
}
|
|
1973
|
+
var singular = (w) => w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w;
|
|
1932
1974
|
function matchOperations(query, rows) {
|
|
1933
1975
|
const q = query.toLowerCase();
|
|
1934
1976
|
const stems = ACTION_STEMS.filter((s) => q.includes(s));
|
|
1935
|
-
|
|
1977
|
+
const objects = query.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 2 && !STOP_WORDS.has(w)).map(singular);
|
|
1936
1978
|
const scored = [];
|
|
1937
1979
|
for (const r of rows) {
|
|
1938
1980
|
const hay = `${r.anchor} ${r.text}`.toLowerCase();
|
|
1939
|
-
const
|
|
1940
|
-
|
|
1981
|
+
const verbScore = stems.filter((s) => hay.includes(s)).length * 3;
|
|
1982
|
+
const words = anchorWords(r.anchor).map(singular);
|
|
1983
|
+
const objectScore = objects.filter((o) => words.includes(o)).length * 10;
|
|
1984
|
+
if (objectScore + verbScore > 0) scored.push({ ...r, score: objectScore + verbScore, objectScore });
|
|
1941
1985
|
}
|
|
1942
|
-
|
|
1986
|
+
const named = scored.filter((s) => s.objectScore > 0);
|
|
1987
|
+
const pool = named.length ? named : scored;
|
|
1988
|
+
return pool.sort((a, b) => b.score - a.score).slice(0, 4);
|
|
1943
1989
|
}
|
|
1944
1990
|
function catalogNote(anchors) {
|
|
1945
1991
|
if (!anchors.length) return "";
|
|
@@ -1949,12 +1995,12 @@ ${anchors.map((a) => `- ${a}`).join("\n")}`;
|
|
|
1949
1995
|
async function operationsCatalogNote(db, query) {
|
|
1950
1996
|
if (!isOperationIntent(query)) return void 0;
|
|
1951
1997
|
try {
|
|
1952
|
-
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1,
|
|
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 ?? [];
|
|
1953
1999
|
const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
|
|
1954
2000
|
const matched = matchOperations(query, opRows);
|
|
1955
2001
|
const parts = [];
|
|
1956
2002
|
if (matched.length) {
|
|
1957
|
-
parts.push(`Operations whose description matches the question
|
|
2003
|
+
parts.push(`Operations whose name or description matches the question (AUTHORITATIVE \u2014 if one matches, the answer MUST name it and cite its line; never answer that the information is missing):
|
|
1958
2004
|
${matched.map((m) => `- ${m.text}`).join("\n")}`);
|
|
1959
2005
|
}
|
|
1960
2006
|
parts.push(catalogNote(opRows.map((r) => r.anchor)));
|
|
@@ -2124,7 +2170,23 @@ async function summarizeHistory(env, model, turns) {
|
|
|
2124
2170
|
return null;
|
|
2125
2171
|
}
|
|
2126
2172
|
}
|
|
2127
|
-
|
|
2173
|
+
function messageChars(messages) {
|
|
2174
|
+
let n = 0;
|
|
2175
|
+
for (const m of messages) {
|
|
2176
|
+
if (typeof m.content === "string") n += m.content.length;
|
|
2177
|
+
else if (Array.isArray(m.content)) {
|
|
2178
|
+
for (const part of m.content) if (part?.type === "text" && typeof part.text === "string") n += part.text.length;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
return n;
|
|
2182
|
+
}
|
|
2183
|
+
async function chargeAnswerTokens(env, bucketId, stats, promptChars, answerChars) {
|
|
2184
|
+
const total = usageTotal(stats?.usage) ?? estimateTokens(promptChars + answerChars);
|
|
2185
|
+
await chargeTokens(env, bucketId, total);
|
|
2186
|
+
const budget = await tokenBudget(env, bucketId);
|
|
2187
|
+
return { tokens_used: budget.used, token_limit: budget.limit };
|
|
2188
|
+
}
|
|
2189
|
+
async function* sseTokens(stream, stats) {
|
|
2128
2190
|
const reader = stream.getReader();
|
|
2129
2191
|
const decoder = new TextDecoder();
|
|
2130
2192
|
let buf = "";
|
|
@@ -2142,6 +2204,7 @@ async function* sseTokens(stream) {
|
|
|
2142
2204
|
try {
|
|
2143
2205
|
const evt = JSON.parse(payload);
|
|
2144
2206
|
const tok = typeof evt?.response === "string" ? evt.response : evt?.choices?.[0]?.delta?.content;
|
|
2207
|
+
if (evt.usage && stats && stats.usage === void 0) stats.usage = evt.usage;
|
|
2145
2208
|
if (tok) yield tok;
|
|
2146
2209
|
} catch {
|
|
2147
2210
|
}
|
|
@@ -2185,6 +2248,12 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
2185
2248
|
if (!quota.ok) {
|
|
2186
2249
|
return err(429, "quota_exceeded", `Daily question limit reached (${quota.limit}). Try again tomorrow.`);
|
|
2187
2250
|
}
|
|
2251
|
+
const tokBudget = await tokenBudget(env, bucketId);
|
|
2252
|
+
if (tokBudget.used >= tokBudget.limit) {
|
|
2253
|
+
return err(429, "quota_exceeded", `Daily token budget reached (${tokBudget.limit} tokens). Try again tomorrow.`);
|
|
2254
|
+
}
|
|
2255
|
+
quota.tokens_used = tokBudget.used;
|
|
2256
|
+
quota.token_limit = tokBudget.limit;
|
|
2188
2257
|
const hardCap = num(env, "ANON_DAY_HARD_CAP", 5e3);
|
|
2189
2258
|
if (tier === "anon" && quota.used > hardCap) {
|
|
2190
2259
|
return err(503, "generation_disabled", "Generation is temporarily paused; search remains available.");
|
|
@@ -2402,14 +2471,17 @@ ${summary}` }] : [],
|
|
|
2402
2471
|
`));
|
|
2403
2472
|
if (readAs()) send({ type: "read", read: readAs() });
|
|
2404
2473
|
send({ type: "citations", citations: [], context_applied: NO_CONTEXT, quota });
|
|
2474
|
+
const stats = {};
|
|
2405
2475
|
let full = "";
|
|
2406
2476
|
try {
|
|
2407
|
-
for await (const tok of sseTokens(stream)) {
|
|
2477
|
+
for await (const tok of sseTokens(stream, stats)) {
|
|
2408
2478
|
full += tok;
|
|
2409
2479
|
send({ type: "token", v: tok });
|
|
2410
2480
|
}
|
|
2411
2481
|
} catch {
|
|
2412
2482
|
}
|
|
2483
|
+
const tq2 = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages2), full.length);
|
|
2484
|
+
send({ type: "quota", quota: { ...quota, ...tq2 } });
|
|
2413
2485
|
send({ type: "done", model, query_hash: queryHash2, context_applied: NO_CONTEXT, read: readAs() });
|
|
2414
2486
|
telemetry(env, ctx, tier, "ask", model, true, full.length, queryHash2, q.lang, void 0, telemetryMeta());
|
|
2415
2487
|
controller.close();
|
|
@@ -2465,7 +2537,8 @@ ${summary}` }] : [],
|
|
|
2465
2537
|
corsHeaders(req)
|
|
2466
2538
|
);
|
|
2467
2539
|
}
|
|
2468
|
-
|
|
2540
|
+
const tq2 = await chargeAnswerTokens(env, bucketId, void 0, q.query.length, verdict.answer.length);
|
|
2541
|
+
return json({ answer: verdict.answer, citations: citations2, model, query_hash: queryHash2, follow_ups: [], context_applied: draftCtxApplied, read: readAs(), ...draftPayload ? { draft: draftPayload } : {}, quota: { ...quota, ...tq2 } });
|
|
2469
2542
|
}
|
|
2470
2543
|
if (apiCallIntent) {
|
|
2471
2544
|
const callCtxApplied = declaredCtx ? appliedContext(declaredCtx, null) : NO_CONTEXT;
|
|
@@ -2492,7 +2565,8 @@ ${summary}` }] : [],
|
|
|
2492
2565
|
corsHeaders(req)
|
|
2493
2566
|
);
|
|
2494
2567
|
}
|
|
2495
|
-
|
|
2568
|
+
const tq2 = await chargeAnswerTokens(env, bucketId, void 0, q.query.length, verdict.answer.length);
|
|
2569
|
+
return json({ answer: verdict.answer, citations: [], model, query_hash: queryHash2, follow_ups: [], context_applied: callCtxApplied, read: readAs(), ...draftPayload ? { draft: draftPayload } : {}, quota: { ...quota, ...tq2 } });
|
|
2496
2570
|
}
|
|
2497
2571
|
let liveRecords;
|
|
2498
2572
|
let accountNote;
|
|
@@ -2792,14 +2866,17 @@ ${q.query}` }],
|
|
|
2792
2866
|
send({ type: "read", read: readAs() });
|
|
2793
2867
|
const sourceQuality = answerQuality(cites.map((c) => c.quality));
|
|
2794
2868
|
send({ type: "citations", citations: cites, context_applied: ctxApplied, ...sourceQuality ? { source_quality: sourceQuality, confidence_note: qualityNote(sourceQuality), ...sourceQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(cites) } : {} } : {}, ...liveRecords ? { records: liveRecords } : {}, quota });
|
|
2869
|
+
const stats = {};
|
|
2795
2870
|
let full = "";
|
|
2796
2871
|
try {
|
|
2797
|
-
for await (const tok of sseTokens(stream)) {
|
|
2872
|
+
for await (const tok of sseTokens(stream, stats)) {
|
|
2798
2873
|
full += tok;
|
|
2799
2874
|
send({ type: "token", v: tok });
|
|
2800
2875
|
}
|
|
2801
2876
|
} catch {
|
|
2802
2877
|
}
|
|
2878
|
+
const tq2 = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages), full.length);
|
|
2879
|
+
send({ type: "quota", quota: { ...quota, ...tq2 } });
|
|
2803
2880
|
const canonical0 = canonicalRefusal(full);
|
|
2804
2881
|
const c2 = canonical0.includes(refusalAnswer()) ? { text: canonical0, blocks: [], dropped: [] } : await contractV2(env.DB, canonical0, usedHits);
|
|
2805
2882
|
send({
|
|
@@ -2965,7 +3042,8 @@ ${q.query}` }],
|
|
|
2965
3042
|
text: h.text.slice(0, 1200),
|
|
2966
3043
|
...h.metadata.table_selection ? { sel: h.metadata.table_selection } : {}
|
|
2967
3044
|
}));
|
|
2968
|
-
|
|
3045
|
+
const tq = await chargeAnswerTokens(env, bucketId, void 0, messageChars(messages), answer.length);
|
|
3046
|
+
return json({ ...out, context: contextOut, read: readAs(), quota: { ...quota, ...tq } }, 200, { ...corsHeaders(req), "server-timing": serverTiming() });
|
|
2969
3047
|
}
|
|
2970
3048
|
function sseResponse(events, cors) {
|
|
2971
3049
|
const encoder = new TextEncoder();
|
package/dist/operations.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export declare function isOperationIntent(query: string): boolean;
|
|
2
|
+
/** camelCase/snake anchors meet the question's words: "updates an
|
|
3
|
+
* entity record" must reach putEntityRecord even when its description
|
|
4
|
+
* words the action differently ("replaces the stored…"). */
|
|
5
|
+
export declare function anchorWords(anchor: string): string[];
|
|
2
6
|
export declare function matchOperations(query: string, rows: {
|
|
3
7
|
anchor: string;
|
|
4
8
|
text: string;
|
package/dist/quota.d.ts
CHANGED
|
@@ -15,3 +15,11 @@ export declare function telemetry(env: Env, ctx: Background, tier: string, route
|
|
|
15
15
|
keyId?: string | null;
|
|
16
16
|
retries?: number;
|
|
17
17
|
}): void;
|
|
18
|
+
export declare function estimateTokens(chars: number): number;
|
|
19
|
+
export declare function tokenLimit(env: Env, bucketId: string): number;
|
|
20
|
+
export declare function tokenBudget(env: Env, bucketId: string): Promise<{
|
|
21
|
+
used: number;
|
|
22
|
+
limit: number;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function chargeTokens(env: Env, bucketId: string, tokens: number): Promise<void>;
|
|
25
|
+
export declare function usageTotal(usage: unknown): number | null;
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
modelGroundingBlock,
|
|
7
7
|
scoreFaithfulness,
|
|
8
8
|
scoreJudge
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-V7PEVNNO.js";
|
|
10
10
|
import {
|
|
11
11
|
handleSearch
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-OXTRNMTZ.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-LL3LWBZH.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-3IRX4B7Q.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-MFFOHVQ2.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LN5Q7OSZ.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.73",
|
|
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",
|
|
@@ -37,7 +37,7 @@ import { cacheKeyMaterial, corpusGen, exactCacheKey, freshRequested } from "./an
|
|
|
37
37
|
import type { Env } from "./env";
|
|
38
38
|
export type { Env };
|
|
39
39
|
import { json, err, corsHeaders, readJson, validateQuery, type ApiKey } from "./lib/http";
|
|
40
|
-
import {
|
|
40
|
+
import { chargeTokens, checkQuota, clientIp, estimateTokens, telemetry, tokenBudget, usageTotal } from "./quota";
|
|
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";
|
|
@@ -206,7 +206,28 @@ async function summarizeHistory(
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
/** The prompt's character weight — text parts only, image data URLs
|
|
210
|
+
* excluded (a photo must not read as half a million characters). */
|
|
211
|
+
function messageChars(messages: any[]): number {
|
|
212
|
+
let n = 0;
|
|
213
|
+
for (const m of messages) {
|
|
214
|
+
if (typeof m.content === "string") n += m.content.length;
|
|
215
|
+
else if (Array.isArray(m.content)) for (const part of m.content) if (part?.type === "text" && typeof part.text === "string") n += part.text.length;
|
|
216
|
+
}
|
|
217
|
+
return n;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Post-generation true-up: the model's own usage when the stream
|
|
221
|
+
* reported it, the character estimate otherwise — charged, then the
|
|
222
|
+
* budget's fresh state returned for the quota payload. */
|
|
223
|
+
async function chargeAnswerTokens(env: Env, bucketId: string, stats: { usage?: unknown } | undefined, promptChars: number, answerChars: number): Promise<{ tokens_used: number; token_limit: number }> {
|
|
224
|
+
const total = usageTotal(stats?.usage) ?? estimateTokens(promptChars + answerChars);
|
|
225
|
+
await chargeTokens(env, bucketId, total);
|
|
226
|
+
const budget = await tokenBudget(env, bucketId);
|
|
227
|
+
return { tokens_used: budget.used, token_limit: budget.limit };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function* sseTokens(stream: ReadableStream<Uint8Array>, stats?: { usage?: unknown }): AsyncGenerator<string> {
|
|
210
231
|
const reader = stream.getReader();
|
|
211
232
|
const decoder = new TextDecoder();
|
|
212
233
|
let buf = "";
|
|
@@ -227,6 +248,7 @@ async function* sseTokens(stream: ReadableStream<Uint8Array>): AsyncGenerator<st
|
|
|
227
248
|
typeof evt?.response === "string"
|
|
228
249
|
? evt.response
|
|
229
250
|
: evt?.choices?.[0]?.delta?.content;
|
|
251
|
+
if (evt.usage && stats && stats.usage === undefined) stats.usage = evt.usage;
|
|
230
252
|
if (tok) yield tok;
|
|
231
253
|
} catch {
|
|
232
254
|
// partial JSON in line splitting — ignore
|
|
@@ -320,6 +342,14 @@ async function handleAsk(
|
|
|
320
342
|
if (!quota.ok) {
|
|
321
343
|
return err(429, "quota_exceeded", `Daily question limit reached (${quota.limit}). Try again tomorrow.`);
|
|
322
344
|
}
|
|
345
|
+
// the real allowance is the day's TOKENS (the 2026-09-29 shift): the
|
|
346
|
+
// request cap above is the anti-abuse backstop, this is the budget
|
|
347
|
+
const tokBudget = await tokenBudget(env, bucketId);
|
|
348
|
+
if (tokBudget.used >= tokBudget.limit) {
|
|
349
|
+
return err(429, "quota_exceeded", `Daily token budget reached (${tokBudget.limit} tokens). Try again tomorrow.`);
|
|
350
|
+
}
|
|
351
|
+
(quota as any).tokens_used = tokBudget.used;
|
|
352
|
+
(quota as any).token_limit = tokBudget.limit;
|
|
323
353
|
|
|
324
354
|
const hardCap = num(env as any, "ANON_DAY_HARD_CAP", 5000);
|
|
325
355
|
if (tier === "anon" && quota.used > hardCap) {
|
|
@@ -668,15 +698,18 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
668
698
|
// the reading arrives first — a conversational turn is still a reading
|
|
669
699
|
if (readAs()) send({ type: "read", read: readAs() });
|
|
670
700
|
send({ type: "citations", citations: [], context_applied: NO_CONTEXT, quota, });
|
|
701
|
+
const stats: { usage?: unknown } = {};
|
|
671
702
|
let full = "";
|
|
672
703
|
try {
|
|
673
|
-
for await (const tok of sseTokens(stream)) {
|
|
704
|
+
for await (const tok of sseTokens(stream, stats)) {
|
|
674
705
|
full += tok;
|
|
675
706
|
send({ type: "token", v: tok });
|
|
676
707
|
}
|
|
677
708
|
} catch {
|
|
678
709
|
// stream ended prematurely — deliver what we have
|
|
679
710
|
}
|
|
711
|
+
const tq = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages), full.length);
|
|
712
|
+
send({ type: "quota", quota: { ...quota, ...tq } });
|
|
680
713
|
send({ type: "done", model, query_hash: queryHash, context_applied: NO_CONTEXT, read: readAs() });
|
|
681
714
|
telemetry(env, ctx, tier, "ask", model, true, full.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
682
715
|
controller.close();
|
|
@@ -748,7 +781,8 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
748
781
|
corsHeaders(req),
|
|
749
782
|
);
|
|
750
783
|
}
|
|
751
|
-
|
|
784
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, verdict.answer.length);
|
|
785
|
+
return json({ answer: verdict.answer, citations, model, query_hash: queryHash, follow_ups: [], context_applied: draftCtxApplied, read: readAs(), ...(draftPayload ? { draft: draftPayload } : {}), quota: { ...quota, ...tq } });
|
|
752
786
|
}
|
|
753
787
|
|
|
754
788
|
// ── The api_call draft (TODO.ai-platform/09) — the operations
|
|
@@ -785,7 +819,8 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
785
819
|
corsHeaders(req),
|
|
786
820
|
);
|
|
787
821
|
}
|
|
788
|
-
|
|
822
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, verdict.answer.length);
|
|
823
|
+
return json({ answer: verdict.answer, citations: [], model, query_hash: queryHash, follow_ups: [], context_applied: callCtxApplied, read: readAs(), ...(draftPayload ? { draft: draftPayload } : {}), quota: { ...quota, ...tq } });
|
|
789
824
|
}
|
|
790
825
|
|
|
791
826
|
// (declared before the retrieval try: the account block, the refusal
|
|
@@ -1227,15 +1262,18 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1227
1262
|
send({ type: "read", read: readAs() });
|
|
1228
1263
|
const sourceQuality = answerQuality(cites.map((c: any) => c.quality));
|
|
1229
1264
|
send({ type: "citations", citations: cites, context_applied: ctxApplied, ...(sourceQuality ? { source_quality: sourceQuality, confidence_note: qualityNote(sourceQuality), ...(sourceQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(cites) } : {}) } : {}), ...(liveRecords ? { records: liveRecords } : {}), quota, });
|
|
1265
|
+
const stats: { usage?: unknown } = {};
|
|
1230
1266
|
let full = "";
|
|
1231
1267
|
try {
|
|
1232
|
-
for await (const tok of sseTokens(stream)) {
|
|
1268
|
+
for await (const tok of sseTokens(stream, stats)) {
|
|
1233
1269
|
full += tok;
|
|
1234
1270
|
send({ type: "token", v: tok });
|
|
1235
1271
|
}
|
|
1236
1272
|
} catch {
|
|
1237
1273
|
// stream ended prematurely — deliver what we have
|
|
1238
1274
|
}
|
|
1275
|
+
const tq = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages), full.length);
|
|
1276
|
+
send({ type: "quota", quota: { ...quota, ...tq } });
|
|
1239
1277
|
const canonical0 = canonicalRefusal(full);
|
|
1240
1278
|
// answer contract v2: validate [[u:]] refs, resolve typed blocks
|
|
1241
1279
|
const c2 = canonical0.includes(refusalAnswer())
|
|
@@ -1475,7 +1513,8 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1475
1513
|
text: h.text.slice(0, 1200),
|
|
1476
1514
|
...((h.metadata as any).table_selection ? { sel: (h.metadata as any).table_selection } : {}),
|
|
1477
1515
|
}));
|
|
1478
|
-
|
|
1516
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, messageChars(messages), answer.length);
|
|
1517
|
+
return json({ ...out, context: contextOut, read: readAs(), quota: { ...quota, ...tq } }, 200, { ...corsHeaders(req), "server-timing": serverTiming() });
|
|
1479
1518
|
}
|
|
1480
1519
|
|
|
1481
1520
|
function sseResponse(events: unknown[], cors: Record<string, string>): Response {
|
|
@@ -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.ommisa.org/cert-pdf/${r.pdf_key}`);
|
|
138
138
|
return links.length ? `The certificates' documents:\n${links.join("\n")}` : "";
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// The operations catalog lane (TODO.new-era/6's platform surface): the
|
|
2
2
|
// smart-ops corpus is SMALL (169 operations) and name-addressable. An
|
|
3
|
-
// operation-intent question gets (1) the operations whose
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// name the operation from it,
|
|
7
|
-
//
|
|
3
|
+
// operation-intent question gets (1) the operations whose NAME carries
|
|
4
|
+
// the question's object words and whose description carries its action
|
|
5
|
+
// stems — with their full lines, and (2) the complete name index. The
|
|
6
|
+
// note is AUTHORITATIVE: the model must name the operation from it,
|
|
7
|
+
// never answer from the passages' silence. Pure composition lives
|
|
8
|
+
// here; the D1 call is a thin wrapper.
|
|
8
9
|
|
|
9
10
|
export function isOperationIntent(query: string): boolean {
|
|
10
11
|
return /\b(operations?|endpoints?)\b/i.test(query);
|
|
@@ -18,17 +19,48 @@ const ACTION_STEMS = [
|
|
|
18
19
|
"confirm", "approv", "declin", "decide", "revok", "grant", "notify", "advanc", "announc", "download",
|
|
19
20
|
];
|
|
20
21
|
|
|
22
|
+
const STOP_WORDS = new Set([
|
|
23
|
+
"which", "what", "where", "when", "how", "why", "who", "does", "did", "can", "the", "and", "for",
|
|
24
|
+
"with", "that", "this", "are", "was", "were", "has", "have", "had", "not", "but", "all", "any",
|
|
25
|
+
"platform", "operation", "operations", "endpoint", "endpoints", "api",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
/** camelCase/snake anchors meet the question's words: "updates an
|
|
29
|
+
* entity record" must reach putEntityRecord even when its description
|
|
30
|
+
* words the action differently ("replaces the stored…"). */
|
|
31
|
+
export function anchorWords(anchor: string): string[] {
|
|
32
|
+
return anchor
|
|
33
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.split(/[^a-z0-9]+/)
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const singular = (w: string) => (w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w);
|
|
40
|
+
|
|
21
41
|
export function matchOperations(query: string, rows: { anchor: string; text: string }[]): { anchor: string; text: string }[] {
|
|
22
42
|
const q = query.toLowerCase();
|
|
23
43
|
const stems = ACTION_STEMS.filter((s) => q.includes(s));
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
const objects = query
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.split(/[^a-z0-9]+/)
|
|
47
|
+
.filter((w) => w.length > 2 && !STOP_WORDS.has(w))
|
|
48
|
+
.map(singular);
|
|
49
|
+
const scored: { anchor: string; text: string; score: number; objectScore: number }[] = [];
|
|
26
50
|
for (const r of rows) {
|
|
27
51
|
const hay = `${r.anchor} ${r.text}`.toLowerCase();
|
|
28
|
-
const
|
|
29
|
-
|
|
52
|
+
const verbScore = stems.filter((s) => hay.includes(s)).length * 3;
|
|
53
|
+
const words = anchorWords(r.anchor).map(singular);
|
|
54
|
+
const objectScore = objects.filter((o) => words.includes(o)).length * 10;
|
|
55
|
+
if (objectScore + verbScore > 0) scored.push({ ...r, score: objectScore + verbScore, objectScore });
|
|
30
56
|
}
|
|
31
|
-
|
|
57
|
+
// A question that names its object ("entity record") has found its
|
|
58
|
+
// target by name — verb-only mentions are the noise that displaced
|
|
59
|
+
// the answer with an unrelated sibling (the 2026-09-29 "blob upload"
|
|
60
|
+
// answers).
|
|
61
|
+
const named = scored.filter((s) => s.objectScore > 0);
|
|
62
|
+
const pool = named.length ? named : scored;
|
|
63
|
+
return pool.sort((a, b) => b.score - a.score).slice(0, 4);
|
|
32
64
|
}
|
|
33
65
|
|
|
34
66
|
export function catalogNote(anchors: string[]): string {
|
|
@@ -39,12 +71,12 @@ export function catalogNote(anchors: string[]): string {
|
|
|
39
71
|
export async function operationsCatalogNote(db: any, query: string): Promise<string | undefined> {
|
|
40
72
|
if (!isOperationIntent(query)) return undefined;
|
|
41
73
|
try {
|
|
42
|
-
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1,
|
|
74
|
+
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 ?? [];
|
|
43
75
|
const opRows = (rows as any[]).map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
|
|
44
76
|
const matched = matchOperations(query, opRows);
|
|
45
77
|
const parts: string[] = [];
|
|
46
78
|
if (matched.length) {
|
|
47
|
-
parts.push(`Operations whose description matches the question
|
|
79
|
+
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")}`);
|
|
48
80
|
}
|
|
49
81
|
parts.push(catalogNote(opRows.map((r) => r.anchor)));
|
|
50
82
|
return parts.filter(Boolean).join("\n\n") || undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Quota + telemetry: per-bucket daily counters (KV) and the D1
|
|
2
2
|
// queries/spend ledger writes (TODO.impl/23).
|
|
3
|
-
import { sha256Hex, today } from "./config";
|
|
3
|
+
import { num, sha256Hex, today } from "./config.ts";
|
|
4
4
|
import type { Env } from "./env";
|
|
5
5
|
import type { Background } from "./ports/runtime.ts";
|
|
6
6
|
|
|
@@ -54,3 +54,44 @@ export function telemetry(
|
|
|
54
54
|
]),
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
// ── the token budget (2026-09-29): the daily allowance is TOKENS, not
|
|
59
|
+
// questions — an elevated-effort answer over a wide retrieval context
|
|
60
|
+
// costs orders of magnitude more than a cached refusal, and one number
|
|
61
|
+
// should say so. Charged after generation from the model's own usage
|
|
62
|
+
// when the stream reports it, estimated by characters otherwise.
|
|
63
|
+
export function estimateTokens(chars: number): number {
|
|
64
|
+
return Math.ceil(chars / 4);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function tokenTier(bucketId: string): "anon" | "member" | "key" {
|
|
68
|
+
return bucketId.startsWith("key:") ? "key" : bucketId.startsWith("sub:") ? "member" : "anon";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function tokenLimit(env: Env, bucketId: string): number {
|
|
72
|
+
const tier = tokenTier(bucketId);
|
|
73
|
+
return tier === "key"
|
|
74
|
+
? num(env as any, "KEY_DAY_TOKENS", 4_000_000)
|
|
75
|
+
: tier === "member"
|
|
76
|
+
? num(env as any, "MEMBER_DAY_TOKENS", 1_500_000)
|
|
77
|
+
: num(env as any, "ANON_DAY_TOKENS", 300_000);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function tokenBudget(env: Env, bucketId: string): Promise<{ used: number; limit: number }> {
|
|
81
|
+
const used = Number((await env.CACHE.get(`t:${today()}:ask:${await sha256Hex(bucketId)}`)) ?? "0");
|
|
82
|
+
return { used, limit: tokenLimit(env, bucketId) };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function chargeTokens(env: Env, bucketId: string, tokens: number): Promise<void> {
|
|
86
|
+
if (!(tokens > 0)) return;
|
|
87
|
+
await kvIncr(env.CACHE, `t:${today()}:ask:${await sha256Hex(bucketId)}`, tokens);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function usageTotal(usage: unknown): number | null {
|
|
91
|
+
if (!usage || typeof usage !== "object") return null;
|
|
92
|
+
const u = usage as any;
|
|
93
|
+
const p = Number(u.prompt_tokens ?? 0);
|
|
94
|
+
const c = Number(u.completion_tokens ?? 0);
|
|
95
|
+
const total = p + c;
|
|
96
|
+
return Number.isFinite(total) && total > 0 ? total : null;
|
|
97
|
+
}
|