@konneal/engine 0.2.155 → 0.2.157
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-MRVXR7CY.js → ask-IC5A5LIN.js} +1 -1
- package/dist/{chunk-TXF5VMNM.js → chunk-S3O7QZQ7.js} +19 -1
- package/dist/enrichPolicy.d.ts +29 -0
- package/dist/worker_public/src/index.js +17 -4
- package/package.json +1 -1
- package/workers/worker_public/src/admin.ts +7 -11
- package/workers/worker_public/src/ask.ts +27 -1
- package/workers/worker_public/src/enrichPolicy.ts +40 -0
|
@@ -2914,7 +2914,25 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
2914
2914
|
...h.metadata.table_selection ? { sel: h.metadata.table_selection } : {}
|
|
2915
2915
|
}));
|
|
2916
2916
|
const tq = await chargeAnswerTokens(env, bucketId, void 0, messageChars(messages), answer.length, probeTokensUsed);
|
|
2917
|
-
|
|
2917
|
+
const manifestOut = body?.manifest === true ? { manifest: {
|
|
2918
|
+
index_version: env.INDEX_VERSION,
|
|
2919
|
+
corpus_gen: gen,
|
|
2920
|
+
model,
|
|
2921
|
+
effort: answerEffort(env),
|
|
2922
|
+
fresh,
|
|
2923
|
+
citations: used.length,
|
|
2924
|
+
retrieval: used.map((h, i) => ({
|
|
2925
|
+
rank: i + 1,
|
|
2926
|
+
doc_id: h.metadata.doc_id,
|
|
2927
|
+
clause_anchor: h.metadata.clause_anchor ?? null,
|
|
2928
|
+
score: h.score,
|
|
2929
|
+
rerank_score: h.rerank_score ?? null
|
|
2930
|
+
})),
|
|
2931
|
+
stages: stageTiming,
|
|
2932
|
+
generate_retries: generateRetries,
|
|
2933
|
+
total_ms: Date.now() - tStart
|
|
2934
|
+
} } : {};
|
|
2935
|
+
return json({ ...out, context: contextOut, read: readAs(), ...manifestOut, quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } }, 200, { ...corsHeaders(req), "server-timing": serverTiming() });
|
|
2918
2936
|
};
|
|
2919
2937
|
if (liveResponse) {
|
|
2920
2938
|
void pipeline(live, q).catch(() => {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** The model's raw response → the situating context: trimmed, the
|
|
2
|
+
* wrapping quotes stripped, capped at 400 chars. Empty string = the
|
|
3
|
+
* model had nothing to say (the deterministic-empty class). */
|
|
4
|
+
export declare function normalizeContext(raw: unknown): string;
|
|
5
|
+
export type EnrichOutcome = {
|
|
6
|
+
action: "serve";
|
|
7
|
+
context: string;
|
|
8
|
+
cached: boolean;
|
|
9
|
+
} | {
|
|
10
|
+
action: "serve-raw";
|
|
11
|
+
} | {
|
|
12
|
+
action: "fail";
|
|
13
|
+
error: string;
|
|
14
|
+
};
|
|
15
|
+
/** The per-chunk decision, given the cache read and the generation.
|
|
16
|
+
* - a context (cached or generated) serves enriched;
|
|
17
|
+
* - an EMPTY generation in the production flow serves the chunk RAW
|
|
18
|
+
* (the deterministic-empty class, 2026-10-20: real corpus content —
|
|
19
|
+
* B 18's thin terms sections, two R 134-1 clauses — must not vanish
|
|
20
|
+
* from the index because its enrichment has nothing to say; a later
|
|
21
|
+
* forced re-enrich overwrites the vector in place);
|
|
22
|
+
* - an empty generation in mode:context / mode:ab FAILS: those
|
|
23
|
+
* builders want the signal, not a side-effecting write. */
|
|
24
|
+
export declare function enrichOutcome(opts: {
|
|
25
|
+
cached: string | null;
|
|
26
|
+
generated: string;
|
|
27
|
+
contextOnly: boolean;
|
|
28
|
+
abMode: boolean;
|
|
29
|
+
}): EnrichOutcome;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
handleMemories,
|
|
5
5
|
scoreFaithfulness,
|
|
6
6
|
scoreJudge
|
|
7
|
-
} from "../../chunk-
|
|
7
|
+
} from "../../chunk-S3O7QZQ7.js";
|
|
8
8
|
import "../../chunk-ECOYO7EX.js";
|
|
9
9
|
import {
|
|
10
10
|
TOOLS_REGISTRY,
|
|
@@ -351,6 +351,18 @@ async function handleGetShared(env, slug) {
|
|
|
351
351
|
// workers/worker_public/prompts/enrichment.md
|
|
352
352
|
var enrichment_default = "You write a retrieval context for a passage from {{CORPUS_KIND}}. The context is prepended to the passage before embedding so a semantic search can locate the passage even when the query uses different vocabulary than the passage itself.\n\nWrite ONE concise sentence (at most 40 words) that situates the passage: name the publication by its exact {{PUBLISHER_NAME}} identifier (including part or annex when applicable) and what the passage covers \u2014 paraphrasing the topic in words DIFFERENT from the passage's own. Do not copy the passage verbatim, do not add facts that are not derivable from the passage or its header, do not answer or explain the content. Reply with the context sentence only \u2014 no quotes, no preamble.\n";
|
|
353
353
|
|
|
354
|
+
// workers/worker_public/src/enrichPolicy.ts
|
|
355
|
+
function normalizeContext(raw) {
|
|
356
|
+
return typeof raw === "string" ? raw.trim().replace(/^["\']|["']$/g, "").slice(0, 400) : "";
|
|
357
|
+
}
|
|
358
|
+
function enrichOutcome(opts) {
|
|
359
|
+
const fromCache = typeof opts.cached === "string" && opts.cached;
|
|
360
|
+
if (fromCache) return { action: "serve", context: fromCache, cached: true };
|
|
361
|
+
if (opts.generated) return { action: "serve", context: opts.generated, cached: false };
|
|
362
|
+
if (opts.contextOnly || opts.abMode) return { action: "fail", error: "empty enrichment" };
|
|
363
|
+
return { action: "serve-raw" };
|
|
364
|
+
}
|
|
365
|
+
|
|
354
366
|
// workers/worker_public/prompts/doc-summary.md
|
|
355
367
|
var doc_summary_default = "You write the document-level summary for a legal-metrology publication in {{PUBLISHER_NAME}}'s retrieval index. The section summaries below cover the publication clause by clause. Write one compact abstract, three to five sentences: what the publication regulates or provides, its scope and applicability, and the structure a reader should know (parts, annexes, where the normative core sits). Use the publication's own terms exactly. No preamble, no headings.\n";
|
|
356
368
|
|
|
@@ -408,10 +420,11 @@ ${c.text.slice(0, 1500)}` }
|
|
|
408
420
|
reasoning_effort: abMode ? effort : "high"
|
|
409
421
|
});
|
|
410
422
|
const raw = typeof res?.response === "string" && res.response.trim() ? res.response : res?.choices?.[0]?.message?.content;
|
|
411
|
-
context =
|
|
423
|
+
context = normalizeContext(raw);
|
|
412
424
|
if (!context) {
|
|
413
425
|
console.log("enrich raw keys:", Object.keys(res ?? {}), "sample:", JSON.stringify(res).slice(0, 300));
|
|
414
|
-
|
|
426
|
+
const outcome = enrichOutcome({ cached: null, generated: "", contextOnly, abMode });
|
|
427
|
+
if (outcome.action === "fail") return { id: c.id, ok: false, error: outcome.error };
|
|
415
428
|
const rawText = typeof c.metadata.chunk_text === "string" && c.metadata.chunk_text ? c.metadata.chunk_text : c.text;
|
|
416
429
|
const rawVector = await embed(portModelRunner(env), MODELS.embed, rawText.slice(0, 6e3));
|
|
417
430
|
await env.VECTORIZE.upsert([{ id: c.id, values: rawVector, metadata: { ...c.metadata, chunk_text: rawText } }]);
|
|
@@ -1111,7 +1124,7 @@ async function callToolOnce(env, ctx, _req, tier, key, name, args, t0) {
|
|
|
1111
1124
|
const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
|
|
1112
1125
|
payload = r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
|
|
1113
1126
|
} else {
|
|
1114
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
1127
|
+
const res = name === "ask" ? await (await import("../../ask-IC5A5LIN.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-GAU5SEZI.js")).handleSearch(env, ctx, inner, tier, key);
|
|
1115
1128
|
payload = await res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
1116
1129
|
}
|
|
1117
1130
|
telemetry(env, ctx, tier, `mcp:${name}`, null, !payload?.error, JSON.stringify(payload).length, await sha256Hex(`${name}:${JSON.stringify(args ?? {})}`), void 0, void 0, { durationMs: Date.now() - t0 });
|
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.157",
|
|
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",
|
|
@@ -6,6 +6,7 @@ import { embed } from "./ai";
|
|
|
6
6
|
import { err, json, corsHeaders, readJson, authenticate } from "./lib/http";
|
|
7
7
|
import type { Env } from "./env";
|
|
8
8
|
import enrichmentPrompt from "../prompts/enrichment.md";
|
|
9
|
+
import { normalizeContext, enrichOutcome } from "./enrichPolicy.ts";
|
|
9
10
|
import docSummaryPrompt from "../prompts/doc-summary.md";
|
|
10
11
|
import familySummaryPrompt from "../prompts/family-summary.md";
|
|
11
12
|
import sectionSummaryPrompt from "../prompts/section-summary.md";
|
|
@@ -75,19 +76,14 @@ export async function handleEnrich(env: Env, ctx: ExecutionContext, req: Request
|
|
|
75
76
|
const raw = typeof res?.response === "string" && res.response.trim()
|
|
76
77
|
? res.response
|
|
77
78
|
: res?.choices?.[0]?.message?.content;
|
|
78
|
-
context =
|
|
79
|
+
context = normalizeContext(raw);
|
|
79
80
|
if (!context) {
|
|
80
81
|
console.log("enrich raw keys:", Object.keys(res ?? {}), "sample:", JSON.stringify(res).slice(0, 300));
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// — embed the original text without the context band, upsert
|
|
87
|
-
// without the ctx marker; a later forced re-enrich overwrites
|
|
88
|
-
// the vector in place. mode:"context" and "ab" keep failing:
|
|
89
|
-
// their builders want the signal, not a side-effecting write.
|
|
90
|
-
if (contextOnly || abMode) return { id: c.id, ok: false, error: "empty enrichment" };
|
|
82
|
+
// serve-raw: the chunk's own text is real corpus content —
|
|
83
|
+
// embed it without the context band, upsert without the ctx
|
|
84
|
+
// marker; a later forced re-enrich overwrites in place.
|
|
85
|
+
const outcome = enrichOutcome({ cached: null, generated: "", contextOnly, abMode });
|
|
86
|
+
if (outcome.action === "fail") return { id: c.id, ok: false, error: outcome.error };
|
|
91
87
|
const rawText = typeof c.metadata.chunk_text === "string" && c.metadata.chunk_text ? c.metadata.chunk_text : c.text;
|
|
92
88
|
const rawVector = await embed(portModelRunner(env), MODELS.embed, rawText.slice(0, 6000));
|
|
93
89
|
await env.VECTORIZE.upsert([{ id: c.id, values: rawVector, metadata: { ...c.metadata, chunk_text: rawText } }]);
|
|
@@ -1969,7 +1969,33 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1969
1969
|
...((h.metadata as any).table_selection ? { sel: (h.metadata as any).table_selection } : {}),
|
|
1970
1970
|
}));
|
|
1971
1971
|
const tq = await chargeAnswerTokens(env, bucketId, undefined, messageChars(messages!), answer!.length, probeTokensUsed);
|
|
1972
|
-
|
|
1972
|
+
// the reproducibility manifest (TODO.sota/14.6, v1): an opt-in, one-object
|
|
1973
|
+
// derivation record — the served snapshot identity, the answer model, the
|
|
1974
|
+
// retrieval anatomy (rank, corpus score, rerank score per used passage),
|
|
1975
|
+
// the stage timings, and the enforcement counters. Everything here is
|
|
1976
|
+
// already computed; the flag only ASSEMBLES it for the harness and for
|
|
1977
|
+
// external auditors. v2 names the CRAG grades per passage.
|
|
1978
|
+
const manifestOut = body?.manifest === true
|
|
1979
|
+
? { manifest: {
|
|
1980
|
+
index_version: env.INDEX_VERSION,
|
|
1981
|
+
corpus_gen: gen,
|
|
1982
|
+
model,
|
|
1983
|
+
effort: answerEffort(env),
|
|
1984
|
+
fresh,
|
|
1985
|
+
citations: used.length,
|
|
1986
|
+
retrieval: used.map((h: Hit, i: number) => ({
|
|
1987
|
+
rank: i + 1,
|
|
1988
|
+
doc_id: h.metadata.doc_id,
|
|
1989
|
+
clause_anchor: h.metadata.clause_anchor ?? null,
|
|
1990
|
+
score: h.score,
|
|
1991
|
+
rerank_score: h.rerank_score ?? null,
|
|
1992
|
+
})),
|
|
1993
|
+
stages: stageTiming,
|
|
1994
|
+
generate_retries: generateRetries,
|
|
1995
|
+
total_ms: Date.now() - tStart,
|
|
1996
|
+
} }
|
|
1997
|
+
: {};
|
|
1998
|
+
return json({ ...out, context: contextOut, read: readAs(), ...manifestOut, quota: { ...quota, ...tq, credits: await creditBudget(env, bucketId), credit_estimate: creditPreview } }, 200, { ...corsHeaders(req), "server-timing": serverTiming() });
|
|
1973
1999
|
}
|
|
1974
2000
|
|
|
1975
2001
|
// THE DISPATCH: the streamed lane's response went out at the top — the
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// The enrichment route's decision policy, dependency-free so plain node
|
|
2
|
+
// can load it for unit tests (admin.ts imports .md prompts that only
|
|
3
|
+
// the bundler resolves — the serve-raw policy rode the live
|
|
4
|
+
// verification alone until this extraction). The handler keeps the
|
|
5
|
+
// I/O: cache, generation, embedding, upsert; this module owns the
|
|
6
|
+
// DECISION table — what a chunk's outcome is, given what came back.
|
|
7
|
+
|
|
8
|
+
/** The model's raw response → the situating context: trimmed, the
|
|
9
|
+
* wrapping quotes stripped, capped at 400 chars. Empty string = the
|
|
10
|
+
* model had nothing to say (the deterministic-empty class). */
|
|
11
|
+
export function normalizeContext(raw: unknown): string {
|
|
12
|
+
return typeof raw === "string" ? raw.trim().replace(/^["\']|["']$/g, "").slice(0, 400) : "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type EnrichOutcome =
|
|
16
|
+
| { action: "serve"; context: string; cached: boolean }
|
|
17
|
+
| { action: "serve-raw" }
|
|
18
|
+
| { action: "fail"; error: string };
|
|
19
|
+
|
|
20
|
+
/** The per-chunk decision, given the cache read and the generation.
|
|
21
|
+
* - a context (cached or generated) serves enriched;
|
|
22
|
+
* - an EMPTY generation in the production flow serves the chunk RAW
|
|
23
|
+
* (the deterministic-empty class, 2026-10-20: real corpus content —
|
|
24
|
+
* B 18's thin terms sections, two R 134-1 clauses — must not vanish
|
|
25
|
+
* from the index because its enrichment has nothing to say; a later
|
|
26
|
+
* forced re-enrich overwrites the vector in place);
|
|
27
|
+
* - an empty generation in mode:context / mode:ab FAILS: those
|
|
28
|
+
* builders want the signal, not a side-effecting write. */
|
|
29
|
+
export function enrichOutcome(opts: {
|
|
30
|
+
cached: string | null;
|
|
31
|
+
generated: string;
|
|
32
|
+
contextOnly: boolean;
|
|
33
|
+
abMode: boolean;
|
|
34
|
+
}): EnrichOutcome {
|
|
35
|
+
const fromCache = typeof opts.cached === "string" && opts.cached;
|
|
36
|
+
if (fromCache) return { action: "serve", context: fromCache, cached: true };
|
|
37
|
+
if (opts.generated) return { action: "serve", context: opts.generated, cached: false };
|
|
38
|
+
if (opts.contextOnly || opts.abMode) return { action: "fail", error: "empty enrichment" };
|
|
39
|
+
return { action: "serve-raw" };
|
|
40
|
+
}
|