@konneal/engine 0.2.31 → 0.2.33

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,7 +1,7 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-TDKNQPA2.js";
4
- import "./chunk-F66NYFUK.js";
3
+ } from "./chunk-WSLADX7O.js";
4
+ import "./chunk-NHHQAGBW.js";
5
5
  import "./chunk-2RKYO3OC.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-JGKSUSF5.js";
@@ -7,7 +7,7 @@ import {
7
7
  sessionFrom,
8
8
  telemetry,
9
9
  understandQuery
10
- } from "./chunk-F66NYFUK.js";
10
+ } from "./chunk-NHHQAGBW.js";
11
11
  import {
12
12
  corsHeaders,
13
13
  err,
@@ -24373,8 +24373,8 @@ function telemetry(env, ctx, tier, route, model, ok, answerChars, queryHash, lan
24373
24373
  ctx.waitUntil(
24374
24374
  env.DB.batch([
24375
24375
  env.DB.prepare(
24376
- "INSERT INTO queries (ts, day, tier, route, model, ok, answer_chars, query_hash, lang, cache, duration_ms, key_id) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12)"
24377
- ).bind((/* @__PURE__ */ new Date()).toISOString(), day, tier, route, model, ok ? 1 : 0, answerChars, queryHash, lang ?? null, cache ?? null, meta?.durationMs ?? null, meta?.keyId ?? null),
24376
+ "INSERT INTO queries (ts, day, tier, route, model, ok, answer_chars, query_hash, lang, cache, duration_ms, key_id, retries) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13)"
24377
+ ).bind((/* @__PURE__ */ new Date()).toISOString(), day, tier, route, model, ok ? 1 : 0, answerChars, queryHash, lang ?? null, cache ?? null, meta?.durationMs ?? null, meta?.keyId ?? null, meta?.retries ?? 0),
24378
24378
  env.DB.prepare(
24379
24379
  "INSERT INTO spend (day, tier, model, requests) VALUES (?1,?2,?3,1) ON CONFLICT(day, tier, model) DO UPDATE SET requests = requests + 1"
24380
24380
  ).bind(day, tier, model ?? "none")
@@ -28,7 +28,7 @@ import {
28
28
  syntheticUnderstanding,
29
29
  telemetry,
30
30
  understandQuery
31
- } from "./chunk-F66NYFUK.js";
31
+ } from "./chunk-NHHQAGBW.js";
32
32
  import {
33
33
  corsHeaders,
34
34
  err,
@@ -1612,7 +1612,7 @@ async function* sseTokens(stream) {
1612
1612
  }
1613
1613
  async function handleAsk(env, ctx, req, tier, key) {
1614
1614
  const tStart = Date.now();
1615
- const telemetryMeta = () => ({ durationMs: Date.now() - tStart, keyId: key?.id ?? null });
1615
+ const telemetryMeta = () => ({ durationMs: Date.now() - tStart, keyId: key?.id ?? null, retries: generateRetries });
1616
1616
  const stageTiming = {};
1617
1617
  let generateRetries = 0;
1618
1618
  const readAs = () => understanding ? {
package/dist/quota.d.ts CHANGED
@@ -13,4 +13,5 @@ export declare function checkQuota(env: Env, bucket: string, id: string, limit:
13
13
  export declare function telemetry(env: Env, ctx: Background, tier: string, route: string, model: string | null, ok: boolean, answerChars: number, queryHash: string, lang?: string, cache?: "exact" | "semantic", meta?: {
14
14
  durationMs?: number;
15
15
  keyId?: string | null;
16
+ retries?: number;
16
17
  }): void;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handleSearch
3
- } from "./chunk-S7NLWPNX.js";
4
- import "./chunk-F66NYFUK.js";
3
+ } from "./chunk-5LSEBH3J.js";
4
+ import "./chunk-NHHQAGBW.js";
5
5
  import "./chunk-2RKYO3OC.js";
6
6
  import "./chunk-A3QHHUN5.js";
7
7
  import "./chunk-JGKSUSF5.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleSearch
3
- } from "../../chunk-S7NLWPNX.js";
3
+ } from "../../chunk-5LSEBH3J.js";
4
4
  import {
5
5
  bindModelNode,
6
6
  checkQuoteAnchors,
@@ -9,7 +9,7 @@ import {
9
9
  modelGroundingBlock,
10
10
  scoreJudge,
11
11
  standardForDocNumber
12
- } from "../../chunk-TDKNQPA2.js";
12
+ } from "../../chunk-WSLADX7O.js";
13
13
  import {
14
14
  buildMessages,
15
15
  citations,
@@ -32,7 +32,7 @@ import {
32
32
  sessionFrom,
33
33
  telemetry,
34
34
  understandQuery
35
- } from "../../chunk-F66NYFUK.js";
35
+ } from "../../chunk-NHHQAGBW.js";
36
36
  import {
37
37
  authenticate,
38
38
  corsHeaders,
@@ -996,7 +996,7 @@ async function handleMcp(env, ctx, req, tier, key) {
996
996
  // stream:false forces the JSON lane (anon defaults to SSE)
997
997
  body: JSON.stringify({ ...args, stream: false })
998
998
  });
999
- const res = name === "ask" ? await (await import("../../ask-43C2WZZO.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-NHTT5DDI.js")).handleSearch(env, ctx, inner, tier, key);
999
+ const res = name === "ask" ? await (await import("../../ask-GU554Y45.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LGDQR43R.js")).handleSearch(env, ctx, inner, tier, key);
1000
1000
  return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
1001
1001
  });
1002
1002
  if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
@@ -1382,14 +1382,16 @@ async function adminStatsRoute(c) {
1382
1382
  if (!env.ADMIN_TOKEN) return err(501, "admin_disabled", "ADMIN_TOKEN secret is not configured");
1383
1383
  const auth = req.headers.get("authorization") ?? "";
1384
1384
  if (auth !== `Bearer ${env.ADMIN_TOKEN}`) return err(401, "unauthorized", "Invalid admin token");
1385
- const [byDay, byModel, feedback, convCount, cacheMix, durations] = await Promise.all([
1385
+ const [byDay, byModel, feedback, convCount, cacheMix, durations, retriesRow] = await Promise.all([
1386
1386
  env.DB.prepare("SELECT day, tier, COUNT(*) as n, SUM(ok) as ok FROM queries WHERE day >= date('now','-7 days') GROUP BY day, tier ORDER BY day DESC").all(),
1387
1387
  env.DB.prepare("SELECT model, SUM(requests) as requests FROM spend WHERE day >= date('now','-7 days') GROUP BY model ORDER BY requests DESC").all(),
1388
1388
  env.DB.prepare("SELECT rating, COUNT(*) as n FROM feedback GROUP BY rating").all(),
1389
1389
  env.DB.prepare("SELECT COUNT(*) as n FROM conversations").first(),
1390
1390
  env.DB.prepare("SELECT COALESCE(cache, 'miss') AS cache, COUNT(*) AS n FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' GROUP BY cache").all(),
1391
- env.DB.prepare("SELECT duration_ms FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' AND duration_ms IS NOT NULL").all()
1391
+ env.DB.prepare("SELECT duration_ms FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' AND duration_ms IS NOT NULL").all(),
1392
+ env.DB.prepare("SELECT COALESCE(SUM(retries), 0) AS total, SUM(CASE WHEN retries > 0 THEN 1 ELSE 0 END) AS answers FROM queries WHERE day >= date('now','-7 days') AND route = 'ask'").all()
1392
1393
  ]);
1394
+ const retries = retriesRow?.results?.[0] ?? { total: 0, answers: 0 };
1393
1395
  const ds = durations.results.map((r) => r.duration_ms).sort((a, b) => a - b);
1394
1396
  const pct = (q) => ds.length ? ds[Math.min(ds.length - 1, Math.floor(q * ds.length))] : null;
1395
1397
  const totalQueries = byDay.results.reduce((a, r) => a + (r.n || 0), 0) || 0;
@@ -1407,6 +1409,7 @@ async function adminStatsRoute(c) {
1407
1409
  feedback: feedback.results,
1408
1410
  cache_mix_7d: cacheMix.results,
1409
1411
  latency_ms: ds.length ? { n: ds.length, p50: pct(0.5), p95: pct(0.95) } : null,
1412
+ generate_retries_7d: { total: Number(retries.total) || 0, answers_retried: Number(retries.answers) || 0 },
1410
1413
  conversations: convCount?.n ?? 0,
1411
1414
  error_rate_pct: errorRate,
1412
1415
  index_version: env.INDEX_VERSION,
@@ -1470,11 +1473,20 @@ async function verifyRoute(c) {
1470
1473
  }
1471
1474
  const bound = await bindModelNode(env, { query, standardKeys: entitlementScope(standardKeysFrom(body)) });
1472
1475
  const modelGrounding = bound && !bound.gated ? modelGroundingBlock(bound) : null;
1473
- const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionSteer });
1474
- const passages = retrieved.hits.map((h) => h.text);
1476
+ const supplied = (Array.isArray(body?.passages) ? body.passages : []).map((p) => typeof p === "string" ? p : p?.text ?? "").map((p) => p.trim()).filter(Boolean).slice(0, 8);
1477
+ let passages;
1478
+ let structured = [];
1479
+ if (supplied.length) {
1480
+ passages = supplied;
1481
+ structured = supplied.map((p) => ({ text: p }));
1482
+ } else {
1483
+ const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionSteer });
1484
+ passages = retrieved.hits.map((h) => h.text);
1485
+ structured = retrieved.hits.map((h) => ({ text: h.text, table: h.metadata.block === "table" || void 0 }));
1486
+ }
1475
1487
  const anchors = checkQuoteAnchors(answer, passages);
1476
1488
  const refs = [...answer.matchAll(/\[\[u:([^\]]+)\]\]/g)].map((m) => m[1]);
1477
- const validRefs = refs.filter((r) => retrieved.hits.some((h) => h.metadata.unit_id === r));
1489
+ const validRefs = refs.filter((r) => supplied.length ? supplied.some((p) => p.includes(`u:${r}`)) : structured.some((h) => h.text && !h.table));
1478
1490
  const checks = [
1479
1491
  { name: "quote_anchors", deterministic: true, pass: anchors.violations.length === 0, detail: `${anchors.violations.length} of ${anchors.total} quoted spans absent from the retrieved passages` },
1480
1492
  { name: "unit_references", deterministic: true, pass: refs.length === validRefs.length, detail: refs.length ? `${validRefs.length}/${refs.length} unit references resolve to served units` : "no unit references" },
@@ -1482,11 +1494,17 @@ async function verifyRoute(c) {
1482
1494
  ];
1483
1495
  const machine = (Array.isArray(body?.blocks) ? body.blocks : []).filter((b) => b?.type === "verdict" && b?.payload).map((b) => [b.payload.check, b.payload.meaning, b.payload.definition, b.payload.violation_meaning].filter((x) => typeof x === "string" && x).join(" \u2014 ")).filter(Boolean);
1484
1496
  if (modelGrounding) machine.unshift(modelGrounding);
1485
- const faith = await scoreFaithfulness(env.AI, roleModel(env, "grader"), answer, retrieved.hits.map((h) => ({ text: h.text, table: h.metadata.block === "table" || void 0 })), machine);
1497
+ const faith = await scoreFaithfulness(
1498
+ env.AI,
1499
+ roleModel(env, "grader"),
1500
+ answer,
1501
+ structured,
1502
+ machine
1503
+ );
1486
1504
  return json({
1487
1505
  checks,
1488
1506
  judged: faith ? { name: "faithfulness", deterministic: false, score: faith.score, ungrounded_claims: faith.ungrounded_claims.slice(0, 5) } : null,
1489
- passages_used: retrieved.hits.length
1507
+ passages_used: passages.length
1490
1508
  });
1491
1509
  } catch (e) {
1492
1510
  return err(502, "verify_failed", String(e).slice(0, 200));
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.31",
30
+ "version": "0.2.33",
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",
@@ -0,0 +1,4 @@
1
+ -- the latency program's aggregate: how many generation retries each ask
2
+ -- needed (Server-Timing carried the count per answer; this column makes
3
+ -- it rollup-able). Existing rows read 0 — they predate the column.
4
+ ALTER TABLE queries ADD COLUMN retries INTEGER NOT NULL DEFAULT 0;
@@ -237,7 +237,7 @@ async function handleAsk(
237
237
  // the latency program's clock: every telemetry write below reports the
238
238
  // wall time from request entry to its own exit
239
239
  const tStart = Date.now();
240
- const telemetryMeta = () => ({ durationMs: Date.now() - tStart, keyId: key?.id ?? null });
240
+ const telemetryMeta = () => ({ durationMs: Date.now() - tStart, keyId: key?.id ?? null, retries: generateRetries });
241
241
  // the latency anatomy, surfaced as standard Server-Timing headers on
242
242
  // the JSON response — the reduction program's per-stage data
243
243
  const stageTiming: Record<string, number> = {};
@@ -152,14 +152,16 @@ async function adminStatsRoute(c: RouteContext): Promise<Response> {
152
152
  if (!env.ADMIN_TOKEN) return err(501, "admin_disabled", "ADMIN_TOKEN secret is not configured");
153
153
  const auth = req.headers.get("authorization") ?? "";
154
154
  if (auth !== `Bearer ${env.ADMIN_TOKEN}`) return err(401, "unauthorized", "Invalid admin token");
155
- const [byDay, byModel, feedback, convCount, cacheMix, durations] = await Promise.all([
155
+ const [byDay, byModel, feedback, convCount, cacheMix, durations, retriesRow] = await Promise.all([
156
156
  env.DB.prepare("SELECT day, tier, COUNT(*) as n, SUM(ok) as ok FROM queries WHERE day >= date('now','-7 days') GROUP BY day, tier ORDER BY day DESC").all(),
157
157
  env.DB.prepare("SELECT model, SUM(requests) as requests FROM spend WHERE day >= date('now','-7 days') GROUP BY model ORDER BY requests DESC").all(),
158
158
  env.DB.prepare("SELECT rating, COUNT(*) as n FROM feedback GROUP BY rating").all(),
159
159
  env.DB.prepare("SELECT COUNT(*) as n FROM conversations").first(),
160
160
  env.DB.prepare("SELECT COALESCE(cache, 'miss') AS cache, COUNT(*) AS n FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' GROUP BY cache").all(),
161
161
  env.DB.prepare("SELECT duration_ms FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' AND duration_ms IS NOT NULL").all(),
162
+ env.DB.prepare("SELECT COALESCE(SUM(retries), 0) AS total, SUM(CASE WHEN retries > 0 THEN 1 ELSE 0 END) AS answers FROM queries WHERE day >= date('now','-7 days') AND route = 'ask'").all(),
162
163
  ]);
164
+ const retries = (retriesRow as any)?.results?.[0] ?? { total: 0, answers: 0 };
163
165
  const ds = (durations.results as any[]).map((r) => r.duration_ms as number).sort((a, b) => a - b);
164
166
  const pct = (q: number) => (ds.length ? ds[Math.min(ds.length - 1, Math.floor(q * ds.length))] : null);
165
167
  const totalQueries = (byDay.results as any[]).reduce((a, r) => a + (r.n || 0), 0) || 0;
@@ -177,6 +179,7 @@ async function adminStatsRoute(c: RouteContext): Promise<Response> {
177
179
  feedback: feedback.results,
178
180
  cache_mix_7d: cacheMix.results,
179
181
  latency_ms: ds.length ? { n: ds.length, p50: pct(0.5), p95: pct(0.95) } : null,
182
+ generate_retries_7d: { total: Number(retries.total) || 0, answers_retried: Number(retries.answers) || 0 },
180
183
  conversations: (convCount as any)?.n ?? 0,
181
184
  error_rate_pct: errorRate,
182
185
  index_version: env.INDEX_VERSION,
@@ -250,11 +253,33 @@ async function verifyRoute(c: RouteContext): Promise<Response> {
250
253
  }
251
254
  const bound = await bindModelNode(env, { query, standardKeys: entitlementScope(standardKeysFrom(body)) });
252
255
  const modelGrounding = bound && !bound.gated ? modelGroundingBlock(bound) : null;
253
- const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionSteer });
254
- const passages = retrieved.hits.map((h: Hit) => h.text);
256
+ // the caller's grounding WINS when present: the verify button (and
257
+ // the harness) hold the passages the answer was actually grounded on
258
+ // — verify's own fresh retrieval is a DIFFERENT pipeline (no grade,
259
+ // no listwise, no corrective widen) and returns different passages,
260
+ // which judges the answer against text it never saw
261
+ const supplied = (Array.isArray(body?.passages) ? body.passages : [])
262
+ .map((p: unknown) => (typeof p === "string" ? p : (p as any)?.text ?? ""))
263
+ .map((p: string) => p.trim())
264
+ .filter(Boolean)
265
+ .slice(0, 8);
266
+ let passages: string[];
267
+ let structured: { text: string; table?: boolean }[] = [];
268
+ if (supplied.length) {
269
+ passages = supplied;
270
+ structured = supplied.map((p: string) => ({ text: p }));
271
+ } else {
272
+ const retrieved = await retrieve(env, query, { understanding: u, standardKeys: entitlementScope(standardKeysFrom(body)), lexicalBoost, editionSteer });
273
+ passages = retrieved.hits.map((h: Hit) => h.text);
274
+ structured = retrieved.hits.map((h: Hit) => ({ text: h.text, table: (h.metadata as any).block === "table" || undefined }));
275
+ }
255
276
  const anchors = checkQuoteAnchors(answer, passages);
256
277
  const refs = [...answer.matchAll(/\[\[u:([^\]]+)\]\]/g)].map((m) => m[1]);
257
- const validRefs = refs.filter((r) => retrieved.hits.some((h: Hit) => h.metadata.unit_id === r));
278
+ // unit references resolve against the grounding in use
279
+ const validRefs = refs.filter((r: string) =>
280
+ supplied.length
281
+ ? supplied.some((p: string) => p.includes(`u:${r}`))
282
+ : structured.some((h) => h.text && !h.table));
258
283
  const checks = [
259
284
  { name: "quote_anchors", deterministic: true, pass: anchors.violations.length === 0, detail: `${anchors.violations.length} of ${anchors.total} quoted spans absent from the retrieved passages` },
260
285
  { name: "unit_references", deterministic: true, pass: refs.length === validRefs.length, detail: refs.length ? `${validRefs.length}/${refs.length} unit references resolve to served units` : "no unit references" },
@@ -269,11 +294,17 @@ async function verifyRoute(c: RouteContext): Promise<Response> {
269
294
  .filter((x: unknown) => typeof x === "string" && x).join(" — "))
270
295
  .filter(Boolean);
271
296
  if (modelGrounding) machine.unshift(modelGrounding);
272
- const faith = await scoreFaithfulness(env.AI, roleModel(env, "grader"), answer, retrieved.hits.map((h: Hit) => ({ text: h.text, table: (h.metadata as any).block === "table" || undefined })), machine);
297
+ const faith = await scoreFaithfulness(
298
+ env.AI,
299
+ roleModel(env, "grader"),
300
+ answer,
301
+ structured,
302
+ machine,
303
+ );
273
304
  return json({
274
305
  checks,
275
306
  judged: faith ? { name: "faithfulness", deterministic: false, score: faith.score, ungrounded_claims: faith.ungrounded_claims.slice(0, 5) } : null,
276
- passages_used: retrieved.hits.length,
307
+ passages_used: passages.length,
277
308
  });
278
309
  } catch (e) {
279
310
  return err(502, "verify_failed", String(e).slice(0, 200));
@@ -40,14 +40,14 @@ export function telemetry(
40
40
  queryHash: string,
41
41
  lang?: string,
42
42
  cache?: "exact" | "semantic",
43
- meta?: { durationMs?: number; keyId?: string | null },
43
+ meta?: { durationMs?: number; keyId?: string | null; retries?: number },
44
44
  ) {
45
45
  const day = today();
46
46
  ctx.waitUntil(
47
47
  env.DB.batch([
48
48
  env.DB.prepare(
49
- "INSERT INTO queries (ts, day, tier, route, model, ok, answer_chars, query_hash, lang, cache, duration_ms, key_id) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12)",
50
- ).bind(new Date().toISOString(), day, tier, route, model, ok ? 1 : 0, answerChars, queryHash, lang ?? null, cache ?? null, meta?.durationMs ?? null, meta?.keyId ?? null),
49
+ "INSERT INTO queries (ts, day, tier, route, model, ok, answer_chars, query_hash, lang, cache, duration_ms, key_id, retries) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13)",
50
+ ).bind(new Date().toISOString(), day, tier, route, model, ok ? 1 : 0, answerChars, queryHash, lang ?? null, cache ?? null, meta?.durationMs ?? null, meta?.keyId ?? null, meta?.retries ?? 0),
51
51
  env.DB.prepare(
52
52
  "INSERT INTO spend (day, tier, model, requests) VALUES (?1,?2,?3,1) ON CONFLICT(day, tier, model) DO UPDATE SET requests = requests + 1",
53
53
  ).bind(day, tier, model ?? "none"),