@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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-BCNSMJ7Q.js";
3
+ } from "./chunk-SE67PTJ6.js";
4
4
  import "./chunk-N2UOENDY.js";
5
5
  import "./chunk-R2V3X6SQ.js";
6
6
  import "./chunk-A3QHHUN5.js";
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-BCNSMJ7Q.js";
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-OOIREJML.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-QZDQVB57.js")).handleSearch(env, ctx, inner, tier, key);
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.45",
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, modelNodeRefIn, standardForDocNumber } from "./modelplane";
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, semanticCacheKey } from "./answercache";
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