@konneal/engine 0.2.45 → 0.2.46
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.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ import type { Env } from "./env";
|
|
|
2
2
|
export type { Env };
|
|
3
3
|
import { type ApiKey } from "./lib/http";
|
|
4
4
|
declare function handleAsk(env: Env, ctx: ExecutionContext, req: Request, tier: "anon" | "key" | "member", key: ApiKey | null): Promise<Response>;
|
|
5
|
+
/** RAGAS-style metric battery (G13): judge an (question, answer, passages)
|
|
6
|
+
* triple — faithfulness, answer relevancy, context precision. Driven by
|
|
7
|
+
* tests/eval-suite.mjs; prompts are data; refuses nothing, judges only. */
|
|
5
8
|
export { handleAsk };
|
|
@@ -1743,9 +1743,6 @@ function cacheKeyMaterial(query, lang, salt) {
|
|
|
1743
1743
|
function exactCacheKey(indexVersion, gen, ns, queryHash) {
|
|
1744
1744
|
return `a:${indexVersion}:g${gen}:${ns}:${queryHash}`;
|
|
1745
1745
|
}
|
|
1746
|
-
function semanticCacheKey(indexVersion, gen, signature) {
|
|
1747
|
-
return `sc:${indexVersion}:g${gen}:${signature}`;
|
|
1748
|
-
}
|
|
1749
1746
|
|
|
1750
1747
|
// workers/worker_public/src/certificates.ts
|
|
1751
1748
|
function isRegisterShaped(query) {
|
|
@@ -2093,22 +2090,6 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
2093
2090
|
}
|
|
2094
2091
|
}
|
|
2095
2092
|
let understanding = null;
|
|
2096
|
-
const nodeScoped = !!modelNodeRefIn(q.query) || !!modelNodeRefIn(declaredCtx?.label);
|
|
2097
|
-
if (!cached && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !q.lang && !userImage && !fresh) {
|
|
2098
|
-
const wv0 = await warmEmbed ?? null;
|
|
2099
|
-
if (wv0) {
|
|
2100
|
-
const sc0 = await semanticCacheGet(env, gen, wv0, salt);
|
|
2101
|
-
if (sc0) {
|
|
2102
|
-
console.log("semantic cache hit (pre-understanding)");
|
|
2103
|
-
telemetry(env, ctx, tier, "ask", null, true, sc0.answer.length, sc0.query_hash, q.lang, "semantic", telemetryMeta());
|
|
2104
|
-
const cctx0 = sc0.context_applied ?? NO_CONTEXT;
|
|
2105
|
-
if (wantsStream) {
|
|
2106
|
-
return sseResponse([{ type: "citations", citations: sc0.citations ?? [], context_applied: cctx0 }, { type: "token", v: sc0.answer }, { type: "done", model: sc0.model, query_hash: sc0.query_hash, similar: true, served_from: "similar", context_applied: cctx0 }], corsHeaders(req));
|
|
2107
|
-
}
|
|
2108
|
-
return json({ ...sc0, similar: true, context_applied: cctx0, quota });
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
2093
|
let optimisticVec = null;
|
|
2113
2094
|
let optimisticHits = [];
|
|
2114
2095
|
const t0 = Date.now();
|
|
@@ -2195,26 +2176,6 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
2195
2176
|
const register = await searchRegister(env.DB, q.query);
|
|
2196
2177
|
console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
|
|
2197
2178
|
const regNote = register ? registerNote(register.rows) : void 0;
|
|
2198
|
-
if (understanding?.intent !== "conversational" && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !userImage && !fresh) {
|
|
2199
|
-
const warmVec = await warmEmbed ?? null;
|
|
2200
|
-
if (warmVec) {
|
|
2201
|
-
const sc = await semanticCacheGet(env, gen, warmVec, salt);
|
|
2202
|
-
if (sc) {
|
|
2203
|
-
console.log("semantic cache hit");
|
|
2204
|
-
telemetry(env, ctx, tier, "ask", null, true, sc.answer.length, sc.query_hash, q.lang, "semantic", telemetryMeta());
|
|
2205
|
-
const cctx = sc.context_applied ?? NO_CONTEXT;
|
|
2206
|
-
if (wantsStream) {
|
|
2207
|
-
return sseResponse([
|
|
2208
|
-
...readAs() ? [{ type: "read", read: readAs() }] : [],
|
|
2209
|
-
{ type: "citations", citations: sc.citations ?? [], context_applied: cctx },
|
|
2210
|
-
{ type: "token", v: sc.answer },
|
|
2211
|
-
{ type: "done", model: sc.model, query_hash: sc.query_hash, similar: true, served_from: "similar", context_applied: cctx, read: readAs() }
|
|
2212
|
-
], corsHeaders(req));
|
|
2213
|
-
}
|
|
2214
|
-
return json({ ...sc, similar: true, context_applied: cctx, read: readAs(), quota });
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
2179
|
if (understanding?.intent === "conversational") {
|
|
2219
2180
|
const queryHash2 = await sha256Hex(q.query);
|
|
2220
2181
|
const messages2 = [
|
|
@@ -2628,8 +2589,6 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
2628
2589
|
console.log("anchors:", streamed.violations.length, "of", streamed.total, "unverified \u2014 not caching");
|
|
2629
2590
|
}
|
|
2630
2591
|
if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && !canonical.includes(refusalAnswer())) {
|
|
2631
|
-
const wv = await warmEmbed ?? null;
|
|
2632
|
-
if (wv) semanticCachePut(env, ctx, gen, wv, salt, { answer: canonical, citations: cites, model, query_hash: queryHash });
|
|
2633
2592
|
ctx.waitUntil(
|
|
2634
2593
|
env.CACHE.put(exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt))), JSON.stringify({ answer: canonical, citations: cites, model, query_hash: queryHash }), { expirationTtl: LIMITS.cacheTtlSec })
|
|
2635
2594
|
);
|
|
@@ -2731,10 +2690,6 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
2731
2690
|
const jsonQuality = answerQuality(finalCites.map((c) => c.quality));
|
|
2732
2691
|
const out = { answer, citations: finalCites, ...jsonQuality ? { source_quality: jsonQuality, confidence_note: qualityNote(jsonQuality), ...jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {} } : {}, model: MODELS.member, query_hash: queryHash, follow_ups: understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...verdictBlock ? [verdictBlock] : [], ...conditionBlock ? [conditionBlock] : [], ...aggregationBlock ? [aggregationBlock] : [], ...completionBlocks], context_applied: ctxApplied, ...liveRecords ? { records: liveRecords } : {} };
|
|
2733
2692
|
const cacheable = !contextual && !declaredCtx && !answer.includes(refusalAnswer()) && finalAnchors.violations.length === 0;
|
|
2734
|
-
if (cacheable) {
|
|
2735
|
-
const warmVec = await warmEmbed ?? null;
|
|
2736
|
-
if (warmVec) semanticCachePut(env, ctx, gen, warmVec, salt, out);
|
|
2737
|
-
}
|
|
2738
2693
|
if (cacheable) {
|
|
2739
2694
|
const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt)));
|
|
2740
2695
|
ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
|
|
@@ -2762,36 +2717,6 @@ function sseResponse(events, cors) {
|
|
|
2762
2717
|
}
|
|
2763
2718
|
});
|
|
2764
2719
|
}
|
|
2765
|
-
function cosine(a, b) {
|
|
2766
|
-
let dot = 0;
|
|
2767
|
-
let na = 0;
|
|
2768
|
-
let nb = 0;
|
|
2769
|
-
for (let i = 0; i < a.length; i++) {
|
|
2770
|
-
dot += a[i] * b[i];
|
|
2771
|
-
na += a[i] * a[i];
|
|
2772
|
-
nb += b[i] * b[i];
|
|
2773
|
-
}
|
|
2774
|
-
return dot / (Math.sqrt(na) * Math.sqrt(nb) || 1);
|
|
2775
|
-
}
|
|
2776
|
-
function scSignature(v, salt) {
|
|
2777
|
-
return v.slice(0, 16).map((x) => x.toFixed(2)).join(",") + (salt ? `|s:${salt.length}:${salt.slice(0, 64)}` : "");
|
|
2778
|
-
}
|
|
2779
|
-
async function semanticCacheGet(env, gen, vec, salt) {
|
|
2780
|
-
try {
|
|
2781
|
-
const raw = await env.CACHE.get(semanticCacheKey(env.INDEX_VERSION, gen, scSignature(vec, salt)), "json");
|
|
2782
|
-
if (!raw?.v || !Array.isArray(raw.v) || raw.v.length !== vec.length) return null;
|
|
2783
|
-
if (cosine(raw.v, vec) < 0.97) return null;
|
|
2784
|
-
return raw;
|
|
2785
|
-
} catch {
|
|
2786
|
-
return null;
|
|
2787
|
-
}
|
|
2788
|
-
}
|
|
2789
|
-
function semanticCachePut(env, ctx, gen, vec, salt, payload) {
|
|
2790
|
-
const v = vec.map((x) => Number(x.toFixed(3)));
|
|
2791
|
-
ctx.waitUntil(
|
|
2792
|
-
env.CACHE.put(semanticCacheKey(env.INDEX_VERSION, gen, scSignature(vec, salt)), JSON.stringify({ v, ...payload }), { expirationTtl: LIMITS.cacheTtlSec })
|
|
2793
|
-
);
|
|
2794
|
-
}
|
|
2795
2720
|
|
|
2796
2721
|
export {
|
|
2797
2722
|
handleMemories,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
modelGroundingBlock,
|
|
10
10
|
scoreJudge,
|
|
11
11
|
standardForDocNumber
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-SE67PTJ6.js";
|
|
13
13
|
import {
|
|
14
14
|
buildMessages,
|
|
15
15
|
citations,
|
|
@@ -999,7 +999,7 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
999
999
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
1000
1000
|
body: JSON.stringify({ ...args, stream: false })
|
|
1001
1001
|
});
|
|
1002
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
1002
|
+
const res = name === "ask" ? await (await import("../../ask-6QPD3QA5.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-QZDQVB57.js")).handleSearch(env, ctx, inner, tier, key);
|
|
1003
1003
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
1004
1004
|
});
|
|
1005
1005
|
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.46",
|
|
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",
|
|
@@ -20,7 +20,7 @@ import { contractV2, tableRetyped } from "./refs";
|
|
|
20
20
|
import { completeTables, completeFigures } from "./completion";
|
|
21
21
|
import { NO_CONTEXT, appliedContext, contextNote, namedDocumentIn, parseContext, resolveDocScope, syntheticUnderstanding } from "./context";
|
|
22
22
|
import { liveDataConfig, liveTokenFor, opCfg, opTokenMember, resolveLiveAccount, type LiveRecord } from "./livedata";
|
|
23
|
-
import { bindModelNode, licenseBoundaryNote, licenseBoundaryRefusal, licensedEntryForPackage, modelCitation, modelEcho, modelGroundingBlock,
|
|
23
|
+
import { bindModelNode, licenseBoundaryNote, licenseBoundaryRefusal, licensedEntryForPackage, modelCitation, modelEcho, modelGroundingBlock, standardForDocNumber } from "./modelplane";
|
|
24
24
|
import { evaluate as machineEvaluate, verdictNote } from "./verdict";
|
|
25
25
|
import { evaluateConditionSets, quantitiesIn, type ConditionVerdict } from "./conditions";
|
|
26
26
|
import { evaluateAggregation, type AggregationVerdict } from "./aggregation";
|
|
@@ -31,7 +31,7 @@ import { detectApiCallIntent, prepareApiCall } from "./apicalls";
|
|
|
31
31
|
import { memoryNote } from "./memories";
|
|
32
32
|
import { entitlementScope, resolveRequestScope, requestSalt, standardKeysFrom } from "./requestScope";
|
|
33
33
|
import { rawSessionToken, type SessionClaims } from "./session";
|
|
34
|
-
import { cacheKeyMaterial, corpusGen, exactCacheKey, freshRequested
|
|
34
|
+
import { cacheKeyMaterial, corpusGen, exactCacheKey, freshRequested } from "./answercache";
|
|
35
35
|
import type { Env } from "./env";
|
|
36
36
|
export type { Env };
|
|
37
37
|
import { json, err, corsHeaders, readJson, validateQuery, type ApiKey } from "./lib/http";
|
|
@@ -435,26 +435,6 @@ async function handleAsk(
|
|
|
435
435
|
// fresh already bypassed the exact cache above and bypasses this one.
|
|
436
436
|
let understanding: any = null;
|
|
437
437
|
// a query naming a model node (/req/…, /term/…) is node-SCOPED: its
|
|
438
|
-
// embedding sits near every other node-scoped ask about the same
|
|
439
|
-
// standard, and the single-entry semantic bucket then serves one
|
|
440
|
-
// node's answer for another (observed run-to-run across the golden
|
|
441
|
-
// model legs). Node-scoped queries use the exact cache only.
|
|
442
|
-
const nodeScoped = !!modelNodeRefIn(q.query) || !!modelNodeRefIn(declaredCtx?.label);
|
|
443
|
-
if (!cached && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !q.lang && !userImage && !fresh) {
|
|
444
|
-
const wv0 = (await warmEmbed) ?? null;
|
|
445
|
-
if (wv0) {
|
|
446
|
-
const sc0 = await semanticCacheGet(env, gen, wv0, salt);
|
|
447
|
-
if (sc0) {
|
|
448
|
-
console.log("semantic cache hit (pre-understanding)");
|
|
449
|
-
telemetry(env, ctx, tier, "ask", null, true, sc0.answer.length, sc0.query_hash, q.lang, "semantic", telemetryMeta());
|
|
450
|
-
const cctx0 = sc0.context_applied ?? NO_CONTEXT;
|
|
451
|
-
if (wantsStream) {
|
|
452
|
-
return sseResponse([{ type: "citations", citations: sc0.citations ?? [], context_applied: cctx0 }, { type: "token", v: sc0.answer }, { type: "done", model: sc0.model, query_hash: sc0.query_hash, similar: true, served_from: "similar", context_applied: cctx0 }], corsHeaders(req));
|
|
453
|
-
}
|
|
454
|
-
return json({ ...sc0, similar: true, context_applied: cctx0, quota, });
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
438
|
// ── Option C: optimistic parallel retrieval ──
|
|
459
439
|
// The dense lane (folded-query embed + unfiltered Vectorize query) runs
|
|
460
440
|
// CONCURRENTLY with understanding instead of after it — the serial
|
|
@@ -586,32 +566,6 @@ async function handleAsk(
|
|
|
586
566
|
console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
|
|
587
567
|
const regNote = register ? registerNote(register.rows) : undefined;
|
|
588
568
|
|
|
589
|
-
// semantic cache: near-duplicate of a recently answered question —
|
|
590
|
-
// serves the stored answer with a `similar: true` marker (checked only
|
|
591
|
-
// for standalone knowledge questions; contextual turns, declared-context
|
|
592
|
-
// asks and image asks always run live; fresh regenerates, bypassing
|
|
593
|
-
// this cache too)
|
|
594
|
-
if (understanding?.intent !== "conversational" && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !userImage && !fresh) {
|
|
595
|
-
const warmVec = (await warmEmbed) ?? null;
|
|
596
|
-
if (warmVec) {
|
|
597
|
-
const sc = await semanticCacheGet(env, gen, warmVec, salt);
|
|
598
|
-
if (sc) {
|
|
599
|
-
console.log("semantic cache hit");
|
|
600
|
-
telemetry(env, ctx, tier, "ask", null, true, sc.answer.length, sc.query_hash, q.lang, "semantic", telemetryMeta());
|
|
601
|
-
const cctx = sc.context_applied ?? NO_CONTEXT;
|
|
602
|
-
if (wantsStream) {
|
|
603
|
-
return sseResponse([
|
|
604
|
-
...(readAs() ? [{ type: "read", read: readAs() }] : []),
|
|
605
|
-
{ type: "citations", citations: sc.citations ?? [], context_applied: cctx },
|
|
606
|
-
{ type: "token", v: sc.answer },
|
|
607
|
-
{ type: "done", model: sc.model, query_hash: sc.query_hash, similar: true, served_from: "similar", context_applied: cctx, read: readAs() },
|
|
608
|
-
], corsHeaders(req));
|
|
609
|
-
}
|
|
610
|
-
return json({ ...sc, similar: true, context_applied: cctx, read: readAs(), quota, });
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
569
|
// Conversational route, decided by query UNDERSTANDING (any language, any
|
|
616
570
|
// phrasing) — not string matching. No retrieval: nothing in the corpus
|
|
617
571
|
// answers "who are you". The model speaks for itself from the service
|
|
@@ -1193,8 +1147,6 @@ async function handleAsk(
|
|
|
1193
1147
|
console.log("anchors:", streamed.violations.length, "of", streamed.total, "unverified — not caching");
|
|
1194
1148
|
}
|
|
1195
1149
|
if (streamed.violations.length === 0 && canonical.length > 0 && !contextual && !declaredCtx && !canonical.includes(refusalAnswer())) {
|
|
1196
|
-
const wv = (await warmEmbed) ?? null;
|
|
1197
|
-
if (wv) semanticCachePut(env, ctx, gen, wv, salt, { answer: canonical, citations: cites, model, query_hash: queryHash });
|
|
1198
1150
|
ctx.waitUntil(
|
|
1199
1151
|
env.CACHE.put(exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt))), JSON.stringify({ answer: canonical, citations: cites, model, query_hash: queryHash }), { expirationTtl: LIMITS.cacheTtlSec }),
|
|
1200
1152
|
);
|
|
@@ -1359,10 +1311,6 @@ async function handleAsk(
|
|
|
1359
1311
|
const jsonQuality = answerQuality(finalCites.map((c: any) => c.quality));
|
|
1360
1312
|
const out = { answer, citations: finalCites, ...(jsonQuality ? { source_quality: jsonQuality, confidence_note: qualityNote(jsonQuality), ...(jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {}) } : {}), model: MODELS.member, query_hash: queryHash, follow_ups: understanding?.follow_ups ?? [], blocks: [...c2ns.blocks, ...(verdictBlock ? [verdictBlock] : []), ...(conditionBlock ? [conditionBlock] : []), ...(aggregationBlock ? [aggregationBlock] : []), ...completionBlocks], context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}) };
|
|
1361
1313
|
const cacheable = !contextual && !declaredCtx && !answer.includes(refusalAnswer()) && finalAnchors.violations.length === 0;
|
|
1362
|
-
if (cacheable) {
|
|
1363
|
-
const warmVec = (await warmEmbed) ?? null;
|
|
1364
|
-
if (warmVec) semanticCachePut(env, ctx, gen, warmVec, salt, out);
|
|
1365
|
-
}
|
|
1366
1314
|
if (cacheable) {
|
|
1367
1315
|
const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt)));
|
|
1368
1316
|
ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
|
|
@@ -1404,44 +1352,6 @@ function sseResponse(events: unknown[], cors: Record<string, string>): Response
|
|
|
1404
1352
|
|
|
1405
1353
|
|
|
1406
1354
|
// ── Semantic answer cache (G6) ──
|
|
1407
|
-
// Near-duplicate queries re-pay the whole pipeline. Bucket KV by a
|
|
1408
|
-
// leading-dimension signature of the query embedding; confirm with full
|
|
1409
|
-
// cosine >= 0.97 before serving. Same INDEX_VERSION + corpus-generation
|
|
1410
|
-
// namespace as the answer cache (deploys and corpus surgery invalidate
|
|
1411
|
-
// both). Single entry per bucket (v1): collisions overwrite, never mix.
|
|
1412
|
-
function cosine(a: number[], b: number[]): number {
|
|
1413
|
-
let dot = 0;
|
|
1414
|
-
let na = 0;
|
|
1415
|
-
let nb = 0;
|
|
1416
|
-
for (let i = 0; i < a.length; i++) {
|
|
1417
|
-
dot += a[i] * b[i];
|
|
1418
|
-
na += a[i] * a[i];
|
|
1419
|
-
nb += b[i] * b[i];
|
|
1420
|
-
}
|
|
1421
|
-
return dot / (Math.sqrt(na) * Math.sqrt(nb) || 1);
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
function scSignature(v: number[], salt?: string | null): string {
|
|
1425
|
-
return v.slice(0, 16).map((x) => x.toFixed(2)).join(",") + (salt ? `|s:${salt.length}:${salt.slice(0, 64)}` : "");
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
async function semanticCacheGet(env: Env, gen: string, vec: number[], salt?: string | null): Promise<{ answer: string; citations: unknown[]; model: string; query_hash: string; context_applied?: unknown } | null> {
|
|
1429
|
-
try {
|
|
1430
|
-
const raw = await env.CACHE.get(semanticCacheKey(env.INDEX_VERSION, gen, scSignature(vec, salt)), "json") as any;
|
|
1431
|
-
if (!raw?.v || !Array.isArray(raw.v) || raw.v.length !== vec.length) return null;
|
|
1432
|
-
if (cosine(raw.v, vec) < 0.97) return null;
|
|
1433
|
-
return raw;
|
|
1434
|
-
} catch {
|
|
1435
|
-
return null;
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
function semanticCachePut(env: Env, ctx: ExecutionContext, gen: string, vec: number[], salt: string | null | undefined, payload: { answer: string; citations: unknown[]; model: string; query_hash: string }): void {
|
|
1440
|
-
const v = vec.map((x) => Number(x.toFixed(3)));
|
|
1441
|
-
ctx.waitUntil(
|
|
1442
|
-
env.CACHE.put(semanticCacheKey(env.INDEX_VERSION, gen, scSignature(vec, salt)), JSON.stringify({ v, ...payload }), { expirationTtl: LIMITS.cacheTtlSec }),
|
|
1443
|
-
);
|
|
1444
|
-
}
|
|
1445
1355
|
|
|
1446
1356
|
// ── the route table (TODO.impl/03) ───────────────────────────────────────
|
|
1447
1357
|
|