@konneal/engine 0.2.120 → 0.2.122
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-3BJKTVLY.js → ask-J4QFS6Q2.js} +3 -3
- package/dist/{chunk-IIQWESHC.js → chunk-2JUWER2A.js} +5 -3
- package/dist/chunk-A4A26BCV.js +89 -0
- package/dist/{chunk-SORXE55V.js → chunk-BRUHQEOT.js} +5 -3
- package/dist/{chunk-C5L55VI7.js → chunk-P6IRYZLL.js} +1 -1
- package/dist/refusal.d.ts +9 -0
- package/dist/{search-JDSQPI3A.js → search-OVUHVV35.js} +3 -3
- package/dist/worker_public/src/index.js +5 -5
- package/dist/worker_public/src/refusal.js +3 -1
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +9 -2
- package/workers/worker_public/src/refusal.ts +45 -1
- package/workers/worker_public/src/stages/rerank.ts +13 -3
- package/dist/chunk-A3QHHUN5.js +0 -54
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleAsk
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BRUHQEOT.js";
|
|
4
4
|
import "./chunk-FKG5VGOL.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-2JUWER2A.js";
|
|
6
6
|
import "./chunk-TH5XGPD5.js";
|
|
7
7
|
import "./chunk-DJ65EF5G.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-A4A26BCV.js";
|
|
9
9
|
import "./chunk-4NXH5ZI6.js";
|
|
10
10
|
import "./chunk-VT7DR6NQ.js";
|
|
11
11
|
import "./chunk-JZFXSTYX.js";
|
|
@@ -22901,9 +22901,11 @@ var rerankStage = {
|
|
|
22901
22901
|
c.hits.forEach((h, i) => h.rerank_score = scores[i]);
|
|
22902
22902
|
c.hits.sort((a, b) => (b.rerank_score ?? -Infinity) - (a.rerank_score ?? -Infinity));
|
|
22903
22903
|
if (c.filter?.doc_number) {
|
|
22904
|
-
const families = c.hits.filter((h) => h.metadata.clause_anchor === "family");
|
|
22905
|
-
|
|
22906
|
-
|
|
22904
|
+
const families = c.hits.filter((h) => h.metadata.clause_anchor === "family" && String(h.metadata.doc_number ?? "").split("-")[0] === String(c.filter.doc_number).split("-")[0]);
|
|
22905
|
+
const own = families.length ? families : c.hits.filter((h) => String(h.metadata.doc_number ?? "") === String(c.filter.doc_number).split("-")[0] && /^(overview|)$/i.test(String(h.metadata.clause_anchor ?? "")));
|
|
22906
|
+
if (own.length) {
|
|
22907
|
+
const ownIds = new Set(own.map((h) => h.id));
|
|
22908
|
+
c.hits = [...own, ...c.hits.filter((h) => !ownIds.has(h.id))];
|
|
22907
22909
|
}
|
|
22908
22910
|
}
|
|
22909
22911
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
P
|
|
3
|
+
} from "./chunk-3FYJM7LH.js";
|
|
4
|
+
|
|
5
|
+
// workers/worker_public/src/refusal.ts
|
|
6
|
+
function refusalAnswer() {
|
|
7
|
+
return P().prompts.vars.refusal_sentence;
|
|
8
|
+
}
|
|
9
|
+
function refusalPatterns(publisher) {
|
|
10
|
+
return {
|
|
11
|
+
variant: new RegExp(`^\\s*I don[\u2019']?t have information on .{1,120}? in the indexed ${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\.?`, "i"),
|
|
12
|
+
drift: [
|
|
13
|
+
new RegExp(`\\b(can'?t|cannot|couldn'?t|unable)\\b[^.]{0,120}?\\b(indexed )?${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\b`, "i"),
|
|
14
|
+
new RegExp(`\\bno real answer to give\\b[^.]{0,120}?\\b${publisher}\\b`, "i"),
|
|
15
|
+
/\b(?:falls|well) outside\b[^.]{0,120}?\b(?:what I can answer|my scope|the scope of)\b/i,
|
|
16
|
+
/\boutside (?:of )?what (?:I|this service) can answer\b/i,
|
|
17
|
+
// "I can't answer that — weather forecasting is outside my scope":
|
|
18
|
+
// requires the refusal verb, so a scope DISCUSSION inside a real answer
|
|
19
|
+
// ("this exemption is outside the scope of R 60") never matches
|
|
20
|
+
/\bI can[’']?t answer\b[^.]{0,100}?\bscope\b/i,
|
|
21
|
+
new RegExp(`^\\s*I don[\u2019']?t have any indexed ${publisher} \\w+(?:s)? (?:covering|about|on)\b`, "im"),
|
|
22
|
+
// trivia refusals in the model's own words (the refuse-trivia
|
|
23
|
+
// flake): out-of-domain content named plainly — the refusal
|
|
24
|
+
// verb is the guard, a scope DISCUSSION inside a real answer
|
|
25
|
+
// ("this exemption is outside the scope of R 60") never matches
|
|
26
|
+
/\bI don[’']?t have (?:any )?information on [^.]{0,80}(?:that[’']?s|—)?\s*(?:general|common) knowledge\b/i,
|
|
27
|
+
/\b(?:that[’']?s|this is) (?:just )?(?:general|common) knowledge[^.]{0,80}(?:outside|beyond|not covered)\b/i
|
|
28
|
+
],
|
|
29
|
+
// the misplaced-pin wall's subset: every pattern here carries a
|
|
30
|
+
// first-person refusal verb ("I don't have information", "I can't
|
|
31
|
+
// answer", "that's general knowledge") — a cited answer's scope
|
|
32
|
+
// DISCUSSION ("outside the scope of R 60's requirements") never
|
|
33
|
+
// matches, so the wall can never eat real content
|
|
34
|
+
wallSafe: [
|
|
35
|
+
new RegExp(`\\b(can'?t|cannot|couldn'?t|unable)\\b[^.]{0,120}?\\b(indexed )?${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\b`, "i"),
|
|
36
|
+
new RegExp(`\\bno real answer to give\\b[^.]{0,120}?\\b${publisher}\\b`, "i"),
|
|
37
|
+
/\bI don[’']?t have (?:any )?information on [^.]{0,80}(?:that[’']?s|—)?\s*(?:general|common) knowledge\b/i,
|
|
38
|
+
/\b(?:that[’']?s|this is) (?:just )?(?:general|common) knowledge[^.]{0,80}(?:outside|beyond|not covered)\b/i,
|
|
39
|
+
/\bI can[’']?t answer\b[^.]{0,100}?\bscope\b/i
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function sentenceStart(answer, i) {
|
|
44
|
+
let s = 0;
|
|
45
|
+
for (const sep of [". ", "! ", "? ", "\n"]) {
|
|
46
|
+
const j = answer.lastIndexOf(sep, i);
|
|
47
|
+
if (j >= 0) s = Math.max(s, j + sep.length);
|
|
48
|
+
}
|
|
49
|
+
return s;
|
|
50
|
+
}
|
|
51
|
+
function sentenceEnd(answer, i) {
|
|
52
|
+
const m = /[.!?\n]/.exec(answer.slice(i));
|
|
53
|
+
return m ? i + m.index + 1 : answer.length;
|
|
54
|
+
}
|
|
55
|
+
function canonicalRefusal(answer) {
|
|
56
|
+
const CANON = refusalAnswer();
|
|
57
|
+
if (answer.includes(CANON)) return answer;
|
|
58
|
+
const { variant: REFUSAL_VARIANT, drift: REFUSAL_DRIFT } = refusalPatterns(P().publisher.name);
|
|
59
|
+
const variant = answer.match(REFUSAL_VARIANT);
|
|
60
|
+
if (variant) return answer.replace(variant[0], CANON);
|
|
61
|
+
for (const drift of REFUSAL_DRIFT) {
|
|
62
|
+
const m = drift.exec(answer);
|
|
63
|
+
if (!m) continue;
|
|
64
|
+
return answer.slice(0, sentenceStart(answer, m.index)) + CANON + answer.slice(sentenceEnd(answer, m.index));
|
|
65
|
+
}
|
|
66
|
+
return answer;
|
|
67
|
+
}
|
|
68
|
+
function exciseMisplacedPin(answer, cites) {
|
|
69
|
+
if (!cites) return answer;
|
|
70
|
+
const CANON = refusalAnswer();
|
|
71
|
+
let out = answer;
|
|
72
|
+
if (out.includes(CANON)) {
|
|
73
|
+
const i = out.indexOf(CANON);
|
|
74
|
+
out = out.slice(0, sentenceStart(out, i)) + out.slice(sentenceEnd(out, i));
|
|
75
|
+
}
|
|
76
|
+
const { wallSafe } = refusalPatterns(P().publisher.name);
|
|
77
|
+
for (const drift of wallSafe) {
|
|
78
|
+
const m = drift.exec(out);
|
|
79
|
+
if (!m) continue;
|
|
80
|
+
out = out.slice(0, sentenceStart(out, m.index)) + out.slice(sentenceEnd(out, m.index));
|
|
81
|
+
}
|
|
82
|
+
return out.replace(/^[ \t\n]+/, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
refusalAnswer,
|
|
87
|
+
canonicalRefusal,
|
|
88
|
+
exciseMisplacedPin
|
|
89
|
+
};
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
tokenBudget,
|
|
64
64
|
understandQuery,
|
|
65
65
|
usageTotal
|
|
66
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-2JUWER2A.js";
|
|
67
67
|
import {
|
|
68
68
|
embed,
|
|
69
69
|
generateOnce,
|
|
@@ -79,8 +79,9 @@ import {
|
|
|
79
79
|
} from "./chunk-DJ65EF5G.js";
|
|
80
80
|
import {
|
|
81
81
|
canonicalRefusal,
|
|
82
|
+
exciseMisplacedPin,
|
|
82
83
|
refusalAnswer
|
|
83
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-A4A26BCV.js";
|
|
84
85
|
import {
|
|
85
86
|
entitlementScope,
|
|
86
87
|
requestSalt,
|
|
@@ -2565,7 +2566,7 @@ ${q.query}`.replace("\n\n\n\n", "\n\n") }];
|
|
|
2565
2566
|
}
|
|
2566
2567
|
}
|
|
2567
2568
|
}
|
|
2568
|
-
const canonical0 = canonicalRefusal(full);
|
|
2569
|
+
const canonical0 = usedHits.length > 0 ? exciseMisplacedPin(canonicalRefusal(full), true) : canonicalRefusal(full);
|
|
2569
2570
|
const c2 = canonical0.includes(refusalAnswer()) ? { text: canonical0, blocks: [], dropped: [] } : await contractV2(env.DB, canonical0, usedHits);
|
|
2570
2571
|
send({
|
|
2571
2572
|
type: "done",
|
|
@@ -2650,6 +2651,7 @@ ${q.query}`.replace("\n\n\n\n", "\n\n") }];
|
|
|
2650
2651
|
answer = await generateOnce(env, MODELS.fallback, flat, routeEffort(routed.route));
|
|
2651
2652
|
}
|
|
2652
2653
|
if (answer) answer = canonicalRefusal(answer);
|
|
2654
|
+
if (answer && usedHits.length > 0) answer = exciseMisplacedPin(answer, true);
|
|
2653
2655
|
stageTiming.generate = Date.now() - tGen;
|
|
2654
2656
|
let used = usedHits;
|
|
2655
2657
|
if (answer && !answer.includes(refusalAnswer())) {
|
package/dist/refusal.d.ts
CHANGED
|
@@ -7,3 +7,12 @@ export declare function refusalAnswer(): string;
|
|
|
7
7
|
* included) for the pinned one — the redirect tail survives. An answer
|
|
8
8
|
* outside the refusal family is returned byte-identical. */
|
|
9
9
|
export declare function canonicalRefusal(answer: string): string;
|
|
10
|
+
/** The misplaced-pin wall (2026-10-10): an answer that actually cites
|
|
11
|
+
* passages is NOT a refusal — but the model sometimes drops the
|
|
12
|
+
* pinned sentence into an otherwise-correct, well-cited answer as a
|
|
13
|
+
* sub-topic disclaimer (measured: the refusal pin at char 435 of a
|
|
14
|
+
* load-cell answer citing R 60). Readers and graders read the pin as
|
|
15
|
+
* a TOTAL refusal. The wall is deterministic: when the answer cites,
|
|
16
|
+
* the pin sentence (and its refusal-shaped paraphrases) are excised —
|
|
17
|
+
* the surrounding content stands on its own. */
|
|
18
|
+
export declare function exciseMisplacedPin(answer: string, cites: boolean): string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleSearch
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-P6IRYZLL.js";
|
|
4
|
+
import "./chunk-2JUWER2A.js";
|
|
5
5
|
import "./chunk-TH5XGPD5.js";
|
|
6
6
|
import "./chunk-DJ65EF5G.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-A4A26BCV.js";
|
|
8
8
|
import "./chunk-4NXH5ZI6.js";
|
|
9
9
|
import "./chunk-VT7DR6NQ.js";
|
|
10
10
|
import "./chunk-3FYJM7LH.js";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
handleMemories,
|
|
5
5
|
scoreFaithfulness,
|
|
6
6
|
scoreJudge
|
|
7
|
-
} from "../../chunk-
|
|
7
|
+
} from "../../chunk-BRUHQEOT.js";
|
|
8
8
|
import {
|
|
9
9
|
TOOLS_REGISTRY,
|
|
10
10
|
bindModelNode,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "../../chunk-FKG5VGOL.js";
|
|
13
13
|
import {
|
|
14
14
|
handleSearch
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-P6IRYZLL.js";
|
|
16
16
|
import {
|
|
17
17
|
buildMessages,
|
|
18
18
|
citations,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
sessionFrom,
|
|
35
35
|
telemetry,
|
|
36
36
|
understandQuery
|
|
37
|
-
} from "../../chunk-
|
|
37
|
+
} from "../../chunk-2JUWER2A.js";
|
|
38
38
|
import {
|
|
39
39
|
cfBlobs,
|
|
40
40
|
embed,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
} from "../../chunk-DJ65EF5G.js";
|
|
53
53
|
import {
|
|
54
54
|
canonicalRefusal
|
|
55
|
-
} from "../../chunk-
|
|
55
|
+
} from "../../chunk-A4A26BCV.js";
|
|
56
56
|
import {
|
|
57
57
|
entitlementScope,
|
|
58
58
|
standardKeysFrom
|
|
@@ -1049,7 +1049,7 @@ async function callToolOnce(env, ctx, _req, tier, key, name, args, t0) {
|
|
|
1049
1049
|
const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
|
|
1050
1050
|
payload = r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
|
|
1051
1051
|
} else {
|
|
1052
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
1052
|
+
const res = name === "ask" ? await (await import("../../ask-J4QFS6Q2.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-OVUHVV35.js")).handleSearch(env, ctx, inner, tier, key);
|
|
1053
1053
|
payload = await res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
1054
1054
|
}
|
|
1055
1055
|
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.122",
|
|
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",
|
|
@@ -17,7 +17,7 @@ import { reflect } from "./reflect";
|
|
|
17
17
|
import { scoreFaithfulness } from "./faithfulness";
|
|
18
18
|
import { entailmentVerdict } from "./entailment";
|
|
19
19
|
import { checkQuoteAnchors, ANCHOR_CORRECTION_NOTE } from "./anchors";
|
|
20
|
-
import { canonicalRefusal } from "./refusal";
|
|
20
|
+
import { canonicalRefusal, exciseMisplacedPin } from "./refusal";
|
|
21
21
|
import { contractV2, tableRetyped } from "./refs";
|
|
22
22
|
import { completeTables, completeFigures } from "./completion";
|
|
23
23
|
import { NO_CONTEXT, appliedContext, contextNote, namedDocumentIn, parseContext, resolveDocScope, syntheticUnderstanding } from "./context";
|
|
@@ -1464,7 +1464,9 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1464
1464
|
} catch { /* deliver what we have */ }
|
|
1465
1465
|
}
|
|
1466
1466
|
}
|
|
1467
|
-
|
|
1467
|
+
// the misplaced-pin wall rides the streamed path too: a
|
|
1468
|
+
// CITED stream is not a refusal
|
|
1469
|
+
const canonical0 = usedHits.length > 0 ? exciseMisplacedPin(canonicalRefusal(full), true) : canonicalRefusal(full);
|
|
1468
1470
|
// answer contract v2: validate [[u:]] refs, resolve typed blocks
|
|
1469
1471
|
const c2 = canonical0.includes(refusalAnswer())
|
|
1470
1472
|
? { text: canonical0, blocks: [], dropped: [] as string[] }
|
|
@@ -1569,6 +1571,11 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1569
1571
|
answer = await generateOnce(env, MODELS.fallback, flat, routeEffort(routed.route));
|
|
1570
1572
|
}
|
|
1571
1573
|
if (answer) answer = canonicalRefusal(answer);
|
|
1574
|
+
// the misplaced-pin wall: a CITED answer is not a refusal — excise
|
|
1575
|
+
// the pinned sentence if the model dropped it in as a disclaimer
|
|
1576
|
+
// (measured on chip-scoped asks: correct cited answers carrying the
|
|
1577
|
+
// pin mid-text, read by users and graders as total refusals)
|
|
1578
|
+
if (answer && usedHits.length > 0) answer = exciseMisplacedPin(answer, true);
|
|
1572
1579
|
stageTiming.generate = Date.now() - tGen;
|
|
1573
1580
|
|
|
1574
1581
|
// ── Deterministic quote-anchor + table-retyping check ──
|
|
@@ -10,7 +10,7 @@ export function refusalAnswer(): string {
|
|
|
10
10
|
// on how to make lasagna in the indexed..."); the API contract is the
|
|
11
11
|
// exact canonical sentence — normalize variants, keep the redirect tail.
|
|
12
12
|
// The patterns are built per call: the publisher token is profile data.
|
|
13
|
-
function refusalPatterns(publisher: string): { variant: RegExp; drift: RegExp[] } {
|
|
13
|
+
function refusalPatterns(publisher: string): { variant: RegExp; drift: RegExp[]; wallSafe: RegExp[] } {
|
|
14
14
|
return {
|
|
15
15
|
variant: new RegExp(`^\\s*I don[’']?t have information on .{1,120}? in the indexed ${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\.?`, "i"),
|
|
16
16
|
drift: [
|
|
@@ -23,6 +23,24 @@ function refusalPatterns(publisher: string): { variant: RegExp; drift: RegExp[]
|
|
|
23
23
|
// ("this exemption is outside the scope of R 60") never matches
|
|
24
24
|
/\bI can[’']?t answer\b[^.]{0,100}?\bscope\b/i,
|
|
25
25
|
new RegExp(`^\\s*I don[’']?t have any indexed ${publisher} \\w+(?:s)? (?:covering|about|on)\b`, "im"),
|
|
26
|
+
// trivia refusals in the model's own words (the refuse-trivia
|
|
27
|
+
// flake): out-of-domain content named plainly — the refusal
|
|
28
|
+
// verb is the guard, a scope DISCUSSION inside a real answer
|
|
29
|
+
// ("this exemption is outside the scope of R 60") never matches
|
|
30
|
+
/\bI don[’']?t have (?:any )?information on [^.]{0,80}(?:that[’']?s|—)?\s*(?:general|common) knowledge\b/i,
|
|
31
|
+
/\b(?:that[’']?s|this is) (?:just )?(?:general|common) knowledge[^.]{0,80}(?:outside|beyond|not covered)\b/i,
|
|
32
|
+
],
|
|
33
|
+
// the misplaced-pin wall's subset: every pattern here carries a
|
|
34
|
+
// first-person refusal verb ("I don't have information", "I can't
|
|
35
|
+
// answer", "that's general knowledge") — a cited answer's scope
|
|
36
|
+
// DISCUSSION ("outside the scope of R 60's requirements") never
|
|
37
|
+
// matches, so the wall can never eat real content
|
|
38
|
+
wallSafe: [
|
|
39
|
+
new RegExp(`\\b(can'?t|cannot|couldn'?t|unable)\\b[^.]{0,120}?\\b(indexed )?${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\b`, "i"),
|
|
40
|
+
new RegExp(`\\bno real answer to give\\b[^.]{0,120}?\\b${publisher}\\b`, "i"),
|
|
41
|
+
/\bI don[’']?t have (?:any )?information on [^.]{0,80}(?:that[’']?s|—)?\s*(?:general|common) knowledge\b/i,
|
|
42
|
+
/\b(?:that[’']?s|this is) (?:just )?(?:general|common) knowledge[^.]{0,80}(?:outside|beyond|not covered)\b/i,
|
|
43
|
+
/\bI can[’']?t answer\b[^.]{0,100}?\bscope\b/i,
|
|
26
44
|
],
|
|
27
45
|
};
|
|
28
46
|
}
|
|
@@ -61,3 +79,29 @@ export function canonicalRefusal(answer: string): string {
|
|
|
61
79
|
}
|
|
62
80
|
return answer;
|
|
63
81
|
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
/** The misplaced-pin wall (2026-10-10): an answer that actually cites
|
|
85
|
+
* passages is NOT a refusal — but the model sometimes drops the
|
|
86
|
+
* pinned sentence into an otherwise-correct, well-cited answer as a
|
|
87
|
+
* sub-topic disclaimer (measured: the refusal pin at char 435 of a
|
|
88
|
+
* load-cell answer citing R 60). Readers and graders read the pin as
|
|
89
|
+
* a TOTAL refusal. The wall is deterministic: when the answer cites,
|
|
90
|
+
* the pin sentence (and its refusal-shaped paraphrases) are excised —
|
|
91
|
+
* the surrounding content stands on its own. */
|
|
92
|
+
export function exciseMisplacedPin(answer: string, cites: boolean): string {
|
|
93
|
+
if (!cites) return answer;
|
|
94
|
+
const CANON = refusalAnswer();
|
|
95
|
+
let out = answer;
|
|
96
|
+
if (out.includes(CANON)) {
|
|
97
|
+
const i = out.indexOf(CANON);
|
|
98
|
+
out = out.slice(0, sentenceStart(out, i)) + out.slice(sentenceEnd(out, i));
|
|
99
|
+
}
|
|
100
|
+
const { wallSafe } = refusalPatterns(P().publisher.name);
|
|
101
|
+
for (const drift of wallSafe) {
|
|
102
|
+
const m = drift.exec(out);
|
|
103
|
+
if (!m) continue;
|
|
104
|
+
out = out.slice(0, sentenceStart(out, m.index)) + out.slice(sentenceEnd(out, m.index));
|
|
105
|
+
}
|
|
106
|
+
return out.replace(/^[ \t\n]+/, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
107
|
+
}
|
|
@@ -29,9 +29,19 @@ export const rerankStage: Stage = {
|
|
|
29
29
|
c.hits.forEach((h, i) => (h.rerank_score = scores[i]));
|
|
30
30
|
c.hits.sort((a, b) => (b.rerank_score ?? -Infinity) - (a.rerank_score ?? -Infinity));
|
|
31
31
|
if (c.filter?.doc_number) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// pin the scoped doc's OWN front matter: the synthetic family
|
|
33
|
+
// chunk first; documents without one (D 29 — its chunks carry
|
|
34
|
+
// overview/empty anchors, no family synthetic exists) still get
|
|
35
|
+
// their overview pinned — the tail showed rerank preferring
|
|
36
|
+
// R 106-1's prose for "What is OIML D 29?" with the scoped
|
|
37
|
+
// doc's own content present in the pool (2026-10-10)
|
|
38
|
+
const families = c.hits.filter((h) => h.metadata.clause_anchor === "family" && String(h.metadata.doc_number ?? "").split("-")[0] === String(c.filter!.doc_number).split("-")[0]);
|
|
39
|
+
const own = families.length
|
|
40
|
+
? families
|
|
41
|
+
: c.hits.filter((h) => String(h.metadata.doc_number ?? "") === String(c.filter!.doc_number).split("-")[0] && /^(overview|)$/i.test(String(h.metadata.clause_anchor ?? "")));
|
|
42
|
+
if (own.length) {
|
|
43
|
+
const ownIds = new Set(own.map((h) => h.id));
|
|
44
|
+
c.hits = [...own, ...c.hits.filter((h) => !ownIds.has(h.id))];
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
}
|
package/dist/chunk-A3QHHUN5.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
P
|
|
3
|
-
} from "./chunk-3FYJM7LH.js";
|
|
4
|
-
|
|
5
|
-
// workers/worker_public/src/refusal.ts
|
|
6
|
-
function refusalAnswer() {
|
|
7
|
-
return P().prompts.vars.refusal_sentence;
|
|
8
|
-
}
|
|
9
|
-
function refusalPatterns(publisher) {
|
|
10
|
-
return {
|
|
11
|
-
variant: new RegExp(`^\\s*I don[\u2019']?t have information on .{1,120}? in the indexed ${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\.?`, "i"),
|
|
12
|
-
drift: [
|
|
13
|
-
new RegExp(`\\b(can'?t|cannot|couldn'?t|unable)\\b[^.]{0,120}?\\b(indexed )?${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\b`, "i"),
|
|
14
|
-
new RegExp(`\\bno real answer to give\\b[^.]{0,120}?\\b${publisher}\\b`, "i"),
|
|
15
|
-
/\b(?:falls|well) outside\b[^.]{0,120}?\b(?:what I can answer|my scope|the scope of)\b/i,
|
|
16
|
-
/\boutside (?:of )?what (?:I|this service) can answer\b/i,
|
|
17
|
-
// "I can't answer that — weather forecasting is outside my scope":
|
|
18
|
-
// requires the refusal verb, so a scope DISCUSSION inside a real answer
|
|
19
|
-
// ("this exemption is outside the scope of R 60") never matches
|
|
20
|
-
/\bI can[’']?t answer\b[^.]{0,100}?\bscope\b/i,
|
|
21
|
-
new RegExp(`^\\s*I don[\u2019']?t have any indexed ${publisher} \\w+(?:s)? (?:covering|about|on)\b`, "im")
|
|
22
|
-
]
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function sentenceStart(answer, i) {
|
|
26
|
-
let s = 0;
|
|
27
|
-
for (const sep of [". ", "! ", "? ", "\n"]) {
|
|
28
|
-
const j = answer.lastIndexOf(sep, i);
|
|
29
|
-
if (j >= 0) s = Math.max(s, j + sep.length);
|
|
30
|
-
}
|
|
31
|
-
return s;
|
|
32
|
-
}
|
|
33
|
-
function sentenceEnd(answer, i) {
|
|
34
|
-
const m = /[.!?\n]/.exec(answer.slice(i));
|
|
35
|
-
return m ? i + m.index + 1 : answer.length;
|
|
36
|
-
}
|
|
37
|
-
function canonicalRefusal(answer) {
|
|
38
|
-
const CANON = refusalAnswer();
|
|
39
|
-
if (answer.includes(CANON)) return answer;
|
|
40
|
-
const { variant: REFUSAL_VARIANT, drift: REFUSAL_DRIFT } = refusalPatterns(P().publisher.name);
|
|
41
|
-
const variant = answer.match(REFUSAL_VARIANT);
|
|
42
|
-
if (variant) return answer.replace(variant[0], CANON);
|
|
43
|
-
for (const drift of REFUSAL_DRIFT) {
|
|
44
|
-
const m = drift.exec(answer);
|
|
45
|
-
if (!m) continue;
|
|
46
|
-
return answer.slice(0, sentenceStart(answer, m.index)) + CANON + answer.slice(sentenceEnd(answer, m.index));
|
|
47
|
-
}
|
|
48
|
-
return answer;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export {
|
|
52
|
-
refusalAnswer,
|
|
53
|
-
canonicalRefusal
|
|
54
|
-
};
|