@konneal/engine 0.2.152 → 0.2.153
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.
|
@@ -366,17 +366,6 @@ async function handleAsk(
|
|
|
366
366
|
let generateRetries = 0;
|
|
367
367
|
// how the question was read, for the reader: the interpretation that
|
|
368
368
|
// steered retrieval — a wrong read is visible before it costs trust
|
|
369
|
-
const readAs = () =>
|
|
370
|
-
understanding
|
|
371
|
-
? {
|
|
372
|
-
intent: understanding.intent,
|
|
373
|
-
doc: understanding.docidentifier,
|
|
374
|
-
edition: understanding.edition ?? null,
|
|
375
|
-
term: understanding.term,
|
|
376
|
-
terms: (understanding.defined_terms ?? []).slice(0, 4),
|
|
377
|
-
lang: q?.lang ?? null,
|
|
378
|
-
}
|
|
379
|
-
: undefined;
|
|
380
369
|
const serverTiming = () =>
|
|
381
370
|
Object.entries(stageTiming)
|
|
382
371
|
.map(([k, v]) => `${k};dur=${v}`)
|
|
@@ -482,11 +471,39 @@ async function handleAsk(
|
|
|
482
471
|
// owner's 2026-10-04 direction — the user must see the system-to-model
|
|
483
472
|
// questioning sessions, not stare at a spinner)
|
|
484
473
|
const agentSteps: string[] = [];
|
|
474
|
+
// THE AGENT-MODE CHANNEL (the owner's directive: users see every call
|
|
475
|
+
// as it runs): the streamed lane's SSE opens IMMEDIATELY — the channel
|
|
476
|
+
// exists before the pipeline starts, and every step (and the answer
|
|
477
|
+
// tokens) enqueues into it live. The json lane leaves it null and the
|
|
478
|
+
// steps fall back to the agentSteps buffer for its single response.
|
|
479
|
+
let live: { send: (ev: unknown) => void; close: () => void } | null = null;
|
|
480
|
+
let liveResponse: Response | null = null;
|
|
485
481
|
const mark = (step: string) => {
|
|
486
|
-
|
|
482
|
+
if (live) live.send({ type: "agent", step });
|
|
483
|
+
else agentSteps.push(step);
|
|
487
484
|
console.log(`agent-step: ${step}`);
|
|
488
485
|
};
|
|
489
486
|
|
|
487
|
+
// THE STREAM-FIRST HOIST (the owner's directive: users see every call as
|
|
488
|
+
// it runs): the SSE response returns NOW — before the vision step, the
|
|
489
|
+
// understanding, the register bridge — so every agent step streams to
|
|
490
|
+
// the user at the moment it runs, never replayed from a buffer. The json
|
|
491
|
+
// lane leaves the channel null and the buffer still collects for its
|
|
492
|
+
// single response.
|
|
493
|
+
const wantsStream = body?.stream === true || (tier === "anon" && body?.stream !== false);
|
|
494
|
+
if (wantsStream) {
|
|
495
|
+
const encoder = new TextEncoder();
|
|
496
|
+
let ctrl: ReadableStreamDefaultController<Uint8Array> | null = null;
|
|
497
|
+
const sse = new ReadableStream({
|
|
498
|
+
start(c) { ctrl = c; },
|
|
499
|
+
});
|
|
500
|
+
live = {
|
|
501
|
+
send: (ev: unknown) => { try { ctrl!.enqueue(encoder.encode(`data: ${JSON.stringify(ev)}\n\n`)); } catch { /* the client went away */ } },
|
|
502
|
+
close: () => { try { ctrl!.close(); } catch { /* closed */ } },
|
|
503
|
+
};
|
|
504
|
+
liveResponse = new Response(sse, { headers: { "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "no", ...corsHeaders(req) } });
|
|
505
|
+
}
|
|
506
|
+
|
|
490
507
|
// Members get federated retrieval (OIML + ISO/IEC) merged into the same
|
|
491
508
|
// pipeline via the service binding; rag-public never touches the
|
|
492
509
|
// internal index itself, and generation/rerank stay in ONE pipeline.
|
|
@@ -601,7 +618,42 @@ async function handleAsk(
|
|
|
601
618
|
const gen = await corpusGen(env.CACHE);
|
|
602
619
|
const withImage = effectiveImage || storedImage;
|
|
603
620
|
const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || withImage || ablate ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
|
|
604
|
-
|
|
621
|
+
let probeTokensUsed = 0;
|
|
622
|
+
|
|
623
|
+
// THE PIPELINE: one body, two deliveries. Every `return` inside is an
|
|
624
|
+
// exit — for the streamed lane each exit converts to its live events
|
|
625
|
+
// via `finish` (and null, meaning the SSE already carried everything);
|
|
626
|
+
// for the json lane the same payload returns as the response.
|
|
627
|
+
const pipeline = async (
|
|
628
|
+
channel: { send: (ev: unknown) => void; close: () => void } | null,
|
|
629
|
+
qq: { query: string; lang?: string },
|
|
630
|
+
): Promise<Response | null> => {
|
|
631
|
+
// the channel is a parameter: effectively const inside, so every
|
|
632
|
+
// guard on it narrows for the whole body
|
|
633
|
+
const live = channel;
|
|
634
|
+
// the streamed lane's graceful degradation: a serving failure reaches
|
|
635
|
+
// the user as a line, never as a dropped stream
|
|
636
|
+
const q = qq;
|
|
637
|
+
const finishLine = (message: string) => {
|
|
638
|
+
if (live) {
|
|
639
|
+
live!.send({ type: "token", v: message });
|
|
640
|
+
live!.send({ type: "done", model: MODELS.member, query_hash: "", context_applied: NO_CONTEXT });
|
|
641
|
+
live!.close();
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
return err(503, "serving_busy", message);
|
|
645
|
+
};
|
|
646
|
+
const readAs = () =>
|
|
647
|
+
understanding
|
|
648
|
+
? {
|
|
649
|
+
intent: understanding.intent,
|
|
650
|
+
doc: understanding.docidentifier,
|
|
651
|
+
edition: understanding.edition ?? null,
|
|
652
|
+
term: understanding.term,
|
|
653
|
+
terms: (understanding.defined_terms ?? []).slice(0, 4),
|
|
654
|
+
lang: q?.lang ?? null,
|
|
655
|
+
}
|
|
656
|
+
: undefined;
|
|
605
657
|
|
|
606
658
|
if (cached) {
|
|
607
659
|
telemetry(env, ctx, tier, "ask", null, true, (cached.value.answer ?? "").length, cached.value.query_hash, q.lang, "exact", telemetryMeta());
|
|
@@ -610,13 +662,12 @@ async function handleAsk(
|
|
|
610
662
|
// node named in the question binds WITHOUT a chip and its echo must
|
|
611
663
|
// survive the cache, not silently flatten to "none")
|
|
612
664
|
const cctx = cached.value.context_applied ?? NO_CONTEXT;
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
], corsHeaders(req));
|
|
665
|
+
if (live) {
|
|
666
|
+
live!.send({ type: "citations", citations: cached.value.citations ?? [], quota, context_applied: cctx, ...(cached.value.source_quality ? { source_quality: cached.value.source_quality, confidence_note: cached.value.confidence_note ?? null, ...(cached.value.experimental_sources ? { experimental_sources: cached.value.experimental_sources } : {}), ...(cached.value.entailment ? { entailment: cached.value.entailment } : {}) } : {}) });
|
|
667
|
+
live!.send({ type: "token", v: cached.value.answer ?? "" });
|
|
668
|
+
live!.send({ type: "done", model: cached.value.model ?? MODELS.member, query_hash: cached.value.query_hash, served_from: "cache", context_applied: cctx });
|
|
669
|
+
live!.close();
|
|
670
|
+
return null;
|
|
620
671
|
}
|
|
621
672
|
return json({ ...cached.value, cached: true, quota, context_applied: cctx });
|
|
622
673
|
}
|
|
@@ -813,6 +864,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
813
864
|
// own reading is authoritative over the text lane's tokens ("this",
|
|
814
865
|
// "have" — stopwords that can match an unrelated holder)
|
|
815
866
|
if (nameplate?.manufacturer) {
|
|
867
|
+
mark(`certificates.search — ${nameplate.manufacturer}${nameplate.model ? ` ${nameplate.model}` : ""}`.slice(0, 140));
|
|
816
868
|
const bridged = await searchRegister(env.DB, nameplateRegisterQuery(nameplate, q.query), true);
|
|
817
869
|
if (bridged?.rows?.length) {
|
|
818
870
|
register = bridged;
|
|
@@ -855,20 +907,29 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
855
907
|
registerBlock = block;
|
|
856
908
|
aggregateNoteText = aggregateNote(agg);
|
|
857
909
|
const queryHash = await sha256Hex(q.query);
|
|
858
|
-
telemetry(env, ctx, tier, "ask", MODELS.member, true, answer
|
|
859
|
-
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, answer
|
|
860
|
-
|
|
910
|
+
telemetry(env, ctx, tier, "ask", MODELS.member, true, answer!.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
911
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, answer!.length);
|
|
912
|
+
const payload = {
|
|
861
913
|
answer,
|
|
862
914
|
citations: [{ docidentifier: `${P().publisher.name}-CS certificate register (family ${agg.family})`, corpus: "register" }],
|
|
863
915
|
model: MODELS.member,
|
|
864
916
|
query_hash: queryHash,
|
|
865
|
-
follow_ups: [],
|
|
917
|
+
follow_ups: [] as string[],
|
|
866
918
|
blocks: [block],
|
|
867
919
|
context_applied: NO_CONTEXT,
|
|
868
920
|
read: readAs(),
|
|
869
921
|
...(ablate ? { ablate } : {}),
|
|
870
922
|
quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview },
|
|
871
|
-
}
|
|
923
|
+
};
|
|
924
|
+
if (live) {
|
|
925
|
+
live!.send({ type: "citations", citations: payload.citations, quota: payload.quota, context_applied: NO_CONTEXT });
|
|
926
|
+
live!.send({ type: "token", v: answer });
|
|
927
|
+
live!.send({ type: "done", model: MODELS.member, query_hash: queryHash, blocks: [block], follow_ups: [], context_applied: NO_CONTEXT, read: readAs() });
|
|
928
|
+
live!.send({ type: "quota", quota: payload.quota });
|
|
929
|
+
live!.close();
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
return json(payload);
|
|
872
933
|
}
|
|
873
934
|
}
|
|
874
935
|
}
|
|
@@ -890,37 +951,31 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
890
951
|
...keptHistory.slice(-6),
|
|
891
952
|
{ role: "user", content: q.query },
|
|
892
953
|
];
|
|
893
|
-
if (
|
|
954
|
+
if (live) {
|
|
955
|
+
// the reading arrives first — a conversational turn is still a reading
|
|
956
|
+
if (readAs()) live!.send({ type: "read", read: readAs() });
|
|
957
|
+
live!.send({ type: "citations", citations: [], context_applied: NO_CONTEXT, quota, });
|
|
958
|
+
const stats: { usage?: unknown } = {};
|
|
959
|
+
let full = "";
|
|
894
960
|
const stream = await generateStream(env, model, messages, effort);
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
for await (const tok of sseTokens(stream, stats)) {
|
|
907
|
-
full += tok;
|
|
908
|
-
send({ type: "token", v: tok });
|
|
909
|
-
}
|
|
910
|
-
} catch {
|
|
911
|
-
// stream ended prematurely — deliver what we have
|
|
912
|
-
}
|
|
913
|
-
const tq = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages), full.length, probeTokensUsed);
|
|
914
|
-
send({ type: "quota", quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } });
|
|
915
|
-
send({ type: "done", model, query_hash: queryHash, context_applied: NO_CONTEXT, read: readAs() });
|
|
916
|
-
telemetry(env, ctx, tier, "ask", model, true, full.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
917
|
-
controller.close();
|
|
918
|
-
},
|
|
919
|
-
});
|
|
920
|
-
return new Response(sse, {
|
|
921
|
-
headers: { "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "no", ...corsHeaders(req) },
|
|
922
|
-
});
|
|
961
|
+
try {
|
|
962
|
+
if (stream) {
|
|
963
|
+
for await (const tok of sseTokens(stream, stats)) {
|
|
964
|
+
full += tok;
|
|
965
|
+
live!.send({ type: "token", v: tok });
|
|
966
|
+
}
|
|
967
|
+
} else {
|
|
968
|
+
live!.send({ type: "token", v: "The generation model is unavailable; please retry." });
|
|
969
|
+
}
|
|
970
|
+
} catch {
|
|
971
|
+
// stream ended prematurely — deliver what we have
|
|
923
972
|
}
|
|
973
|
+
const tq = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages!), full.length, probeTokensUsed);
|
|
974
|
+
live!.send({ type: "quota", quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } });
|
|
975
|
+
live!.send({ type: "done", model, query_hash: queryHash, context_applied: NO_CONTEXT, read: readAs() });
|
|
976
|
+
telemetry(env, ctx, tier, "ask", model, true, full.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
977
|
+
live!.close();
|
|
978
|
+
return null;
|
|
924
979
|
}
|
|
925
980
|
let answer = await generateOnce(env, model, messages, effort);
|
|
926
981
|
if (answer === null) answer = await generateOnce(env, MODELS.fallback, messages, effort);
|
|
@@ -928,7 +983,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
928
983
|
telemetry(env, ctx, tier, "ask", model, false, 0, queryHash, q.lang, undefined, telemetryMeta());
|
|
929
984
|
return err(502, "generation_failed", "The generation model is unavailable; please retry.");
|
|
930
985
|
}
|
|
931
|
-
telemetry(env, ctx, tier, "ask", model, true, answer
|
|
986
|
+
telemetry(env, ctx, tier, "ask", model, true, answer!.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
932
987
|
return json({ answer, citations: [], model, query_hash: queryHash, follow_ups: [], context_applied: NO_CONTEXT, read: readAs(), quota, });
|
|
933
988
|
}
|
|
934
989
|
|
|
@@ -971,19 +1026,16 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
971
1026
|
console.log("draft act:", draftAct, "→", verdict.status === "draft" ? `draft (${Object.keys(verdict.draft.fields).length} fields)` : `refused (${verdict.reason})`);
|
|
972
1027
|
const citations = verdict.citation ? [{ ...verdict.citation, corpus: P().publisher.id }] : [];
|
|
973
1028
|
const draftPayload = verdict.status === "draft" ? verdict.draft : undefined;
|
|
974
|
-
telemetry(env, ctx, tier, "ask", model, true, verdict.answer
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
],
|
|
983
|
-
corsHeaders(req),
|
|
984
|
-
);
|
|
1029
|
+
telemetry(env, ctx, tier, "ask", model, true, verdict.answer!.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
1030
|
+
if (live) {
|
|
1031
|
+
if (readAs()) live!.send({ type: "read", read: readAs() });
|
|
1032
|
+
live!.send({ type: "citations", citations, context_applied: draftCtxApplied, ...(draftPayload ? { draft: draftPayload } : {}), quota, });
|
|
1033
|
+
live!.send({ type: "token", v: verdict.answer });
|
|
1034
|
+
live!.send({ type: "done", model, query_hash: queryHash, context_applied: draftCtxApplied, read: readAs(), ...(ablate ? { ablate } : {}) });
|
|
1035
|
+
live!.close();
|
|
1036
|
+
return null;
|
|
985
1037
|
}
|
|
986
|
-
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, verdict.answer
|
|
1038
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, verdict.answer!.length);
|
|
987
1039
|
return json({ answer: verdict.answer, citations, model, query_hash: queryHash, follow_ups: [], context_applied: draftCtxApplied, read: readAs(), ...(draftPayload ? { draft: draftPayload } : {}), ...(ablate ? { ablate } : {}), quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } });
|
|
988
1040
|
}
|
|
989
1041
|
|
|
@@ -1009,19 +1061,16 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1009
1061
|
});
|
|
1010
1062
|
console.log("api_call draft:", apiCallIntent, "→", verdict.status === "draft" ? `draft (${verdict.draft.call.method} ${verdict.draft.call.path})` : `refused (${verdict.reason})`);
|
|
1011
1063
|
const draftPayload = verdict.status === "draft" ? verdict.draft : undefined;
|
|
1012
|
-
telemetry(env, ctx, tier, "ask", model, true, verdict.answer
|
|
1013
|
-
if (
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
],
|
|
1021
|
-
corsHeaders(req),
|
|
1022
|
-
);
|
|
1064
|
+
telemetry(env, ctx, tier, "ask", model, true, verdict.answer!.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
1065
|
+
if (live) {
|
|
1066
|
+
if (readAs()) live!.send({ type: "read", read: readAs() });
|
|
1067
|
+
live!.send({ type: "citations", citations: [], context_applied: callCtxApplied, ...(draftPayload ? { draft: draftPayload } : {}), quota, });
|
|
1068
|
+
live!.send({ type: "token", v: verdict.answer });
|
|
1069
|
+
live!.send({ type: "done", model, query_hash: queryHash, context_applied: callCtxApplied, read: readAs(), ...(ablate ? { ablate } : {}) });
|
|
1070
|
+
live!.close();
|
|
1071
|
+
return null;
|
|
1023
1072
|
}
|
|
1024
|
-
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, verdict.answer
|
|
1073
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, q.query.length, verdict.answer!.length);
|
|
1025
1074
|
return json({ answer: verdict.answer, citations: [], model, query_hash: queryHash, follow_ups: [], context_applied: callCtxApplied, read: readAs(), ...(draftPayload ? { draft: draftPayload } : {}), ...(ablate ? { ablate } : {}), quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } });
|
|
1026
1075
|
}
|
|
1027
1076
|
|
|
@@ -1374,7 +1423,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1374
1423
|
} catch (e) {
|
|
1375
1424
|
console.log("ask: retrieval failed:", String(e).slice(0, 300));
|
|
1376
1425
|
telemetry(env, ctx, tier, "ask", MODELS.embed, false, 0, await sha256Hex(q.query), q.lang, undefined, telemetryMeta());
|
|
1377
|
-
return
|
|
1426
|
+
return finishLine("Search is briefly busy — please retry in a moment.");
|
|
1378
1427
|
}
|
|
1379
1428
|
const { hits } = retrieved;
|
|
1380
1429
|
if (hits.length === 0 && !liveRecords?.length && (!boundModel || boundModel.gated)) {
|
|
@@ -1385,7 +1434,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1385
1434
|
const answer =
|
|
1386
1435
|
licenseBoundaryRefusal(modelDocHint?.doc_number ?? understanding?.doc_number ?? null, standardKeys) ?? refusalAnswer();
|
|
1387
1436
|
const out = { answer, citations: [], model, query_hash: await sha256Hex(q.query), context_applied: ctxApplied, ...(ablate ? { ablate } : {}), route: { route: routed.route, features: routed.features } };
|
|
1388
|
-
telemetry(env, ctx, tier, "ask", model, true, answer
|
|
1437
|
+
telemetry(env, ctx, tier, "ask", model, true, answer!.length, out.query_hash, q.lang, undefined, telemetryMeta());
|
|
1389
1438
|
return json({ ...out, quota, });
|
|
1390
1439
|
}
|
|
1391
1440
|
|
|
@@ -1461,13 +1510,13 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1461
1510
|
const pendingTools: { name: string; query: string; output: string }[] = [];
|
|
1462
1511
|
// the agent's own consumption (2026-10-11): every tool probe is a real
|
|
1463
1512
|
// model call — its usage joins the charge so credits price the true cost
|
|
1464
|
-
|
|
1513
|
+
|
|
1465
1514
|
// On an IMAGE turn the probe CARRIES THE IMAGE (2026-10-03, the owner's
|
|
1466
1515
|
// architecture): the model that reads the plate is the model that issues
|
|
1467
1516
|
// the tool call — one reader, one tool, no parallel extraction racing a
|
|
1468
1517
|
// second vision pass that drifts hour to hour. The old probe dropped the
|
|
1469
1518
|
// image-bearing message, which is why the private bridge had to exist.
|
|
1470
|
-
if (multiTurn && env.DB) {
|
|
1519
|
+
if (multiTurn && env.DB && !nameplate?.manufacturer) {
|
|
1471
1520
|
try {
|
|
1472
1521
|
const imageTurn = typeof withImage === "string";
|
|
1473
1522
|
if (imageTurn) mark("Reading the photograph to identify the instrument");
|
|
@@ -1552,13 +1601,10 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1552
1601
|
// provenance — ahead of the prose passages.
|
|
1553
1602
|
const cites = boundModel ? [modelCitation(boundModel), ...citations(usedHits)] : citations(usedHits);
|
|
1554
1603
|
|
|
1555
|
-
if (
|
|
1604
|
+
if (live) {
|
|
1556
1605
|
const stream = await generateStream(env, model, messages, routeEffort(routed.route));
|
|
1557
1606
|
if (stream) {
|
|
1558
|
-
const
|
|
1559
|
-
const sse = new ReadableStream({
|
|
1560
|
-
async start(controller) {
|
|
1561
|
-
const send = (obj: unknown) => controller.enqueue(encoder.encode(`data: ${JSON.stringify(obj)}\n\n`));
|
|
1607
|
+
const send = (obj: unknown) => live!.send(obj);
|
|
1562
1608
|
// the agent trace arrives first: every questioning round the
|
|
1563
1609
|
// assistant ran — the photograph read, the register search, any
|
|
1564
1610
|
// re-answer — so the user watched what happened, not a spinner
|
|
@@ -1615,7 +1661,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1615
1661
|
} catch {
|
|
1616
1662
|
// stream ended prematurely — deliver what we have
|
|
1617
1663
|
}
|
|
1618
|
-
const tq = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages), full.length, probeTokensUsed);
|
|
1664
|
+
const tq = await chargeAnswerTokens(env, bucketId, stats, messageChars(messages!), full.length, probeTokensUsed);
|
|
1619
1665
|
send({ type: "quota", quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } });
|
|
1620
1666
|
// the blind-answer guard ON THE STREAM PATH (the JSON path has
|
|
1621
1667
|
// had it since 0.2.80): an image rode the turn, the register
|
|
@@ -1671,10 +1717,10 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1671
1717
|
new Promise<null>((r) => setTimeout(() => r(null), THRESHOLDS.entailmentBudgetMs)),
|
|
1672
1718
|
]);
|
|
1673
1719
|
if (raced) {
|
|
1674
|
-
const v = entailmentVerdict(raced
|
|
1675
|
-
streamedEntailment = { support: v.support, score: Math.round(raced
|
|
1720
|
+
const v = entailmentVerdict(raced!.score, THRESHOLDS.entailmentSupportedFloor, THRESHOLDS.entailmentPartialFloor);
|
|
1721
|
+
streamedEntailment = { support: v.support, score: Math.round(raced!.score * 100) / 100, ...(raced!.ungrounded_claims?.length ? { ungrounded: raced!.ungrounded_claims.slice(0, 3) } : {}) };
|
|
1676
1722
|
streamedNote = v.note;
|
|
1677
|
-
if (v.support !== "supported") console.log("entailment(stream):", v.support, raced
|
|
1723
|
+
if (v.support !== "supported") console.log("entailment(stream):", v.support, raced!.score);
|
|
1678
1724
|
send({ type: "confidence", source_quality: sourceQuality, confidence_note: streamedNote || qualityNote(sourceQuality), ...(streamedEntailment ? { entailment: streamedEntailment } : {}) });
|
|
1679
1725
|
}
|
|
1680
1726
|
}
|
|
@@ -1691,19 +1737,17 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1691
1737
|
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, ...(sourceQuality ? { source_quality: sourceQuality, confidence_note: streamedNote || qualityNote(sourceQuality) } : {}), ...(streamedEntailment ? { entailment: streamedEntailment } : {}) }), { expirationTtl: LIMITS.cacheTtlSec }),
|
|
1692
1738
|
);
|
|
1693
1739
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
});
|
|
1740
|
+
live!.close();
|
|
1741
|
+
} else {
|
|
1742
|
+
// the streamed lane's generation is unavailable — the line goes
|
|
1743
|
+
// out on the already-open SSE; the json lane never enters this block
|
|
1744
|
+
live!.send({ type: "token", v: "The generation model is unavailable; please retry." });
|
|
1745
|
+
live!.send({ type: "done", model: MODELS.member, query_hash: "", context_applied: NO_CONTEXT });
|
|
1746
|
+
live!.close();
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
return null;
|
|
1705
1750
|
}
|
|
1706
|
-
}
|
|
1707
1751
|
|
|
1708
1752
|
const tGen = Date.now();
|
|
1709
1753
|
// ── speculative draft-verify (TODO.sota/02 row 2): when the ablation
|
|
@@ -1722,9 +1766,9 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1722
1766
|
return null;
|
|
1723
1767
|
});
|
|
1724
1768
|
if (spec) {
|
|
1725
|
-
answer = spec
|
|
1769
|
+
answer = spec!.answer;
|
|
1726
1770
|
stageTiming.speculative = Date.now() - tGen;
|
|
1727
|
-
console.log("speculative:", spec
|
|
1771
|
+
console.log("speculative:", spec!.nDrafts, "drafts,", spec!.chosen, "viable — verified");
|
|
1728
1772
|
}
|
|
1729
1773
|
}
|
|
1730
1774
|
if (!answer) answer = await generateOnce(env, model, messages, routeEffort(routed.route));
|
|
@@ -1749,19 +1793,25 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1749
1793
|
);
|
|
1750
1794
|
answer = await generateOnce(env, MODELS.fallback, flat, routeEffort(routed.route));
|
|
1751
1795
|
}
|
|
1796
|
+
if (answer === null) {
|
|
1797
|
+
// both generations failed — the json lane returns the error shape; the
|
|
1798
|
+
// streamed lane's equivalent lives inside the streamed block above
|
|
1799
|
+
telemetry(env, ctx, tier, "ask", model, false, 0, queryHash, q.lang, undefined, telemetryMeta());
|
|
1800
|
+
return err(502, "generation_failed", "The generation model is unavailable; please retry.");
|
|
1801
|
+
}
|
|
1752
1802
|
// the protocol scrub: a TOOL line is the agent loop's wire protocol,
|
|
1753
1803
|
// never user-facing prose — the answer model occasionally echoes the
|
|
1754
1804
|
// line it just composed (observed live: 'TOOL db.find …' opened a
|
|
1755
1805
|
// served answer). Stripped here and on the streamed path.
|
|
1756
1806
|
const scrubToolLines = (t: string) => t.replace(/^TOOL [a-z_.]+.*$\n?/gm, "").replace(/^\n+/, "");
|
|
1757
|
-
if (answer) answer = scrubToolLines(answer);
|
|
1758
|
-
if (answer) answer = canonicalRefusal(answer
|
|
1759
|
-
if (answer) answer = scrubToolLines(answer);
|
|
1807
|
+
if (answer) answer = scrubToolLines(answer!);
|
|
1808
|
+
if (answer) answer = canonicalRefusal(answer!, refusalScope(answerCites(answer!)));
|
|
1809
|
+
if (answer) answer = scrubToolLines(answer!);
|
|
1760
1810
|
// the misplaced-pin wall: a CITED answer is not a refusal — excise
|
|
1761
1811
|
// the pinned sentence if the model dropped it in as a disclaimer
|
|
1762
1812
|
// (measured on chip-scoped asks: correct cited answers carrying the
|
|
1763
1813
|
// pin mid-text, read by users and graders as total refusals)
|
|
1764
|
-
if (answer) answer = exciseMisplacedPin(answer
|
|
1814
|
+
if (answer) answer = exciseMisplacedPin(answer!, answerCites(answer!));
|
|
1765
1815
|
// the tool-narration leak (2026-10-14, the R 134-1 probe): the answer
|
|
1766
1816
|
// model sometimes NARRATES the agent loop's call in prose — 'The
|
|
1767
1817
|
// db.find tool returned, for the query "…": {…json…}' — instead of
|
|
@@ -1775,31 +1825,31 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1775
1825
|
// the passages or a typed table was retyped as markdown; the retry
|
|
1776
1826
|
// wins only if it verifies better.
|
|
1777
1827
|
let used = usedHits;
|
|
1778
|
-
if (answer && !answer
|
|
1779
|
-
const anchors = checkQuoteAnchors(answer
|
|
1828
|
+
if (answer && !answer!.includes(refusalAnswer())) {
|
|
1829
|
+
const anchors = checkQuoteAnchors(answer!, used.map((h: Hit) => h.text));
|
|
1780
1830
|
const hasTableUnit = used.some((h: Hit) => h.metadata.unit_id && h.metadata.block === "table");
|
|
1781
|
-
const retyped = tableRetyped(answer
|
|
1831
|
+
const retyped = tableRetyped(answer!, hasTableUnit);
|
|
1782
1832
|
// presenting a served table's DATA without its unit reference is the
|
|
1783
1833
|
// same contract violation as retyping it — the HARD RULE wants the
|
|
1784
1834
|
// token wherever the table's values carry the answer
|
|
1785
1835
|
const unreferenced = (() => {
|
|
1786
|
-
if (!hasTableUnit || answer
|
|
1836
|
+
if (!hasTableUnit || answer!.includes("[[u:")) return false;
|
|
1787
1837
|
const norm = (s: string) => (s.match(/\d[\d ,.]{1,8}\d/g) ?? []).map((x) => x.replace(/[ ,.]/g, ""));
|
|
1788
|
-
const nums = norm(answer);
|
|
1838
|
+
const nums = norm(answer!);
|
|
1789
1839
|
if (nums.length < 2) return false;
|
|
1790
1840
|
const tableNums = new Set(
|
|
1791
1841
|
norm(used.filter((h: Hit) => h.metadata.unit_id && h.metadata.block === "table").map((h: Hit) => h.text).join(" ")),
|
|
1792
1842
|
);
|
|
1793
1843
|
return nums.filter((n) => tableNums.has(n)).length >= 2;
|
|
1794
1844
|
})();
|
|
1795
|
-
if (anchors.violations.length > 0 || retyped || unreferenced || toolNarration(answer) || intentPreamble(answer)) {
|
|
1796
|
-
console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "; toolNarration:", toolNarration(answer), "; intentPreamble:", intentPreamble(answer), "— regenerating");
|
|
1845
|
+
if (anchors.violations.length > 0 || retyped || unreferenced || toolNarration(answer!) || intentPreamble(answer!)) {
|
|
1846
|
+
console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "; toolNarration:", toolNarration(answer!), "; intentPreamble:", intentPreamble(answer!), "— regenerating");
|
|
1797
1847
|
// name the EXACT unit the token must reference — a generic note
|
|
1798
1848
|
// leaves the model guessing which id to write
|
|
1799
1849
|
const tableUnitId = unreferenced
|
|
1800
1850
|
? used.find((h: Hit) => h.metadata.unit_id && h.metadata.block === "table")?.metadata.unit_id
|
|
1801
1851
|
: undefined;
|
|
1802
|
-
const note = toolNarration(answer)
|
|
1852
|
+
const note = toolNarration(answer!)
|
|
1803
1853
|
? TOOL_NARRATION_NOTICE
|
|
1804
1854
|
: intentPreamble(answer)
|
|
1805
1855
|
? INTENT_PREAMBLE_NOTICE
|
|
@@ -1809,7 +1859,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1809
1859
|
generateRetries += 1;
|
|
1810
1860
|
const corrected = await generateOnce(env, model, [...messages, { role: "system", content: note }], routeEffort(routed.route));
|
|
1811
1861
|
if (corrected) {
|
|
1812
|
-
const correctedAnswer = canonicalRefusal(corrected
|
|
1862
|
+
const correctedAnswer = canonicalRefusal(corrected!, refusalScope(answerCites(corrected!)));
|
|
1813
1863
|
const retryAnchors = checkQuoteAnchors(correctedAnswer, used.map((h: Hit) => h.text));
|
|
1814
1864
|
const retryRetyped = tableRetyped(correctedAnswer, hasTableUnit);
|
|
1815
1865
|
if (retryAnchors.violations.length < anchors.violations.length || (!retryRetyped && retyped) || (unreferenced && correctedAnswer.includes("[[u:")) || !toolNarration(correctedAnswer) || !intentPreamble(correctedAnswer)) {
|
|
@@ -1834,27 +1884,27 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1834
1884
|
// The model critiques its own answer; if claims are ungrounded, retry
|
|
1835
1885
|
// retrieval with the missing-info hint (max one retry).
|
|
1836
1886
|
// Ref: selfrag.github.io; arXiv 2606.05658 bounded reflection
|
|
1837
|
-
if (answer && !answer
|
|
1838
|
-
const reflection = await reflect(env.AI, MODELS.grader, q.query, answer
|
|
1839
|
-
console.log("reflection:", reflection ? (reflection
|
|
1840
|
-
if (reflection && !reflection
|
|
1887
|
+
if (answer && !answer!.includes(refusalAnswer())) {
|
|
1888
|
+
const reflection = await reflect(env.AI, MODELS.grader, q.query, answer!, hits.map((h: Hit) => h.text));
|
|
1889
|
+
console.log("reflection:", reflection ? (reflection!.grounded ? "grounded" : "ungrounded") : "null");
|
|
1890
|
+
if (reflection && !reflection!.grounded && reflection!.missing_info) {
|
|
1841
1891
|
// re-retrieve targeting what was missing — the declared context's
|
|
1842
1892
|
// hard seal binds the retry exactly as the first pass
|
|
1843
1893
|
// (TODO.ai-platform/02)
|
|
1844
1894
|
const retryRetrieve = await retrieve(env, q.query, {
|
|
1845
1895
|
prev,
|
|
1846
|
-
understanding: { ...understanding, standalone_query: `${understanding?.standalone_query || q.query} ${reflection
|
|
1896
|
+
understanding: { ...understanding, standalone_query: `${understanding?.standalone_query || q.query} ${reflection!.missing_info}` } as any,
|
|
1847
1897
|
sealScope: declaredScoped ? docScope : null,
|
|
1848
1898
|
datasetScope: narrowed ? corpora : null,
|
|
1849
1899
|
standardKeys,
|
|
1850
|
-
...(routeSubset ? { stageSubset: routeSubset } : {}),
|
|
1900
|
+
...(routeSubset ? { stageSubset: routeSubset ?? undefined } : {}),
|
|
1851
1901
|
});
|
|
1852
1902
|
if (retryRetrieve.hits.length > 0) {
|
|
1853
1903
|
const { messages: retryMessages, usedHits: retryUsed } = buildMessages(q.query, retryRetrieve.hits, q.lang, keptHistory, undefined, summary, budget);
|
|
1854
1904
|
const retryAnswer = await generateOnce(env, model, retryMessages, routeEffort(routed.route));
|
|
1855
1905
|
// the answer now comes from the retry passages — citations must follow
|
|
1856
1906
|
if (retryAnswer) {
|
|
1857
|
-
answer = canonicalRefusal(retryAnswer
|
|
1907
|
+
answer = canonicalRefusal(retryAnswer!, refusalScope(answerCites(retryAnswer!)));
|
|
1858
1908
|
used = retryUsed;
|
|
1859
1909
|
}
|
|
1860
1910
|
}
|
|
@@ -1865,14 +1915,14 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1865
1915
|
telemetry(env, ctx, tier, "ask", model, false, 0, queryHash, q.lang, undefined, telemetryMeta());
|
|
1866
1916
|
return err(502, "generation_failed", "The generation model is unavailable; please retry.");
|
|
1867
1917
|
}
|
|
1868
|
-
const finalCites = boundModel ? [modelCitation(boundModel), ...citations(used)] : citations(used);
|
|
1869
|
-
const c2ns = answer
|
|
1918
|
+
const finalCites = boundModel ? [modelCitation(boundModel!), ...citations(used)] : citations(used);
|
|
1919
|
+
const c2ns = answer!.includes(refusalAnswer())
|
|
1870
1920
|
? { text: answer, blocks: [] as Awaited<ReturnType<typeof contractV2>>["blocks"], dropped: [] as string[] }
|
|
1871
|
-
: await contractV2(env.DB, answer
|
|
1921
|
+
: await contractV2(env.DB, answer!, used);
|
|
1872
1922
|
answer = c2ns.text;
|
|
1873
|
-
const finalAnchors = answer
|
|
1923
|
+
const finalAnchors = answer!.includes(refusalAnswer())
|
|
1874
1924
|
? { total: 0, violations: [] as string[] }
|
|
1875
|
-
: checkQuoteAnchors(answer
|
|
1925
|
+
: checkQuoteAnchors(answer!, used.map((h: Hit) => h.text));
|
|
1876
1926
|
if (finalAnchors.violations.length > 0) {
|
|
1877
1927
|
console.log("anchors:", finalAnchors.violations.length, "of", finalAnchors.total, "unverified — not caching");
|
|
1878
1928
|
}
|
|
@@ -1884,20 +1934,20 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1884
1934
|
// fallback) but contractV2 then dropped the reference because the unit
|
|
1885
1935
|
// wasn't in the used passages — leaving no block and no token.
|
|
1886
1936
|
let completionBlocks: Awaited<ReturnType<typeof completeTables>> = [];
|
|
1887
|
-
if (!answer
|
|
1888
|
-
completionBlocks = await completeTables(env.DB, answer
|
|
1937
|
+
if (!answer!.includes(refusalAnswer()) && !c2ns.blocks.some((b: any) => b.type === "table")) {
|
|
1938
|
+
completionBlocks = await completeTables(env.DB, answer!, used);
|
|
1889
1939
|
if (completionBlocks.length) console.log("contract completion:", completionBlocks.length, "table block(s) attached server-side");
|
|
1890
1940
|
}
|
|
1891
1941
|
|
|
1892
1942
|
// figure completion (#172) — see ./completion for the rationale
|
|
1893
|
-
completionBlocks.push(...(await completeFigures(env.DB, answer
|
|
1943
|
+
completionBlocks.push(...(await completeFigures(env.DB, answer!, [...c2ns.blocks, ...completionBlocks], used)));
|
|
1894
1944
|
|
|
1895
1945
|
// the blind-answer regeneration (2026-10-03, live on the photo case):
|
|
1896
1946
|
// the multimodal call can answer AROUND an attached image — a 200 whose
|
|
1897
1947
|
// text claims nothing was provided — while the bridge succeeded and the
|
|
1898
1948
|
// register note carries actual rows. One bounded regeneration, decided on
|
|
1899
1949
|
// that exact signature; the retry carries a sterner ground instruction.
|
|
1900
|
-
if (withImage && register?.rows?.length && answer
|
|
1950
|
+
if (withImage && register?.rows?.length && answer!.includes(refusalAnswer())) {
|
|
1901
1951
|
console.log("blind-answer: the register note carried rows but the answer refused — regenerating once");
|
|
1902
1952
|
mark("Re-answering from the register results");
|
|
1903
1953
|
const regen = await generateOnce(env, model, [
|
|
@@ -1905,8 +1955,8 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1905
1955
|
{ role: "user", content: "The photograph was attached and the register note lists actual matching certificate rows. Present those rows — number, holder, model, status, and every document link — as the answer. Do not claim that nothing was provided." },
|
|
1906
1956
|
messages[messages.length - 1],
|
|
1907
1957
|
], effort);
|
|
1908
|
-
if (regen && !regen
|
|
1909
|
-
answer = canonicalRefusal(regen
|
|
1958
|
+
if (regen && !regen!.includes(refusalAnswer())) {
|
|
1959
|
+
answer = canonicalRefusal(regen!, refusalScope(answerCites(regen!)));
|
|
1910
1960
|
console.log("blind-answer: regeneration grounded");
|
|
1911
1961
|
}
|
|
1912
1962
|
}
|
|
@@ -1919,25 +1969,25 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1919
1969
|
// rung note untouched; refusals and uncited answers have nothing to gate.
|
|
1920
1970
|
let entailment: { support: string; score: number; ungrounded?: string[] } | null = null;
|
|
1921
1971
|
let entailmentNote = "";
|
|
1922
|
-
if (THRESHOLDS.entailmentGate && jsonQuality && used.length && !answer
|
|
1972
|
+
if (THRESHOLDS.entailmentGate && jsonQuality && used.length && !answer!.includes(refusalAnswer())) {
|
|
1923
1973
|
const raced = await Promise.race([
|
|
1924
|
-
scoreFaithfulness(env.AI, roleModel(env, "grader"), answer
|
|
1974
|
+
scoreFaithfulness(env.AI, roleModel(env, "grader"), answer!, (used as Hit[]).map((h: Hit) => h.text)),
|
|
1925
1975
|
new Promise<null>((r) => setTimeout(() => r(null), THRESHOLDS.entailmentBudgetMs)),
|
|
1926
1976
|
]);
|
|
1927
1977
|
if (raced) {
|
|
1928
|
-
const v = entailmentVerdict(raced
|
|
1929
|
-
entailment = { support: v.support, score: Math.round(raced
|
|
1978
|
+
const v = entailmentVerdict(raced!.score, THRESHOLDS.entailmentSupportedFloor, THRESHOLDS.entailmentPartialFloor);
|
|
1979
|
+
entailment = { support: v.support, score: Math.round(raced!.score * 100) / 100, ...(raced!.ungrounded_claims?.length ? { ungrounded: raced!.ungrounded_claims.slice(0, 3) } : {}) };
|
|
1930
1980
|
entailmentNote = v.note;
|
|
1931
|
-
if (v.support !== "supported") console.log("entailment:", v.support, raced
|
|
1981
|
+
if (v.support !== "supported") console.log("entailment:", v.support, raced!.score, raced!.ungrounded_claims?.slice(0, 2));
|
|
1932
1982
|
}
|
|
1933
1983
|
}
|
|
1934
|
-
const out = { answer, citations: finalCites, ...(jsonQuality ? { source_quality: jsonQuality
|
|
1935
|
-
const cacheable = !contextual && !declaredCtx && !ablate && cacheableAnswer(answer
|
|
1984
|
+
const out = { answer, citations: finalCites, ...(jsonQuality ? { source_quality: jsonQuality!, confidence_note: entailmentNote || qualityNote(jsonQuality!), ...(entailment ? { entailment } : {}), ...(jsonQuality === "ocr" ? { experimental_sources: experimentalSourceLabels(finalCites) } : {}) } : {}), model: MODELS.member, query_hash: queryHash, follow_ups: refusalShaped(answer!, refusalScope(answerCites(answer!))) ? [] : (understanding?.follow_ups ?? []), blocks: [...c2ns.blocks, ...(verdictBlock ? [verdictBlock] : []), ...(conditionBlock ? [conditionBlock] : []), ...(aggregationBlock ? [aggregationBlock] : []), ...(registerBlock ? [registerBlock] : []), ...completionBlocks], context_applied: ctxApplied, ...(liveRecords ? { records: liveRecords } : {}), ...(ablate ? { ablate } : {}), route: { route: routed.route, features: routed.features } };
|
|
1985
|
+
const cacheable = !contextual && !declaredCtx && !ablate && cacheableAnswer(answer!, refusalScope(answerCites(answer!))) && finalAnchors.violations.length === 0;
|
|
1936
1986
|
if (cacheable) {
|
|
1937
1987
|
const ck = exactCacheKey(env.INDEX_VERSION, gen, ns, await sha256Hex(cacheKeyMaterial(q.query, q.lang, salt)));
|
|
1938
1988
|
ctx.waitUntil(env.CACHE.put(ck, JSON.stringify(out), { expirationTtl: LIMITS.cacheTtlSec }));
|
|
1939
1989
|
}
|
|
1940
|
-
telemetry(env, ctx, tier, "ask", model, true, answer
|
|
1990
|
+
telemetry(env, ctx, tier, "ask", model, true, answer!.length, queryHash, q.lang, undefined, telemetryMeta());
|
|
1941
1991
|
// grounding transparency for integrators (and the eval battery): the
|
|
1942
1992
|
// passages the answer was actually built from — response-only, never
|
|
1943
1993
|
// stored in the answer cache
|
|
@@ -1947,25 +1997,23 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1947
1997
|
text: h.text.slice(0, 1200),
|
|
1948
1998
|
...((h.metadata as any).table_selection ? { sel: (h.metadata as any).table_selection } : {}),
|
|
1949
1999
|
}));
|
|
1950
|
-
const tq = await chargeAnswerTokens(env, bucketId, undefined, messageChars(messages), answer
|
|
2000
|
+
const tq = await chargeAnswerTokens(env, bucketId, undefined, messageChars(messages!), answer!.length, probeTokensUsed);
|
|
1951
2001
|
return json({ ...out, context: contextOut, read: readAs(), quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } }, 200, { ...corsHeaders(req), "server-timing": serverTiming() });
|
|
1952
|
-
}
|
|
2002
|
+
}
|
|
1953
2003
|
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
...cors,
|
|
1963
|
-
},
|
|
1964
|
-
});
|
|
2004
|
+
// THE DISPATCH: the streamed lane's response went out at the top — the
|
|
2005
|
+
// pipeline has been feeding it live. The json lane awaits the pipeline
|
|
2006
|
+
// and returns its aggregate.
|
|
2007
|
+
if (liveResponse) {
|
|
2008
|
+
void pipeline(live!, q).catch(() => { try { live!.close(); } catch { /* the client went away */ } });
|
|
2009
|
+
return liveResponse;
|
|
2010
|
+
}
|
|
2011
|
+
return (await pipeline(null, q))!;
|
|
1965
2012
|
}
|
|
1966
2013
|
|
|
1967
2014
|
|
|
1968
2015
|
|
|
2016
|
+
|
|
1969
2017
|
/** RAGAS-style metric battery (G13): judge an (question, answer, passages)
|
|
1970
2018
|
* triple — faithfulness, answer relevancy, context precision. Driven by
|
|
1971
2019
|
* tests/eval-suite.mjs; prompts are data; refuses nothing, judges only. */
|