llm-output-guard 1.2.1 → 1.3.0
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/README.md +30 -0
- package/dist/{adapter-options-Da4mVjXh.d.ts → adapter-options-ClbcmvaY.d.ts} +1 -1
- package/dist/{adapter-options-LkLsvNy6.d.cts → adapter-options-DpTwyLVK.d.cts} +1 -1
- package/dist/ai-sdk.cjs +31 -3
- package/dist/ai-sdk.cjs.map +1 -1
- package/dist/ai-sdk.d.cts +3 -3
- package/dist/ai-sdk.d.ts +3 -3
- package/dist/ai-sdk.js +2 -2
- package/dist/anthropic.cjs +31 -3
- package/dist/anthropic.cjs.map +1 -1
- package/dist/anthropic.d.cts +3 -3
- package/dist/anthropic.d.ts +3 -3
- package/dist/anthropic.js +3 -3
- package/dist/{chunk-4X6WOBSA.js → chunk-6G6BDPQP.js} +4 -4
- package/dist/{chunk-4X6WOBSA.js.map → chunk-6G6BDPQP.js.map} +1 -1
- package/dist/{chunk-4YJWBJ4K.js → chunk-Q6W2JCSE.js} +3 -3
- package/dist/{chunk-4YJWBJ4K.js.map → chunk-Q6W2JCSE.js.map} +1 -1
- package/dist/{chunk-XHP4LSIH.js → chunk-T4DJ6IFG.js} +33 -5
- package/dist/chunk-T4DJ6IFG.js.map +1 -0
- package/dist/index.cjs +31 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/openai.cjs +31 -3
- package/dist/openai.cjs.map +1 -1
- package/dist/openai.d.cts +3 -3
- package/dist/openai.d.ts +3 -3
- package/dist/openai.js +3 -3
- package/dist/{stream-D-GVZ6iE.d.cts → stream-DUEP28_p.d.cts} +22 -0
- package/dist/{stream-D-GVZ6iE.d.ts → stream-DUEP28_p.d.ts} +22 -0
- package/package.json +1 -1
- package/dist/chunk-XHP4LSIH.js.map +0 -1
package/README.md
CHANGED
|
@@ -121,6 +121,35 @@ as a missing key rather than as whatever the schema calls it.
|
|
|
121
121
|
> This is the one thing in the package that throws about your configuration; it
|
|
122
122
|
> still never throws about a response.
|
|
123
123
|
|
|
124
|
+
#### Arrays of repeated records
|
|
125
|
+
|
|
126
|
+
A model asked for the status of twenty services and returning twenty identical
|
|
127
|
+
rows has done what it was told. Measured across the document that is a perfect
|
|
128
|
+
loop, so `TAIL_LOOP` reads **1.000** and the response fails — under every preset,
|
|
129
|
+
`lenient` included. Three identical records is enough, and an array that is only
|
|
130
|
+
75% repetitive fails on `REPETITION`.
|
|
131
|
+
|
|
132
|
+
The scores are not wrong; twenty identical records *are* exactly periodic. The
|
|
133
|
+
detectors are being asked about the wrong span. If your payloads look like this,
|
|
134
|
+
scope them:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
checkOutput(raw, { ...presets.strictJson, redundancyScope: 'jsonValues' });
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`REPETITION` and `TAIL_LOOP` then read each string value of a parsed payload on
|
|
141
|
+
its own — repetition **across records** is the shape you asked for, repetition
|
|
142
|
+
**inside a value** is the signal.
|
|
143
|
+
|
|
144
|
+
It is more sensitive, not less. A loop confined to one element of an array is
|
|
145
|
+
averaged away across a document and reads clearly on its own, so this closes a
|
|
146
|
+
false negative as well as a false positive. Text that does not parse is measured
|
|
147
|
+
as a document regardless, so prose, truncated payloads and every mid-stream check
|
|
148
|
+
are unaffected, as are the six non-redundancy detectors.
|
|
149
|
+
|
|
150
|
+
It is **opt-in**: switching it on by default would change which of your responses
|
|
151
|
+
get discarded, and this package treats that as a major.
|
|
152
|
+
|
|
124
153
|
### Streaming, where it stops costing you tokens
|
|
125
154
|
|
|
126
155
|
Checking a finished response tells you that you already paid for it. A model
|
|
@@ -772,6 +801,7 @@ patches.
|
|
|
772
801
|
- `REPETITION` does not work on Chinese, Japanese or Thai. See above — this is a known, measured gap, not an oversight.
|
|
773
802
|
- Language detection is a function-word heuristic covering `id`/`en`/`es`. Opt-in, and unreliable under 25 words.
|
|
774
803
|
- Truncation from a missing full stop is weak evidence, scored 0.55 and left below the default thresholds on purpose. Lower `maxTruncation` to ~0.5 to catch it, and expect false positives.
|
|
804
|
+
- A JSON array of repeated identical records reads as a loop under the default scope, and fails from three records up. Set `redundancyScope: 'jsonValues'` — see **Structured output**.
|
|
775
805
|
- Thresholds calibrated on the bundled corpus. Yours will differ — and the word and character thresholds need calibrating **separately**, because they are separate distributions.
|
|
776
806
|
|
|
777
807
|
## License
|
package/dist/ai-sdk.cjs
CHANGED
|
@@ -225,6 +225,27 @@ function languageMismatchScore(text, expected, options = {}) {
|
|
|
225
225
|
return Math.min(1, (best - target) / best);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
// src/internal/json-scope.ts
|
|
229
|
+
function stringValues(value, out = []) {
|
|
230
|
+
if (typeof value === "string") out.push(value);
|
|
231
|
+
else if (Array.isArray(value)) for (const item of value) stringValues(item, out);
|
|
232
|
+
else if (value !== null && typeof value === "object") {
|
|
233
|
+
for (const item of Object.values(value)) stringValues(item, out);
|
|
234
|
+
}
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
237
|
+
function redundancySpans(text, scope) {
|
|
238
|
+
if (scope !== "jsonValues") return [text];
|
|
239
|
+
let parsed;
|
|
240
|
+
try {
|
|
241
|
+
parsed = JSON.parse(stripFence(text));
|
|
242
|
+
} catch {
|
|
243
|
+
return [text];
|
|
244
|
+
}
|
|
245
|
+
const values = stringValues(parsed);
|
|
246
|
+
return values.length > 0 ? values : [""];
|
|
247
|
+
}
|
|
248
|
+
|
|
228
249
|
// src/check.ts
|
|
229
250
|
var DEFAULTS = {
|
|
230
251
|
minLength: 1,
|
|
@@ -237,7 +258,8 @@ var DEFAULTS = {
|
|
|
237
258
|
expectJson: false,
|
|
238
259
|
allowJsonFence: true,
|
|
239
260
|
maxLangMismatch: 0.6,
|
|
240
|
-
ngram: 3
|
|
261
|
+
ngram: 3,
|
|
262
|
+
redundancyScope: "document"
|
|
241
263
|
};
|
|
242
264
|
function checkOutput(text, options = {}) {
|
|
243
265
|
const opts = { ...DEFAULTS, ...options };
|
|
@@ -273,8 +295,9 @@ function checkOutput(text, options = {}) {
|
|
|
273
295
|
`Response is ${text.trim().length} chars, below the ${opts.minLength} minimum.`
|
|
274
296
|
);
|
|
275
297
|
}
|
|
298
|
+
const spans = redundancySpans(text, opts.redundancyScope);
|
|
276
299
|
if (opts.maxRepetition != null) {
|
|
277
|
-
const s = repetitionScore(
|
|
300
|
+
const s = Math.max(...spans.map((span) => repetitionScore(span, { n: opts.ngram })));
|
|
278
301
|
add(
|
|
279
302
|
"REPETITION",
|
|
280
303
|
s,
|
|
@@ -283,7 +306,12 @@ function checkOutput(text, options = {}) {
|
|
|
283
306
|
);
|
|
284
307
|
}
|
|
285
308
|
{
|
|
286
|
-
|
|
309
|
+
let worst = { score: -1, mode: "word" };
|
|
310
|
+
for (const span of spans) {
|
|
311
|
+
const detail = tailLoopDetail(span, { nonSpacedCutoff: opts.nonSpacedCutoff });
|
|
312
|
+
if (detail.score > worst.score) worst = detail;
|
|
313
|
+
}
|
|
314
|
+
const { score, mode } = worst;
|
|
287
315
|
const threshold = mode === "char" ? opts.maxCharTailLoop : opts.maxTailLoop;
|
|
288
316
|
if (threshold != null) {
|
|
289
317
|
add(
|
package/dist/ai-sdk.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/tokenize.ts","../src/detectors/repetition.ts","../src/detectors/compressibility.ts","../src/detectors/emptiness.ts","../src/detectors/truncation.ts","../src/detectors/json.ts","../src/detectors/language.ts","../src/check.ts","../src/stream.ts","../src/internal/tool-calls.ts","../src/ai-sdk.ts"],"names":[],"mappings":"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","file":"ai-sdk.cjs","sourcesContent":["import type { TokenMode } from '../types.js';\n\n/**\n * Word tokenizer for scripts that separate words -- with spaces, punctuation,\n * or anything else that is not a letter or digit. Latin, Cyrillic, Greek,\n * Hangul, Arabic, Devanagari and friends all tokenize correctly here.\n *\n * It does *not* work for Han, Kana or Thai. Those write without inter-word\n * spaces, so a whole punctuation-delimited clause matches as one token, and a\n * loop with no punctuation inside it matches as one token for the entire\n * response. See {@link nonSpacedRatio} for how that case is detected and\n * `tailLoopScore` for what runs instead.\n */\nexport function words(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}']+/gu) ?? [];\n}\n\n/** Character tokens, whitespace dropped. The fallback where `words` cannot see. */\nexport function chars(text: string): string[] {\n return [...text.replace(/\\s+/g, '')];\n}\n\n/** Scripts that do not put spaces between words. */\nconst NON_SPACED = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Thai}]/gu;\n\n/**\n * Share of a span written in a script `words()` cannot tokenize, 0..1.\n *\n * The denominator counts marks as well as letters on purpose.\n * `\\p{Script=Thai}` matches Thai vowel and tone marks, which are `\\p{M}` and\n * not `\\p{L}` -- so counting `\\p{L}` underneath returned ratios above 1 for\n * Thai and made any cutoff meaningless there. Numerator and denominator have\n * to count the same set.\n */\nexport function nonSpacedRatio(text: string): number {\n const total = text.match(/[\\p{L}\\p{M}]/gu)?.length ?? 0;\n if (total === 0) return 0;\n return (text.match(NON_SPACED)?.length ?? 0) / total;\n}\n\n/**\n * Which tokenizer suits this span.\n *\n * Decide it from the span a detector actually reads, never from the whole\n * response. A reply that answers in English and then loops in Chinese measures\n * 0.35 overall and 1.00 across its tail: judging the tail by the whole\n * response's ratio puts the tail detector in word mode on text that has no\n * words in it, which is the exact failure this dispatch exists to prevent.\n */\nexport function tokenModeOf(text: string, cutoff = 0.5): TokenMode {\n return nonSpacedRatio(text) >= cutoff ? 'char' : 'word';\n}\n\n/** Clamp a raw signal into the 0..1 suspicion range. */\nexport function clamp01(n: number): number {\n if (Number.isNaN(n)) return 0;\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n\n/** Short, safe excerpt for messages. Never leaks a full response into logs. */\nexport function excerpt(text: string, max = 80): string {\n const flat = text.replace(/\\s+/g, ' ').trim();\n return flat.length <= max ? flat : flat.slice(0, max) + '\\u2026';\n}\n","import type { TokenMode } from '../types.js';\nimport { words, chars, clamp01, tokenModeOf } from '../internal/tokenize.js';\n\nexport interface RepetitionOptions {\n /** N-gram size. 3 suits prose; 2 is noisy, 4 misses short loops. */\n n?: number;\n /** Only analyse the first N characters. Keeps cost bounded on long outputs. */\n maxSample?: number;\n}\n\n/**\n * Fraction of n-grams that are duplicates. 0 = every n-gram unique, 1 = total collapse.\n *\n * Healthy prose sits near 0.00-0.10. A model stuck in a loop passes 0.5 quickly.\n * Returns 0 for text too short to judge rather than guessing.\n *\n * **Word mode only, and knowingly blind to non-spaced scripts.** In Chinese,\n * Japanese or Thai a punctuation-delimited clause is one token and a loop with\n * no punctuation is one token for the whole response, so this scores 0.000 on\n * an obvious Chinese loop.\n *\n * A character n-gram fallback was built and rejected. Not because no threshold\n * exists -- one does, around 0.7 -- but because **it would buy no coverage and\n * cost a false-positive surface.**\n *\n * Coverage: `tailLoopScore`'s character mode already catches every degenerate\n * non-Latin fixture in the corpus, at a margin of 0.538. There is nothing left\n * for a character-mode `REPETITION` to find.\n *\n * Cost: healthy *structured* CJK output scores high here. Repeated key\n * scaffolding around short CJK values is genuinely redundant character by\n * character, and `json-zh-keys-valid` measures 0.543 over twenty distinct\n * items. The curve flattens rather than diverging -- 0.396 at eight, 0.577 at\n * thirty, 0.597 at forty, converging on the scaffolding's own proportion -- so\n * the plateau near 0.6 against the weakest pure loop at 0.872 leaves about\n * 0.19. That is under this package's own 0.2 bar, and the healthy side rises\n * with the number of keys a payload carries, which nothing bounds.\n *\n * A detector with no coverage to add and a structure-sensitive margin is a\n * false positive waiting for someone's payload shape to change.\n *\n * `tailLoopScore` covers the gap instead: it requires *exact periodicity*, which\n * scaffolding never produces, and it caught every degenerate CJK sample in the\n * corpus. See the `Limitations` section of the README.\n */\nexport function repetitionScore(text: string, options: RepetitionOptions = {}): number {\n const { n = 3, maxSample = 8000 } = options;\n const w = words(text.slice(0, maxSample));\n if (w.length < n * 4) return 0;\n\n const seen = new Set<string>();\n let total = 0;\n for (let i = 0; i + n <= w.length; i++) {\n seen.add(w.slice(i, i + n).join(' '));\n total++;\n }\n if (total === 0) return 0;\n return clamp01(1 - seen.size / total);\n}\n\nexport interface TailLoopOptions {\n /** How many trailing words to inspect in word mode. */\n tailWords?: number;\n /** Longest loop period to look for, in words. */\n maxPeriod?: number;\n /** A block must repeat at least this many times to count as a loop. */\n minRepeats?: number;\n /** How many trailing characters to inspect in char mode. Default 400. */\n tailChars?: number;\n /** Longest loop period to look for in char mode, in characters. Default 80. */\n maxCharPeriod?: number;\n /**\n * Characters required before char mode will judge at all. Default 80.\n *\n * Word mode's floor is a word count, which on a non-spaced script can be\n * satisfied by a single token, so char mode needs its own. Below this the\n * detector abstains: three short sentences ending a 40-character reply are\n * indistinguishable from a loop by coverage alone, and abstaining is the\n * rule everywhere else in this package.\n */\n minCharSample?: number;\n /** Force a tokenizer instead of dispatching on the tail's script. */\n mode?: TokenMode;\n /** Non-spaced-script share at which char mode takes over. Default 0.5. */\n nonSpacedCutoff?: number;\n}\n\nexport interface TailLoopResult {\n /** Fraction of the inspected tail covered by the repeating block. */\n score: number;\n /** Which tokenizer produced `score`. */\n mode: TokenMode;\n}\n\n/**\n * Largest share of `tail` covered by a block repeating to its end.\n *\n * Shared by both modes so the two cannot drift apart: word mode passes word\n * tokens, char mode passes characters, and the periodicity search is the same\n * code either way.\n */\nfunction periodicCoverage(\n tail: readonly string[],\n maxPeriod: number,\n minRepeats: number,\n): number {\n if (tail.length < minRepeats * 2) return 0;\n\n let best = 0;\n const periodCap = Math.min(maxPeriod, Math.floor(tail.length / minRepeats));\n for (let p = 1; p <= periodCap; p++) {\n const block = tail.slice(tail.length - p);\n let repeats = 1;\n let cursor = tail.length - p;\n while (cursor - p >= 0) {\n let same = true;\n for (let k = 0; k < p; k++) {\n if (tail[cursor - p + k] !== block[k]) { same = false; break; }\n }\n if (!same) break;\n repeats++;\n cursor -= p;\n }\n if (repeats >= minRepeats) {\n best = Math.max(best, clamp01((repeats * p) / tail.length));\n }\n }\n return best;\n}\n\n/**\n * Detects the specific failure where a model terminates in a repeating tail --\n * the same clause emitted over and over until max_tokens runs out.\n *\n * Whole-output repetition misses this when the first half of the response was\n * fine. Returns the fraction of the inspected tail covered by the loop, plus\n * the tokenizer that measured it.\n *\n * **The mode is decided from the tail, not the whole response.** A reply that\n * answers in English and then loops in Chinese is 0.35 non-spaced overall and\n * 1.00 across its final 400 characters; dispatching on the former would run\n * word tokenization over text that yields one token, and score 0.000 on an\n * obvious loop. Measured on that shape, whole-response dispatch missed it\n * entirely and tail dispatch scored 1.000.\n *\n * The two modes are **not interchangeable numbers**. Character n-grams\n * duplicate at a different base rate, so each has its own threshold\n * (`maxTailLoop`, `maxCharTailLoop`) and `Verdict.modes` reports which one ran.\n */\nexport function tailLoopDetail(text: string, options: TailLoopOptions = {}): TailLoopResult {\n const {\n tailWords = 200,\n maxPeriod = 40,\n minRepeats = 3,\n tailChars = 400,\n maxCharPeriod = 80,\n minCharSample = 80,\n nonSpacedCutoff = 0.5,\n } = options;\n\n const charTail = chars(text).slice(-tailChars);\n const mode = options.mode ?? tokenModeOf(charTail.join(''), nonSpacedCutoff);\n\n if (mode === 'char') {\n if (charTail.length < minCharSample) return { score: 0, mode };\n return { score: periodicCoverage(charTail, maxCharPeriod, minRepeats), mode };\n }\n\n const tail = words(text).slice(-tailWords);\n return { score: periodicCoverage(tail, maxPeriod, minRepeats), mode };\n}\n\n/** {@link tailLoopDetail} without the mode, for callers that only want the score. */\nexport function tailLoopScore(text: string, options: TailLoopOptions = {}): number {\n return tailLoopDetail(text, options).score;\n}\n","import { clamp01 } from '../internal/tokenize.js';\n\nexport interface CompressibilityOptions {\n /** Sliding window for back-references, in characters. */\n window?: number;\n /** Only analyse the first N characters. */\n maxSample?: number;\n /** Shortest back-reference worth emitting. */\n minMatch?: number;\n}\n\n/**\n * Greedy LZ77 pass returning emitted-tokens / input-characters.\n *\n * Deliberately hand-rolled instead of node:zlib so the package stays\n * runtime-agnostic (browser, edge, Deno, Bun) and dependency-free.\n * This is not a real compressor; it only needs to move monotonically\n * with redundancy, which is all the score requires.\n *\n * Measured against the fixture corpus: healthy output lands at 0.67-0.97,\n * degenerate collapse at 0.007-0.042, and tail loops in between at 0.17-0.20.\n * The gap either side of that middle band is what the pivot below trades on.\n */\nexport function compressionRatio(text: string, options: CompressibilityOptions = {}): number {\n const { window = 1024, maxSample = 4000, minMatch = 4 } = options;\n const s = text.slice(0, maxSample);\n if (s.length < 64) return 1;\n\n let i = 0;\n let emitted = 0;\n while (i < s.length) {\n let bestLen = 0;\n const start = i > window ? i - window : 0;\n for (let j = start; j < i; j++) {\n let k = 0;\n while (k < 255 && i + k < s.length && s[j + k] === s[i + k]) k++;\n if (k > bestLen) {\n bestLen = k;\n if (bestLen >= 255) break;\n }\n }\n emitted++;\n i += bestLen >= minMatch ? bestLen : 1;\n }\n return emitted / s.length;\n}\n\n/**\n * Suspicion score derived from {@link compressionRatio}.\n * `pivot` is the ratio treated as fully healthy; lower ratios scale up toward 1.\n *\n * At the default 0.32 every healthy fixture clamps to exactly 0, with the\n * nearest one still twice the pivot away -- so this detector is deliberately\n * tuned for outright entropy collapse and abstains on everything milder.\n * Tail loops score 0.37-0.48 here and are left to `tailLoopScore`, which\n * separates them far more cleanly (0.90 against a healthy max of 0.00).\n * Raising the pivot would make this fire on loops too, buying redundant\n * coverage with the margin that currently makes a false positive so unlikely.\n */\nexport function compressibilityScore(\n text: string,\n options: CompressibilityOptions & { pivot?: number } = {},\n): number {\n const { pivot = 0.32, ...rest } = options;\n if (text.trim().length < 64) return 0;\n return clamp01(1 - compressionRatio(text, rest) / pivot);\n}\n","import { words } from '../internal/tokenize.js';\n\n/**\n * 1 when the response carries no usable content at all.\n *\n * Covers the cases a plain `!text` check misses: whitespace-only, a lone\n * punctuation mark, an empty code fence, or an empty JSON envelope.\n */\nexport function emptinessScore(text: string): number {\n const trimmed = text.trim();\n if (trimmed.length === 0) return 1;\n if (words(trimmed).length === 0) return 1;\n const stripped = trimmed\n .replace(/```[a-z]*\\s*```/gi, '')\n .replace(/^[{}[\\]\"'\\s,.:;!?-]+$/g, '');\n return stripped.trim().length === 0 ? 1 : 0;\n}\n\n/** 1 when the response is shorter than `minChars`, scaling down to 0 at the threshold. */\nexport function shortnessScore(text: string, minChars: number): number {\n if (minChars <= 0) return 0;\n const len = text.trim().length;\n if (len >= minChars) return 0;\n return 1 - len / minChars;\n}\n","export interface TruncationOptions {\n /**\n * The provider's own stop reason, if you have it. When this says the output\n * hit the token ceiling, that is authoritative and the heuristics are skipped.\n */\n finishReason?: string;\n}\n\nconst LENGTH_STOPS = new Set(['length', 'max_tokens', 'maxtokens', 'max_output_tokens', 'token_limit']);\nconst TERMINAL = /[.!?\"'`\\u2019\\u201d)\\]}:;\\u3002\\uff01\\uff1f]\\s*$/;\n\n/**\n * Detects output that stopped mid-thought.\n *\n * Prefers the provider's finish_reason when supplied, because that is ground\n * truth. Falls back to structural signals: unbalanced fences or brackets, or a\n * final sentence with no terminal punctuation.\n *\n * Returns a graded score, not a boolean -- a missing full stop alone is weak\n * evidence and should not sink a response on its own.\n */\nexport function truncationScore(text: string, options: TruncationOptions = {}): number {\n const { finishReason } = options;\n if (finishReason && LENGTH_STOPS.has(finishReason.toLowerCase())) return 1;\n\n const trimmed = text.trim();\n if (trimmed.length === 0) return 0;\n\n let score = 0;\n\n const fences = (trimmed.match(/```/g) ?? []).length;\n if (fences % 2 === 1) score = Math.max(score, 0.9);\n\n for (const [open, close] of [['{', '}'], ['[', ']'], ['(', ')']] as const) {\n const opens = trimmed.split(open).length - 1;\n const closes = trimmed.split(close).length - 1;\n if (opens > closes) score = Math.max(score, 0.8);\n }\n\n if (!TERMINAL.test(trimmed)) score = Math.max(score, 0.55);\n\n return score;\n}\n","import type { StandardSchemaV1 } from '../standard-schema.js';\n\nexport interface JsonOptions {\n /** Allow the payload to sit inside a ```json fence rather than being bare. */\n allowFence?: boolean;\n /** Top-level keys that must be present for the payload to count as valid. */\n requiredKeys?: string[];\n /**\n * A Standard Schema validator the payload must satisfy -- Zod 4, Valibot,\n * ArkType, or anything else implementing the spec.\n *\n * Strictly stronger than `requiredKeys`, which only asks whether a name is\n * present and says nothing about its type, and the two compose: keys are\n * checked first, so a missing one is still reported as a missing key rather\n * than as whatever the schema calls it.\n *\n * **Must validate synchronously.** See {@link JsonResult.reason}.\n */\n schema?: StandardSchemaV1;\n}\n\nexport interface JsonResult {\n /** 0 when the payload parses and satisfies every contract, 1 otherwise. */\n score: number;\n /**\n * The payload, when parsing succeeded.\n *\n * When a `schema` validated it, this is the schema's *output* rather than the\n * raw parse -- so Zod defaults, coercions and transforms are applied, and the\n * value is the one your types describe. Without a schema it is `JSON.parse`'s\n * result unchanged.\n */\n value?: unknown;\n reason?: 'unparseable' | 'missing-keys' | 'schema';\n missingKeys?: string[];\n /** Messages from a failing `schema`, path-prefixed where the issue had one. */\n issues?: string[];\n}\n\n/** Pull a JSON payload out of a ```json fence, or return the text unchanged. */\nexport function stripFence(text: string): string {\n const fenced = text.trim().match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i);\n return fenced ? fenced[1] : text.trim();\n}\n\n/** `notes.0.body: Expected string` -- the path is what makes an issue actionable. */\nfunction describe(issue: StandardSchemaV1.Issue): string {\n const path = (issue.path ?? [])\n .map((segment) =>\n typeof segment === 'object' && segment !== null && 'key' in segment\n ? String(segment.key)\n : String(segment),\n )\n .join('.');\n return path ? `${path}: ${issue.message}` : issue.message;\n}\n\n/**\n * Structured-output check. Models that \"succeed\" while emitting prose around\n * the JSON, or an object missing half its keys, fail here.\n */\nexport function jsonScore(text: string, options: JsonOptions = {}): JsonResult {\n const { allowFence = true, requiredKeys = [], schema } = options;\n const candidate = allowFence ? stripFence(text) : text.trim();\n\n let value: unknown;\n try {\n value = JSON.parse(candidate);\n } catch {\n return { score: 1, reason: 'unparseable' };\n }\n\n if (requiredKeys.length > 0) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return { score: 1, value, reason: 'missing-keys', missingKeys: [...requiredKeys] };\n }\n const record = value as Record<string, unknown>;\n const missing = requiredKeys.filter((k) => !(k in record));\n if (missing.length > 0) {\n return { score: 1, value, reason: 'missing-keys', missingKeys: missing };\n }\n }\n\n if (schema) {\n const result = schema['~standard'].validate(value);\n\n /*\n * A thenable here is the caller's configuration, not the model's output,\n * and it is the one thing in this package that throws on purpose.\n *\n * `checkOutput` promises never to throw *about a response*, because a\n * TypeError raised on bad model output is not a `DegenerateOutputError` and\n * so slips straight through the retry predicate the README recommends. That\n * reasoning does not extend to a schema wired up wrong: no verdict about it\n * would be true, `{ ok: true }` would silently disable the check the caller\n * asked for, and `{ ok: false }` would blame the model for the caller's\n * bug. Throwing surfaces it on the first call, in development, with the fix\n * in the message -- which is where a misconfiguration should surface.\n *\n * In practice this is reached only by a schema carrying an async refinement.\n * Zod, Valibot and ArkType all validate synchronously otherwise.\n */\n if (typeof (result as PromiseLike<unknown>)?.then === 'function') {\n throw new TypeError(\n 'llm-output-guard: `schema` must validate synchronously, and this one returned a promise. ' +\n 'checkOutput is synchronous by design. Remove the async refinement, or validate ' +\n 'the payload yourself after checkOutput returns.',\n );\n }\n\n const sync = result as StandardSchemaV1.Result<unknown>;\n if (sync.issues) {\n return { score: 1, value, reason: 'schema', issues: sync.issues.map(describe) };\n }\n // The schema's output, not the raw parse: defaults and transforms applied.\n return { score: 0, value: sync.value };\n }\n\n return { score: 0, value };\n}\n","import { words } from '../internal/tokenize.js';\n\n/**\n * Function-word frequency profiles. Coarse by design: this catches a model\n * answering in the wrong language entirely, not dialect or register drift.\n * Off by default in every preset for exactly that reason.\n */\nconst PROFILES: Record<string, Set<string>> = {\n id: new Set(['yang', 'dan', 'di', 'untuk', 'dengan', 'ini', 'itu', 'dari', 'pada', 'tidak', 'adalah', 'akan', 'bisa', 'kita', 'saya', 'atau', 'juga', 'dalam', 'sudah', 'ke']),\n en: new Set(['the', 'and', 'of', 'to', 'in', 'is', 'that', 'for', 'it', 'with', 'as', 'this', 'are', 'be', 'you', 'on', 'not', 'or', 'can', 'we']),\n es: new Set(['el', 'la', 'de', 'que', 'y', 'en', 'los', 'un', 'por', 'con', 'las', 'para', 'una', 'es', 'no', 'se', 'del', 'al', 'lo', 'como']),\n};\n\nexport interface LanguageOptions {\n /** Below this word count the signal is unreliable and the score is 0. */\n minWords?: number;\n}\n\n/** Share of tokens matching each known profile. Not a full language detector. */\nexport function languageProfile(text: string): Record<string, number> {\n const w = words(text);\n const out: Record<string, number> = {};\n if (w.length === 0) return out;\n for (const [lang, set] of Object.entries(PROFILES)) {\n let hits = 0;\n for (const token of w) if (set.has(token)) hits++;\n out[lang] = hits / w.length;\n }\n return out;\n}\n\n/**\n * Suspicion that the response is not in `expected`.\n * Returns 0 for unknown languages or samples too short to judge --\n * silence is better than a confident wrong answer here.\n */\nexport function languageMismatchScore(\n text: string,\n expected: string,\n options: LanguageOptions = {},\n): number {\n const { minWords = 25 } = options;\n if (!(expected in PROFILES)) return 0;\n const w = words(text);\n if (w.length < minWords) return 0;\n\n const profile = languageProfile(text);\n const target = profile[expected] ?? 0;\n const best = Math.max(...Object.values(profile));\n if (best === 0) return 0;\n if (target >= best) return 0;\n return Math.min(1, (best - target) / best);\n}\n\nexport const supportedLanguages = Object.keys(PROFILES);\n","import type { CheckOptions, Reason, ReasonCode, TokenMode, Verdict } from './types.js';\nimport { repetitionScore, tailLoopDetail } from './detectors/repetition.js';\nimport { compressibilityScore } from './detectors/compressibility.js';\nimport { emptinessScore, shortnessScore } from './detectors/emptiness.js';\nimport { truncationScore } from './detectors/truncation.js';\nimport { jsonScore } from './detectors/json.js';\nimport { languageMismatchScore } from './detectors/language.js';\nimport { excerpt } from './internal/tokenize.js';\n\nconst DEFAULTS: Required<\n Pick<CheckOptions,\n 'minLength' | 'maxRepetition' | 'maxTailLoop' | 'maxCompressibility' |\n 'maxTruncation' | 'expectJson' | 'allowJsonFence' | 'maxLangMismatch' | 'ngram' |\n 'maxCharTailLoop' | 'nonSpacedCutoff'>\n> = {\n minLength: 1,\n maxRepetition: 0.35,\n maxTailLoop: 0.5,\n maxCharTailLoop: 0.7,\n nonSpacedCutoff: 0.5,\n maxCompressibility: 0.75,\n maxTruncation: null as unknown as number,\n expectJson: false,\n allowJsonFence: true,\n maxLangMismatch: 0.6,\n ngram: 3,\n};\n\n/**\n * Runs every enabled detector and returns a structured verdict.\n *\n * Pure and synchronous: no network, no clock, no randomness. The same input\n * always produces the same verdict, which is what makes it safe to put on a\n * hot path and easy to unit test.\n *\n * Every detector runs even after one fails, so `reasons` shows the full picture\n * rather than whichever check happened to be ordered first.\n *\n * Never throws. A `null`, `undefined`, or otherwise non-string input is a\n * verdict (`EMPTY`), not an exception -- see the guard below for why.\n */\nexport function checkOutput(\n text: string | null | undefined,\n options: CheckOptions = {},\n): Verdict {\n const opts = { ...DEFAULTS, ...options };\n const reasons: Reason[] = [];\n const scores: Partial<Record<ReasonCode, number>> = {};\n const modes: Partial<Record<ReasonCode, TokenMode>> = {};\n let parsedJson: unknown;\n\n const add = (\n code: ReasonCode,\n score: number,\n threshold: number,\n message: string,\n mode?: TokenMode,\n ) => {\n scores[code] = score;\n if (mode) modes[code] = mode;\n if (score > threshold) reasons.push({ code, score, threshold, message, ...(mode && { mode }) });\n };\n\n /*\n * A caller who has `undefined` where the text should be is in exactly the\n * situation this package exists for: the request \"succeeded\" and produced\n * nothing. Types do not stop it -- an SDK whose field is optional, a JSON\n * envelope that shaped differently than documented, a `.content[0].text`\n * that was never there. Throwing a TypeError here would be the worst\n * possible answer, because it is not a DegenerateOutputError and so slips\n * straight through the very retry predicate the README recommends.\n */\n if (typeof text !== 'string') {\n scores.EMPTY = 1;\n reasons.push({\n code: 'EMPTY',\n score: 1,\n threshold: 0.5,\n message: `Response was ${text === null ? 'null' : typeof text}, not a string.`,\n });\n return { ok: false, reasons, scores };\n }\n\n const empty = emptinessScore(text);\n add('EMPTY', empty, 0.5, 'Response contains no usable content.');\n\n // Once the response is empty, the remaining content signals are noise.\n if (empty >= 1) {\n return { ok: false, reasons, scores };\n }\n\n if (opts.minLength > 0) {\n add(\n 'TOO_SHORT',\n shortnessScore(text, opts.minLength),\n 0,\n `Response is ${text.trim().length} chars, below the ${opts.minLength} minimum.`,\n );\n }\n\n if (opts.maxRepetition != null) {\n const s = repetitionScore(text, { n: opts.ngram });\n add('REPETITION', s, opts.maxRepetition,\n `${Math.round(s * 100)}% of ${opts.ngram}-grams are duplicates.`);\n }\n\n /*\n * The tail detector picks its own tokenizer from its own tail, so the\n * threshold has to be picked the same way -- `maxTailLoop` and\n * `maxCharTailLoop` describe different distributions and are not\n * interchangeable. Either can be null independently, which is what disabling\n * one mode looks like.\n */\n {\n const { score, mode } = tailLoopDetail(text, { nonSpacedCutoff: opts.nonSpacedCutoff });\n const threshold = mode === 'char' ? opts.maxCharTailLoop : opts.maxTailLoop;\n if (threshold != null) {\n add('TAIL_LOOP', score, threshold,\n `Response ends in a repeating block covering ${Math.round(score * 100)}% of the tail.`,\n mode);\n }\n }\n\n if (opts.maxCompressibility != null) {\n const s = compressibilityScore(text);\n add('LOW_ENTROPY', s, opts.maxCompressibility,\n 'Response is far more compressible than natural language.');\n }\n\n if (opts.maxTruncation != null || opts.finishReason) {\n const s = truncationScore(text, { finishReason: opts.finishReason });\n add('TRUNCATED', s, opts.maxTruncation ?? 0.75,\n `Response appears cut off near: \"${excerpt(text.trim().slice(-60), 60)}\"`);\n }\n\n if (opts.expectJson) {\n const result = jsonScore(text, {\n allowFence: opts.allowJsonFence,\n requiredKeys: opts.requiredKeys,\n schema: opts.schema,\n });\n parsedJson = result.value;\n\n /*\n * One code for three ways of failing the same contract: the caller asked\n * for a payload of a given shape and did not get one. A schema mismatch\n * wants exactly the handling `INVALID_JSON` already gets -- retry, or fall\n * through to another provider -- so giving it a code of its own would widen\n * a frozen union and split existing handling for no gain.\n */\n add('INVALID_JSON', result.score, 0,\n result.reason === 'missing-keys'\n ? `JSON is missing required keys: ${result.missingKeys?.join(', ')}.`\n : result.reason === 'schema'\n ? `JSON does not match the schema: ${result.issues?.join('; ')}.`\n : 'Response is not parseable JSON.');\n }\n\n if (opts.expectLang) {\n const s = languageMismatchScore(text, opts.expectLang);\n add('LANG_MISMATCH', s, opts.maxLangMismatch,\n `Response does not look like '${opts.expectLang}'.`);\n }\n\n return {\n ok: reasons.length === 0,\n reasons,\n scores,\n ...(Object.keys(modes).length > 0 && { modes }),\n json: parsedJson,\n };\n}\n\n/** Error thrown by {@link assertOutput}, carrying the full verdict. */\nexport class DegenerateOutputError extends Error {\n readonly verdict: Verdict;\n /** Marks this as safe to retry against another provider. */\n readonly retryable = true;\n\n constructor(verdict: Verdict) {\n super(`Degenerate LLM output: ${verdict.reasons.map((r) => r.code).join(', ')}`);\n this.name = 'DegenerateOutputError';\n this.verdict = verdict;\n }\n}\n\n/**\n * Throwing wrapper, for dropping straight into an existing retry or fallback\n * chain that already keys off thrown errors.\n */\nexport function assertOutput(\n text: string | null | undefined,\n options: CheckOptions = {},\n): string {\n const verdict = checkOutput(text, options);\n if (!verdict.ok) throw new DegenerateOutputError(verdict);\n // Unreachable for non-strings: those score EMPTY 1 and throw above.\n return text as string;\n}\n","import type { CheckOptions, Verdict } from './types.js';\nimport { checkOutput } from './check.js';\n\n/**\n * Detectors that mean nothing until the response is complete.\n *\n * This is the whole problem with judging a stream. Partial output is short,\n * is cut off, and does not parse as JSON -- not because the model is failing\n * but because it has not finished talking. Run the full check on a half-built\n * response and `TOO_SHORT`, `TRUNCATED` and `INVALID_JSON` fire on every\n * healthy generation in the first few tokens, which is worse than no check at\n * all: it trains you to ignore the guard.\n *\n * What *is* meaningful early is redundancy. A model stuck in a loop is already\n * looping by the time it has emitted a few hundred characters, and no amount\n * of further generation makes it less true. So mid-stream runs exactly the\n * three detectors that measure repetition, and defers the rest to `end()`.\n */\nconst DEFERRED_TO_END: CheckOptions = {\n minLength: 0,\n maxTruncation: null,\n expectJson: false,\n expectLang: null,\n finishReason: undefined,\n\n /*\n * LOW_ENTROPY is deferred for a second reason: cost. The LZ77 pass is\n * 0.4ms at 500 characters and 11ms at its 4000-character sample cap, which\n * is 100x the other two detectors combined -- affordable once per response,\n * ruinous every few hundred characters of every stream.\n *\n * DEFERRING IT IS CONDITIONAL, NOT FREE. The condition is that the redundancy\n * detectors still running here reach a verdict *earlier* than LOW_ENTROPY\n * would have, on every script. Two things make that true today:\n *\n * - For spaced scripts, REPETITION catches what LOW_ENTROPY would, because\n * character-level collapse is also n-gram collapse.\n * - For Han, Kana and Thai, REPETITION is blind -- a loop with no\n * punctuation is a single word token -- and TAIL_LOOP's character mode is\n * what covers it. Measured at the 240-character warmup, that mode scores\n * 0.854-1.000 on every degenerate CJK fixture and fires on the first\n * check. At that same moment LOW_ENTROPY reads 0.453-0.805, i.e. below\n * its own 0.75 threshold on most of them: running it here would detect\n * these *later*, at 100x the cost.\n *\n * SO THIS BREAKS IF: character dispatch is disabled (`maxCharTailLoop: null`,\n * or `nonSpacedCutoff` raised out of reach), or `warmup` is raised past the\n * point where a loop's periodicity has established itself in the window. A\n * 15-character loop unit repeats 16 times in 240 characters against a\n * `minRepeats` of 3, so there is room -- but it is room, not immunity. If you\n * change either, re-measure before assuming this deferral is still safe;\n * otherwise CJK streams silently lose mid-stream detection entirely and the\n * only thing left is the end() check, which is after you have paid.\n */\n maxCompressibility: null,\n};\n\nexport interface StreamGuardOptions extends CheckOptions {\n /**\n * Characters of *new* text between checks. Default 400.\n *\n * Checking on every chunk would re-scan the buffer per token and turn a\n * linear stream into quadratic work. Batching costs a little detection\n * latency and buys a bounded cost per stream.\n */\n checkEvery?: number;\n /**\n * Characters that must arrive before any judgement. Default 240.\n *\n * A loop is not visible in the first sentence, and neither is its absence.\n * Below this the guard abstains rather than guessing -- the same rule the\n * detectors already follow for short samples.\n */\n warmup?: number;\n /**\n * Trailing characters each mid-stream check looks at. Default 2000.\n *\n * Two reasons, and the second matters more. Cost: without a window every\n * check re-scans the whole buffer, so a stream costs quadratic work in its\n * own length. Sensitivity: a model that produced four healthy paragraphs\n * and then began looping is diluted to nothing when measured across all\n * five, which is the same reasoning that makes `tailLoopScore` a separate\n * detector from `repetitionScore`. Recent text is the text in question.\n */\n window?: number;\n}\n\nexport interface StreamGuard {\n /**\n * Feed the next chunk.\n *\n * Returns a verdict only on the chunks where a check actually ran, and\n * `null` on the rest -- so `null` means \"not judged yet\", never \"healthy\".\n * Read `.ok` on what you get back.\n */\n push(chunk: string): Verdict | null;\n /**\n * Full check on the complete text, including the detectors deferred above.\n * Pass the provider's stop reason if you have it; truncation keys off it.\n */\n end(finishReason?: string): Verdict;\n /** Everything pushed so far. */\n readonly text: string;\n /** How many mid-stream checks have run. Useful when tuning `checkEvery`. */\n readonly checks: number;\n}\n\n/**\n * Watches a response as it arrives and reports degeneration before it finishes.\n *\n * The reason to bother: a model that has started looping will keep looping\n * until it hits `max_tokens`, and you pay for every one of those tokens plus\n * the latency of waiting for them. Catching it at character 300 of a 4000\n * character run and aborting turns a slow bad answer into a fast one.\n *\n * This never aborts anything itself -- it holds no controller and knows\n * nothing about your provider. It tells you; you decide.\n */\nexport function createStreamGuard(options: StreamGuardOptions = {}): StreamGuard {\n const { checkEvery = 400, warmup = 240, window = 2000, ...checkOptions } = options;\n\n let text = '';\n let sinceCheck = 0;\n let checks = 0;\n\n /*\n * The first check fires as soon as `warmup` is met; `checkEvery` only\n * spaces out the ones after it. Gating the first on both would make the\n * earlier of the two settings dead -- and it is the first check that\n * decides how many wasted tokens a loop gets to emit, which is the entire\n * point of watching a stream instead of its result.\n */\n const due = () => (checks === 0 ? text.length >= warmup : sinceCheck >= checkEvery);\n\n return {\n get text() {\n return text;\n },\n get checks() {\n return checks;\n },\n\n push(chunk: string): Verdict | null {\n if (typeof chunk !== 'string' || chunk.length === 0) return null;\n\n text += chunk;\n sinceCheck += chunk.length;\n\n if (!due()) return null;\n\n sinceCheck = 0;\n checks += 1;\n // The tail, not the head -- the detectors' own `maxSample` takes the\n // first N characters, which for a stream is the part already judged.\n const recent = text.length > window ? text.slice(-window) : text;\n return checkOutput(recent, { ...checkOptions, ...DEFERRED_TO_END });\n },\n\n end(finishReason?: string): Verdict {\n return checkOutput(text, {\n ...checkOptions,\n finishReason: finishReason ?? checkOptions.finishReason,\n });\n },\n };\n}\n\nexport interface GuardStreamOptions extends StreamGuardOptions {\n /**\n * Called the first time a mid-stream check fails. Abort your request here.\n *\n * The guard deliberately does not own the AbortController: the thing that\n * knows how to cancel a generation is the code that started it, and a\n * detection library that reaches into your transport is a library you\n * cannot use with the next transport.\n */\n onDegenerate?: (verdict: Verdict) => void;\n /**\n * Called once with the final verdict when the source ends normally. Skipped\n * when the stream was cut short, because a verdict on a deliberately\n * abandoned response would describe your own abort, not the model.\n */\n onEnd?: (verdict: Verdict) => void;\n /**\n * Stop yielding once degeneration is detected. Default true.\n *\n * Set false to keep passing chunks through while still being told -- useful\n * for a logging-only rollout, where you want the signal without changing\n * what the user sees.\n */\n stopOnDegenerate?: boolean;\n}\n\n/**\n * Wraps a chunk stream and cuts it off when the model starts looping.\n *\n * ```ts\n * const controller = new AbortController();\n * const guarded = guardStream(model.textStream, {\n * ...presets.chat,\n * onDegenerate: () => controller.abort(),\n * });\n * for await (const chunk of guarded) process.stdout.write(chunk);\n * ```\n *\n * Yields the source's chunks unchanged until then, so it drops into an\n * existing loop without touching what you do with the text.\n */\nexport async function* guardStream(\n source: AsyncIterable<string>,\n options: GuardStreamOptions = {},\n): AsyncGenerator<string, void, undefined> {\n const { onDegenerate, onEnd, stopOnDegenerate = true, ...guardOptions } = options;\n const guard = createStreamGuard(guardOptions);\n let degenerate = false;\n\n for await (const chunk of source) {\n yield chunk;\n\n const verdict = guard.push(chunk);\n if (!verdict || verdict.ok || degenerate) continue;\n\n // Once, not on every subsequent check -- a loop keeps failing by\n // definition, and an abort handler called forty times is a bug report.\n degenerate = true;\n onDegenerate?.(verdict);\n if (stopOnDegenerate) return;\n }\n\n if (!degenerate) onEnd?.(guard.end());\n}\n","/**\n * What a guard should do when the model answered with a tool call.\n *\n * Shared by every adapter for the same reason as `adapter-options.ts`: this is\n * a policy, and two hand-maintained copies of a policy is how one of them\n * quietly stops matching the other.\n *\n * ## The bug this exists to prevent\n *\n * A tool call is not text. OpenAI returns `content: null` alongside\n * `tool_calls`, and the AI SDK returns a `content` array with no `text` part --\n * so an adapter that concatenates text parts and hands the result to\n * `checkOutput` passes it `''`, which scores `EMPTY: 1` and throws. The\n * detector is right; it was asked the wrong question. Every tool-calling turn\n * of every agent fails, which is a false positive on the most common shape of\n * modern LLM traffic.\n *\n * So the rule is: **the presence of tool calls means the text, if any, is a\n * preamble rather than the answer.** Judge it as one, or not at all.\n *\n * ## This type is INTERNAL. It is not public API, at 1.0 or after.\n *\n * It is exported from no subpath and is not reachable by any import path a user\n * has. What is observable is the behaviour: adapters do not fail a response for\n * being a tool call. That behaviour is covered by semver; this module is not.\n */\nimport type { CheckOptions, Verdict } from '../types.js';\nimport { checkOutput } from '../check.js';\n\n/**\n * The detectors that ask \"is this a complete answer\", switched off.\n *\n * A preamble is not a complete answer and was never meant to be, so each of\n * these would be measuring the wrong thing:\n *\n * - `minLength` -- \"Let me look that up\" is sixteen characters and correct.\n * Under `presets.longForm` its 200-character minimum fails every tool call.\n * - `maxTruncation` -- a preamble ends without terminal punctuation as a matter\n * of course, which `truncationScore` reads as 0.55. Under a lowered\n * `maxTruncation` that fires on healthy output.\n * - `expectJson` -- on a tool-calling turn the JSON is in the call arguments,\n * which the provider has already validated against your schema. The prose\n * beside it is prose, and `presets.strictJson` would fail it for being so.\n *\n * `finishReason` is cleared with them: it is the input `maxTruncation` keys off,\n * and leaving it set re-enables the detector that was just switched off.\n *\n * What deliberately stays on is redundancy -- `REPETITION`, `TAIL_LOOP`,\n * `LOW_ENTROPY`. A model that loops in its preamble is still a model that is\n * looping, and those detectors measure that without caring whether the text is\n * a whole answer.\n */\nexport const TOOL_CALL_PREAMBLE: CheckOptions = {\n minLength: 0,\n maxTruncation: null,\n expectJson: false,\n finishReason: undefined,\n};\n\n/**\n * The verdict for a response that carried tool calls, or `null` when there is\n * nothing to judge.\n *\n * `null` is the no-text case, and it is the whole point: a response consisting\n * only of tool calls has no prose to measure, so the honest answer is silence\n * rather than a verdict on the empty string. Callers must treat `null` as \"not\n * judged\" and skip both the action and the `onVerdict` report -- an `EMPTY`\n * logged here would poison a calibration run with a spike of `EMPTY: 1` samples\n * that describe nothing but the agent's tool use.\n */\nexport function checkPreamble(text: string, options: CheckOptions): Verdict | null {\n if (text.trim().length === 0) return null;\n return checkOutput(text, { ...options, ...TOOL_CALL_PREAMBLE });\n}\n","/**\n * Middleware adapter for the Vercel AI SDK.\n *\n * Structurally typed against the SDK rather than importing from it, so this\n * subpath adds no dependency, runtime or otherwise -- `ai` stays an optional\n * peer. The shapes below are the parts of the provider spec this touches and\n * nothing more, which is also what keeps it working across spec versions:\n * `finishReason` is a plain string in v2 and an object in v4, and both are\n * accepted here.\n */\nimport type { Verdict } from './types.js';\nimport type { StreamGuardOptions } from './stream.js';\nimport type { AdapterGuardOptions, DegenerateAction } from './internal/adapter-options.js';\nimport { checkOutput, DegenerateOutputError } from './check.js';\nimport { createStreamGuard } from './stream.js';\nimport { checkPreamble } from './internal/tool-calls.js';\n\n/** `'stop' | 'length' | ...` in older specs, `{ unified, raw }` in v4. */\ntype FinishReasonLike = string | { unified?: string; raw?: string } | null | undefined;\n\ninterface StreamPart {\n type: string;\n /** Present on `text` content parts. */\n text?: string;\n /** Present on `text-delta` stream parts. */\n delta?: string;\n /** Present on the `finish` part. */\n finishReason?: FinishReasonLike;\n}\n\n/**\n * Whether a part is the model calling a tool.\n *\n * Matched by prefix rather than by an exact list because the spec has several\n * and has added to them across versions: `tool-call` on a finished generation,\n * and `tool-input-start` / `tool-input-delta` / `tool-input-end` while\n * streaming. A prefix keeps a part type added in a later `ai` major from\n * silently reading as prose, which is the direction that reintroduces the false\n * positive this guards against.\n */\nconst isToolPart = (part: StreamPart): boolean => part.type.startsWith('tool-');\n\ninterface GenerateResultLike {\n content?: StreamPart[];\n finishReason?: FinishReasonLike;\n}\n\ninterface StreamResultLike {\n stream: ReadableStream<StreamPart>;\n}\n\n/** Normalises both spec shapes to what `truncationScore` expects. */\nfunction finishReasonOf(value: FinishReasonLike): string | undefined {\n if (typeof value === 'string') return value;\n if (value && typeof value === 'object') return value.unified ?? value.raw;\n return undefined;\n}\n\nexport type { DegenerateAction };\n\n/**\n * Shares {@link AdapterGuardOptions} with `llm-output-guard/openai`, so the two\n * adapters cannot drift apart. Reading one set of docs is meant to be enough.\n */\nexport interface OutputGuardOptions extends StreamGuardOptions, AdapterGuardOptions {}\n\n/**\n * Guards a model against returning degenerate output, as AI SDK middleware.\n *\n * ```ts\n * import { wrapLanguageModel } from 'ai';\n * import { outputGuard } from 'llm-output-guard/ai-sdk';\n *\n * const model = wrapLanguageModel({\n * model: groq('llama-3.3-70b-versatile'),\n * middleware: outputGuard({ ...presets.chat, onDegenerate: 'abort' }),\n * });\n * ```\n *\n * On `streamText` this is where it pays: the guard watches deltas as they\n * arrive and cancels the generation the moment a loop is detectable, rather\n * than letting the model run to `max_tokens` on your budget.\n */\nexport function outputGuard(options: OutputGuardOptions = {}) {\n const { onDegenerate = 'throw', onVerdict, ...guardOptions } = options;\n\n const act = (verdict: Verdict, streaming: boolean): void => {\n onVerdict?.(verdict, { streaming });\n if (verdict.ok || onDegenerate === 'ignore') return;\n if (onDegenerate === 'throw') throw new DegenerateOutputError(verdict);\n };\n\n return {\n /**\n * A type-level tag only. It is present because the v3 middleware type\n * (`ai` v6) requires it, while v2 (`ai` v5) has no such field and v4\n * (`ai` v7) relaxed it to any string. `'v3'` is the one literal all three\n * admit, so a single object satisfies every supported major.\n *\n * This is load-bearing on an assumption: that `wrapLanguageModel`'s\n * `doWrap` destructures the hooks and never reads this field. That is true\n * of every version in the peer range, and it is checked -- `npm run\n * check:peer-ai` runs the adapter against each major, so a version that\n * started dispatching on the tag would fail there rather than in\n * production. **If that check is ever removed, remove this tag with it**:\n * without it the claim becomes an assumption again, and the failure it\n * would hide is the adapter being handed the wrong contract.\n */\n specificationVersion: 'v3' as const,\n\n /**\n * Non-streaming. The tokens are already bought by the time this runs, so\n * all it can do is stop a bad answer from being used as a good one.\n */\n async wrapGenerate<T extends GenerateResultLike>({\n doGenerate,\n }: {\n doGenerate: () => PromiseLike<T>;\n }): Promise<T> {\n const result = await doGenerate();\n const content = result.content ?? [];\n const text = content\n .filter((part) => part.type === 'text')\n .map((part) => part.text ?? '')\n .join('');\n\n /*\n * A tool call is an answer, just not a textual one. Judging its (absent)\n * text as a response would fail every tool-calling turn on `EMPTY` --\n * see `internal/tool-calls.ts` for why that is the detector being asked\n * the wrong question rather than the detector being wrong.\n */\n if (content.some(isToolPart)) {\n const verdict = checkPreamble(text, guardOptions);\n if (verdict) act(verdict, false);\n return result;\n }\n\n act(\n checkOutput(text, {\n ...guardOptions,\n finishReason: finishReasonOf(result.finishReason) ?? guardOptions.finishReason,\n }),\n false,\n );\n\n return result;\n },\n\n async wrapStream<T extends StreamResultLike>({\n doStream,\n }: {\n doStream: () => PromiseLike<T>;\n }): Promise<T> {\n const result = await doStream();\n const guard = createStreamGuard(guardOptions);\n let fired = false;\n let sawToolCall = false;\n let finishReason: FinishReasonLike;\n\n const guarded = result.stream.pipeThrough(\n new TransformStream<StreamPart, StreamPart>({\n transform(part, controller) {\n // Forward first: a chunk already generated has been paid for, and\n // withholding it buys nothing but a truncated answer.\n controller.enqueue(part);\n\n if (part.type === 'finish') finishReason = part.finishReason;\n if (isToolPart(part)) sawToolCall = true;\n if (part.type !== 'text-delta' || fired) return;\n\n const verdict = guard.push(part.delta ?? '');\n if (!verdict || verdict.ok) return;\n\n fired = true;\n onVerdict?.(verdict, { streaming: true });\n if (onDegenerate === 'ignore') return;\n\n /*\n * Both of these cancel the source stream, which is what actually\n * stops the provider generating -- the saving is not in skipping\n * chunks we already received but in the ones never produced.\n */\n if (onDegenerate === 'throw') {\n controller.error(new DegenerateOutputError(verdict));\n } else {\n controller.terminate();\n }\n },\n\n flush() {\n // A stream we cut short would only be reported as truncated by us,\n // describing our own abort rather than the model.\n if (fired) return;\n\n /*\n * Same rule as `wrapGenerate`, and it matters here even though\n * nothing throws on this path: a tool-call stream carries no text\n * deltas, so `end()` would report `EMPTY: 1` to `onVerdict` on\n * every one. Those samples are what a `calibrate` run is built\n * from, and a spike of them describes the agent's tool use rather\n * than any degeneration.\n */\n if (sawToolCall) {\n const verdict = checkPreamble(guard.text, guardOptions);\n if (verdict) onVerdict?.(verdict, { streaming: true });\n return;\n }\n\n onVerdict?.(guard.end(finishReasonOf(finishReason)), { streaming: true });\n },\n }),\n );\n\n // Everything the provider returned, with only the stream swapped -- the\n // cast is the spread losing `T`, not a change in what is handed back.\n return { ...result, stream: guarded } as T;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/tokenize.ts","../src/detectors/repetition.ts","../src/detectors/compressibility.ts","../src/detectors/emptiness.ts","../src/detectors/truncation.ts","../src/detectors/json.ts","../src/detectors/language.ts","../src/internal/json-scope.ts","../src/check.ts","../src/stream.ts","../src/internal/tool-calls.ts","../src/ai-sdk.ts"],"names":[],"mappings":"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","file":"ai-sdk.cjs","sourcesContent":["import type { TokenMode } from '../types.js';\n\n/**\n * Word tokenizer for scripts that separate words -- with spaces, punctuation,\n * or anything else that is not a letter or digit. Latin, Cyrillic, Greek,\n * Hangul, Arabic, Devanagari and friends all tokenize correctly here.\n *\n * It does *not* work for Han, Kana or Thai. Those write without inter-word\n * spaces, so a whole punctuation-delimited clause matches as one token, and a\n * loop with no punctuation inside it matches as one token for the entire\n * response. See {@link nonSpacedRatio} for how that case is detected and\n * `tailLoopScore` for what runs instead.\n */\nexport function words(text: string): string[] {\n return text.toLowerCase().match(/[\\p{L}\\p{N}']+/gu) ?? [];\n}\n\n/** Character tokens, whitespace dropped. The fallback where `words` cannot see. */\nexport function chars(text: string): string[] {\n return [...text.replace(/\\s+/g, '')];\n}\n\n/** Scripts that do not put spaces between words. */\nconst NON_SPACED = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Thai}]/gu;\n\n/**\n * Share of a span written in a script `words()` cannot tokenize, 0..1.\n *\n * The denominator counts marks as well as letters on purpose.\n * `\\p{Script=Thai}` matches Thai vowel and tone marks, which are `\\p{M}` and\n * not `\\p{L}` -- so counting `\\p{L}` underneath returned ratios above 1 for\n * Thai and made any cutoff meaningless there. Numerator and denominator have\n * to count the same set.\n */\nexport function nonSpacedRatio(text: string): number {\n const total = text.match(/[\\p{L}\\p{M}]/gu)?.length ?? 0;\n if (total === 0) return 0;\n return (text.match(NON_SPACED)?.length ?? 0) / total;\n}\n\n/**\n * Which tokenizer suits this span.\n *\n * Decide it from the span a detector actually reads, never from the whole\n * response. A reply that answers in English and then loops in Chinese measures\n * 0.35 overall and 1.00 across its tail: judging the tail by the whole\n * response's ratio puts the tail detector in word mode on text that has no\n * words in it, which is the exact failure this dispatch exists to prevent.\n */\nexport function tokenModeOf(text: string, cutoff = 0.5): TokenMode {\n return nonSpacedRatio(text) >= cutoff ? 'char' : 'word';\n}\n\n/** Clamp a raw signal into the 0..1 suspicion range. */\nexport function clamp01(n: number): number {\n if (Number.isNaN(n)) return 0;\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n\n/** Short, safe excerpt for messages. Never leaks a full response into logs. */\nexport function excerpt(text: string, max = 80): string {\n const flat = text.replace(/\\s+/g, ' ').trim();\n return flat.length <= max ? flat : flat.slice(0, max) + '\\u2026';\n}\n","import type { TokenMode } from '../types.js';\nimport { words, chars, clamp01, tokenModeOf } from '../internal/tokenize.js';\n\nexport interface RepetitionOptions {\n /** N-gram size. 3 suits prose; 2 is noisy, 4 misses short loops. */\n n?: number;\n /** Only analyse the first N characters. Keeps cost bounded on long outputs. */\n maxSample?: number;\n}\n\n/**\n * Fraction of n-grams that are duplicates. 0 = every n-gram unique, 1 = total collapse.\n *\n * Healthy prose sits near 0.00-0.10. A model stuck in a loop passes 0.5 quickly.\n * Returns 0 for text too short to judge rather than guessing.\n *\n * **Word mode only, and knowingly blind to non-spaced scripts.** In Chinese,\n * Japanese or Thai a punctuation-delimited clause is one token and a loop with\n * no punctuation is one token for the whole response, so this scores 0.000 on\n * an obvious Chinese loop.\n *\n * A character n-gram fallback was built and rejected. Not because no threshold\n * exists -- one does, around 0.7 -- but because **it would buy no coverage and\n * cost a false-positive surface.**\n *\n * Coverage: `tailLoopScore`'s character mode already catches every degenerate\n * non-Latin fixture in the corpus, at a margin of 0.538. There is nothing left\n * for a character-mode `REPETITION` to find.\n *\n * Cost: healthy *structured* CJK output scores high here. Repeated key\n * scaffolding around short CJK values is genuinely redundant character by\n * character, and `json-zh-keys-valid` measures 0.543 over twenty distinct\n * items. The curve flattens rather than diverging -- 0.396 at eight, 0.577 at\n * thirty, 0.597 at forty, converging on the scaffolding's own proportion -- so\n * the plateau near 0.6 against the weakest pure loop at 0.872 leaves about\n * 0.19. That is under this package's own 0.2 bar, and the healthy side rises\n * with the number of keys a payload carries, which nothing bounds.\n *\n * A detector with no coverage to add and a structure-sensitive margin is a\n * false positive waiting for someone's payload shape to change.\n *\n * `tailLoopScore` covers the gap instead: it requires *exact periodicity*, which\n * scaffolding never produces, and it caught every degenerate CJK sample in the\n * corpus. See the `Limitations` section of the README.\n */\nexport function repetitionScore(text: string, options: RepetitionOptions = {}): number {\n const { n = 3, maxSample = 8000 } = options;\n const w = words(text.slice(0, maxSample));\n if (w.length < n * 4) return 0;\n\n const seen = new Set<string>();\n let total = 0;\n for (let i = 0; i + n <= w.length; i++) {\n seen.add(w.slice(i, i + n).join(' '));\n total++;\n }\n if (total === 0) return 0;\n return clamp01(1 - seen.size / total);\n}\n\nexport interface TailLoopOptions {\n /** How many trailing words to inspect in word mode. */\n tailWords?: number;\n /** Longest loop period to look for, in words. */\n maxPeriod?: number;\n /** A block must repeat at least this many times to count as a loop. */\n minRepeats?: number;\n /** How many trailing characters to inspect in char mode. Default 400. */\n tailChars?: number;\n /** Longest loop period to look for in char mode, in characters. Default 80. */\n maxCharPeriod?: number;\n /**\n * Characters required before char mode will judge at all. Default 80.\n *\n * Word mode's floor is a word count, which on a non-spaced script can be\n * satisfied by a single token, so char mode needs its own. Below this the\n * detector abstains: three short sentences ending a 40-character reply are\n * indistinguishable from a loop by coverage alone, and abstaining is the\n * rule everywhere else in this package.\n */\n minCharSample?: number;\n /** Force a tokenizer instead of dispatching on the tail's script. */\n mode?: TokenMode;\n /** Non-spaced-script share at which char mode takes over. Default 0.5. */\n nonSpacedCutoff?: number;\n}\n\nexport interface TailLoopResult {\n /** Fraction of the inspected tail covered by the repeating block. */\n score: number;\n /** Which tokenizer produced `score`. */\n mode: TokenMode;\n}\n\n/**\n * Largest share of `tail` covered by a block repeating to its end.\n *\n * Shared by both modes so the two cannot drift apart: word mode passes word\n * tokens, char mode passes characters, and the periodicity search is the same\n * code either way.\n */\nfunction periodicCoverage(\n tail: readonly string[],\n maxPeriod: number,\n minRepeats: number,\n): number {\n if (tail.length < minRepeats * 2) return 0;\n\n let best = 0;\n const periodCap = Math.min(maxPeriod, Math.floor(tail.length / minRepeats));\n for (let p = 1; p <= periodCap; p++) {\n const block = tail.slice(tail.length - p);\n let repeats = 1;\n let cursor = tail.length - p;\n while (cursor - p >= 0) {\n let same = true;\n for (let k = 0; k < p; k++) {\n if (tail[cursor - p + k] !== block[k]) { same = false; break; }\n }\n if (!same) break;\n repeats++;\n cursor -= p;\n }\n if (repeats >= minRepeats) {\n best = Math.max(best, clamp01((repeats * p) / tail.length));\n }\n }\n return best;\n}\n\n/**\n * Detects the specific failure where a model terminates in a repeating tail --\n * the same clause emitted over and over until max_tokens runs out.\n *\n * Whole-output repetition misses this when the first half of the response was\n * fine. Returns the fraction of the inspected tail covered by the loop, plus\n * the tokenizer that measured it.\n *\n * **The mode is decided from the tail, not the whole response.** A reply that\n * answers in English and then loops in Chinese is 0.35 non-spaced overall and\n * 1.00 across its final 400 characters; dispatching on the former would run\n * word tokenization over text that yields one token, and score 0.000 on an\n * obvious loop. Measured on that shape, whole-response dispatch missed it\n * entirely and tail dispatch scored 1.000.\n *\n * The two modes are **not interchangeable numbers**. Character n-grams\n * duplicate at a different base rate, so each has its own threshold\n * (`maxTailLoop`, `maxCharTailLoop`) and `Verdict.modes` reports which one ran.\n */\nexport function tailLoopDetail(text: string, options: TailLoopOptions = {}): TailLoopResult {\n const {\n tailWords = 200,\n maxPeriod = 40,\n minRepeats = 3,\n tailChars = 400,\n maxCharPeriod = 80,\n minCharSample = 80,\n nonSpacedCutoff = 0.5,\n } = options;\n\n const charTail = chars(text).slice(-tailChars);\n const mode = options.mode ?? tokenModeOf(charTail.join(''), nonSpacedCutoff);\n\n if (mode === 'char') {\n if (charTail.length < minCharSample) return { score: 0, mode };\n return { score: periodicCoverage(charTail, maxCharPeriod, minRepeats), mode };\n }\n\n const tail = words(text).slice(-tailWords);\n return { score: periodicCoverage(tail, maxPeriod, minRepeats), mode };\n}\n\n/** {@link tailLoopDetail} without the mode, for callers that only want the score. */\nexport function tailLoopScore(text: string, options: TailLoopOptions = {}): number {\n return tailLoopDetail(text, options).score;\n}\n","import { clamp01 } from '../internal/tokenize.js';\n\nexport interface CompressibilityOptions {\n /** Sliding window for back-references, in characters. */\n window?: number;\n /** Only analyse the first N characters. */\n maxSample?: number;\n /** Shortest back-reference worth emitting. */\n minMatch?: number;\n}\n\n/**\n * Greedy LZ77 pass returning emitted-tokens / input-characters.\n *\n * Deliberately hand-rolled instead of node:zlib so the package stays\n * runtime-agnostic (browser, edge, Deno, Bun) and dependency-free.\n * This is not a real compressor; it only needs to move monotonically\n * with redundancy, which is all the score requires.\n *\n * Measured against the fixture corpus: healthy output lands at 0.67-0.97,\n * degenerate collapse at 0.007-0.042, and tail loops in between at 0.17-0.20.\n * The gap either side of that middle band is what the pivot below trades on.\n */\nexport function compressionRatio(text: string, options: CompressibilityOptions = {}): number {\n const { window = 1024, maxSample = 4000, minMatch = 4 } = options;\n const s = text.slice(0, maxSample);\n if (s.length < 64) return 1;\n\n let i = 0;\n let emitted = 0;\n while (i < s.length) {\n let bestLen = 0;\n const start = i > window ? i - window : 0;\n for (let j = start; j < i; j++) {\n let k = 0;\n while (k < 255 && i + k < s.length && s[j + k] === s[i + k]) k++;\n if (k > bestLen) {\n bestLen = k;\n if (bestLen >= 255) break;\n }\n }\n emitted++;\n i += bestLen >= minMatch ? bestLen : 1;\n }\n return emitted / s.length;\n}\n\n/**\n * Suspicion score derived from {@link compressionRatio}.\n * `pivot` is the ratio treated as fully healthy; lower ratios scale up toward 1.\n *\n * At the default 0.32 every healthy fixture clamps to exactly 0, with the\n * nearest one still twice the pivot away -- so this detector is deliberately\n * tuned for outright entropy collapse and abstains on everything milder.\n * Tail loops score 0.37-0.48 here and are left to `tailLoopScore`, which\n * separates them far more cleanly (0.90 against a healthy max of 0.00).\n * Raising the pivot would make this fire on loops too, buying redundant\n * coverage with the margin that currently makes a false positive so unlikely.\n */\nexport function compressibilityScore(\n text: string,\n options: CompressibilityOptions & { pivot?: number } = {},\n): number {\n const { pivot = 0.32, ...rest } = options;\n if (text.trim().length < 64) return 0;\n return clamp01(1 - compressionRatio(text, rest) / pivot);\n}\n","import { words } from '../internal/tokenize.js';\n\n/**\n * 1 when the response carries no usable content at all.\n *\n * Covers the cases a plain `!text` check misses: whitespace-only, a lone\n * punctuation mark, an empty code fence, or an empty JSON envelope.\n */\nexport function emptinessScore(text: string): number {\n const trimmed = text.trim();\n if (trimmed.length === 0) return 1;\n if (words(trimmed).length === 0) return 1;\n const stripped = trimmed\n .replace(/```[a-z]*\\s*```/gi, '')\n .replace(/^[{}[\\]\"'\\s,.:;!?-]+$/g, '');\n return stripped.trim().length === 0 ? 1 : 0;\n}\n\n/** 1 when the response is shorter than `minChars`, scaling down to 0 at the threshold. */\nexport function shortnessScore(text: string, minChars: number): number {\n if (minChars <= 0) return 0;\n const len = text.trim().length;\n if (len >= minChars) return 0;\n return 1 - len / minChars;\n}\n","export interface TruncationOptions {\n /**\n * The provider's own stop reason, if you have it. When this says the output\n * hit the token ceiling, that is authoritative and the heuristics are skipped.\n */\n finishReason?: string;\n}\n\nconst LENGTH_STOPS = new Set(['length', 'max_tokens', 'maxtokens', 'max_output_tokens', 'token_limit']);\nconst TERMINAL = /[.!?\"'`\\u2019\\u201d)\\]}:;\\u3002\\uff01\\uff1f]\\s*$/;\n\n/**\n * Detects output that stopped mid-thought.\n *\n * Prefers the provider's finish_reason when supplied, because that is ground\n * truth. Falls back to structural signals: unbalanced fences or brackets, or a\n * final sentence with no terminal punctuation.\n *\n * Returns a graded score, not a boolean -- a missing full stop alone is weak\n * evidence and should not sink a response on its own.\n */\nexport function truncationScore(text: string, options: TruncationOptions = {}): number {\n const { finishReason } = options;\n if (finishReason && LENGTH_STOPS.has(finishReason.toLowerCase())) return 1;\n\n const trimmed = text.trim();\n if (trimmed.length === 0) return 0;\n\n let score = 0;\n\n const fences = (trimmed.match(/```/g) ?? []).length;\n if (fences % 2 === 1) score = Math.max(score, 0.9);\n\n for (const [open, close] of [['{', '}'], ['[', ']'], ['(', ')']] as const) {\n const opens = trimmed.split(open).length - 1;\n const closes = trimmed.split(close).length - 1;\n if (opens > closes) score = Math.max(score, 0.8);\n }\n\n if (!TERMINAL.test(trimmed)) score = Math.max(score, 0.55);\n\n return score;\n}\n","import type { StandardSchemaV1 } from '../standard-schema.js';\n\nexport interface JsonOptions {\n /** Allow the payload to sit inside a ```json fence rather than being bare. */\n allowFence?: boolean;\n /** Top-level keys that must be present for the payload to count as valid. */\n requiredKeys?: string[];\n /**\n * A Standard Schema validator the payload must satisfy -- Zod 4, Valibot,\n * ArkType, or anything else implementing the spec.\n *\n * Strictly stronger than `requiredKeys`, which only asks whether a name is\n * present and says nothing about its type, and the two compose: keys are\n * checked first, so a missing one is still reported as a missing key rather\n * than as whatever the schema calls it.\n *\n * **Must validate synchronously.** See {@link JsonResult.reason}.\n */\n schema?: StandardSchemaV1;\n}\n\nexport interface JsonResult {\n /** 0 when the payload parses and satisfies every contract, 1 otherwise. */\n score: number;\n /**\n * The payload, when parsing succeeded.\n *\n * When a `schema` validated it, this is the schema's *output* rather than the\n * raw parse -- so Zod defaults, coercions and transforms are applied, and the\n * value is the one your types describe. Without a schema it is `JSON.parse`'s\n * result unchanged.\n */\n value?: unknown;\n reason?: 'unparseable' | 'missing-keys' | 'schema';\n missingKeys?: string[];\n /** Messages from a failing `schema`, path-prefixed where the issue had one. */\n issues?: string[];\n}\n\n/** Pull a JSON payload out of a ```json fence, or return the text unchanged. */\nexport function stripFence(text: string): string {\n const fenced = text.trim().match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i);\n return fenced ? fenced[1] : text.trim();\n}\n\n/** `notes.0.body: Expected string` -- the path is what makes an issue actionable. */\nfunction describe(issue: StandardSchemaV1.Issue): string {\n const path = (issue.path ?? [])\n .map((segment) =>\n typeof segment === 'object' && segment !== null && 'key' in segment\n ? String(segment.key)\n : String(segment),\n )\n .join('.');\n return path ? `${path}: ${issue.message}` : issue.message;\n}\n\n/**\n * Structured-output check. Models that \"succeed\" while emitting prose around\n * the JSON, or an object missing half its keys, fail here.\n */\nexport function jsonScore(text: string, options: JsonOptions = {}): JsonResult {\n const { allowFence = true, requiredKeys = [], schema } = options;\n const candidate = allowFence ? stripFence(text) : text.trim();\n\n let value: unknown;\n try {\n value = JSON.parse(candidate);\n } catch {\n return { score: 1, reason: 'unparseable' };\n }\n\n if (requiredKeys.length > 0) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return { score: 1, value, reason: 'missing-keys', missingKeys: [...requiredKeys] };\n }\n const record = value as Record<string, unknown>;\n const missing = requiredKeys.filter((k) => !(k in record));\n if (missing.length > 0) {\n return { score: 1, value, reason: 'missing-keys', missingKeys: missing };\n }\n }\n\n if (schema) {\n const result = schema['~standard'].validate(value);\n\n /*\n * A thenable here is the caller's configuration, not the model's output,\n * and it is the one thing in this package that throws on purpose.\n *\n * `checkOutput` promises never to throw *about a response*, because a\n * TypeError raised on bad model output is not a `DegenerateOutputError` and\n * so slips straight through the retry predicate the README recommends. That\n * reasoning does not extend to a schema wired up wrong: no verdict about it\n * would be true, `{ ok: true }` would silently disable the check the caller\n * asked for, and `{ ok: false }` would blame the model for the caller's\n * bug. Throwing surfaces it on the first call, in development, with the fix\n * in the message -- which is where a misconfiguration should surface.\n *\n * In practice this is reached only by a schema carrying an async refinement.\n * Zod, Valibot and ArkType all validate synchronously otherwise.\n */\n if (typeof (result as PromiseLike<unknown>)?.then === 'function') {\n throw new TypeError(\n 'llm-output-guard: `schema` must validate synchronously, and this one returned a promise. ' +\n 'checkOutput is synchronous by design. Remove the async refinement, or validate ' +\n 'the payload yourself after checkOutput returns.',\n );\n }\n\n const sync = result as StandardSchemaV1.Result<unknown>;\n if (sync.issues) {\n return { score: 1, value, reason: 'schema', issues: sync.issues.map(describe) };\n }\n // The schema's output, not the raw parse: defaults and transforms applied.\n return { score: 0, value: sync.value };\n }\n\n return { score: 0, value };\n}\n","import { words } from '../internal/tokenize.js';\n\n/**\n * Function-word frequency profiles. Coarse by design: this catches a model\n * answering in the wrong language entirely, not dialect or register drift.\n * Off by default in every preset for exactly that reason.\n */\nconst PROFILES: Record<string, Set<string>> = {\n id: new Set(['yang', 'dan', 'di', 'untuk', 'dengan', 'ini', 'itu', 'dari', 'pada', 'tidak', 'adalah', 'akan', 'bisa', 'kita', 'saya', 'atau', 'juga', 'dalam', 'sudah', 'ke']),\n en: new Set(['the', 'and', 'of', 'to', 'in', 'is', 'that', 'for', 'it', 'with', 'as', 'this', 'are', 'be', 'you', 'on', 'not', 'or', 'can', 'we']),\n es: new Set(['el', 'la', 'de', 'que', 'y', 'en', 'los', 'un', 'por', 'con', 'las', 'para', 'una', 'es', 'no', 'se', 'del', 'al', 'lo', 'como']),\n};\n\nexport interface LanguageOptions {\n /** Below this word count the signal is unreliable and the score is 0. */\n minWords?: number;\n}\n\n/** Share of tokens matching each known profile. Not a full language detector. */\nexport function languageProfile(text: string): Record<string, number> {\n const w = words(text);\n const out: Record<string, number> = {};\n if (w.length === 0) return out;\n for (const [lang, set] of Object.entries(PROFILES)) {\n let hits = 0;\n for (const token of w) if (set.has(token)) hits++;\n out[lang] = hits / w.length;\n }\n return out;\n}\n\n/**\n * Suspicion that the response is not in `expected`.\n * Returns 0 for unknown languages or samples too short to judge --\n * silence is better than a confident wrong answer here.\n */\nexport function languageMismatchScore(\n text: string,\n expected: string,\n options: LanguageOptions = {},\n): number {\n const { minWords = 25 } = options;\n if (!(expected in PROFILES)) return 0;\n const w = words(text);\n if (w.length < minWords) return 0;\n\n const profile = languageProfile(text);\n const target = profile[expected] ?? 0;\n const best = Math.max(...Object.values(profile));\n if (best === 0) return 0;\n if (target >= best) return 0;\n return Math.min(1, (best - target) / best);\n}\n\nexport const supportedLanguages = Object.keys(PROFILES);\n","/**\n * Which spans of a response the redundancy detectors should read.\n *\n * ## The problem this exists for\n *\n * `REPETITION` and `TAIL_LOOP` measure a whole response at once, which is right\n * for prose and wrong for a JSON array. A model asked for the status of twenty\n * services and returning twenty identical rows has done exactly what it was\n * told; measured across the document that is a perfect loop, and 1.2.1 scores it\n * `TAIL_LOOP: 1.000` and fails it under every preset -- including `lenient`, and\n * including `strictJson`, which is the preset most likely to be pointed at that\n * payload. Three identical records is enough to trip it.\n *\n * The scores were not wrong. Twenty identical records *are* exactly periodic.\n * The detectors were being asked about the wrong span.\n *\n * ## The rule\n *\n * In a payload that parses as JSON, repetition **across records** is the shape\n * that was requested, and repetition **inside a value** is the signal. So under\n * `'jsonValues'` the redundancy detectors read each string value on its own\n * rather than the serialised document.\n *\n * This is strictly more sensitive, not less. A loop inside one element of an\n * array is diluted to nothing when averaged across the document -- 1.2.1 misses\n * `[{\"q\":\"<30x repeated Chinese clause>\"}, ...four healthy items]` entirely --\n * and reads 1.000 when that element is measured on its own.\n *\n * ## Why it falls back rather than failing closed\n *\n * A response that does not parse gets measured as a document, unchanged. That\n * covers prose, a truncated payload, and every mid-stream check (partial JSON\n * never parses). Returning \"no spans\" for unparseable text would silently\n * disable the redundancy detectors on exactly the responses most likely to be\n * degenerate.\n *\n * ## This module is INTERNAL. It is not public API, at 1.0 or after.\n *\n * What is public is the `redundancyScope` option and the behaviour it selects.\n */\nimport { stripFence } from '../detectors/json.js';\n\n/** Collects every string leaf, in document order. */\nfunction stringValues(value: unknown, out: string[] = []): string[] {\n if (typeof value === 'string') out.push(value);\n else if (Array.isArray(value)) for (const item of value) stringValues(item, out);\n else if (value !== null && typeof value === 'object') {\n for (const item of Object.values(value)) stringValues(item, out);\n }\n return out;\n}\n\n/**\n * The spans to measure for redundancy.\n *\n * Always at least one span, so a caller can reduce over the result without\n * special-casing empty. Under `'document'`, or when the text does not parse,\n * that span is the text itself.\n */\nexport function redundancySpans(text: string, scope: 'document' | 'jsonValues'): string[] {\n if (scope !== 'jsonValues') return [text];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripFence(text));\n } catch {\n return [text];\n }\n\n const values = stringValues(parsed);\n /*\n * A payload of pure numbers and booleans has no prose to judge. Measuring the\n * serialised form instead would reintroduce the false positive this option\n * exists to remove -- `[{\"a\":1},{\"a\":1}]` is periodic and fine -- so an empty\n * result is reported as one empty span, which every detector scores 0.\n */\n return values.length > 0 ? values : [''];\n}\n","import type { CheckOptions, Reason, ReasonCode, TokenMode, Verdict } from './types.js';\nimport { repetitionScore, tailLoopDetail } from './detectors/repetition.js';\nimport { compressibilityScore } from './detectors/compressibility.js';\nimport { emptinessScore, shortnessScore } from './detectors/emptiness.js';\nimport { truncationScore } from './detectors/truncation.js';\nimport { jsonScore } from './detectors/json.js';\nimport { languageMismatchScore } from './detectors/language.js';\nimport { excerpt } from './internal/tokenize.js';\nimport { redundancySpans } from './internal/json-scope.js';\n\nconst DEFAULTS: Required<\n Pick<CheckOptions,\n 'minLength' | 'maxRepetition' | 'maxTailLoop' | 'maxCompressibility' |\n 'maxTruncation' | 'expectJson' | 'allowJsonFence' | 'maxLangMismatch' | 'ngram' |\n 'maxCharTailLoop' | 'nonSpacedCutoff' | 'redundancyScope'>\n> = {\n minLength: 1,\n maxRepetition: 0.35,\n maxTailLoop: 0.5,\n maxCharTailLoop: 0.7,\n nonSpacedCutoff: 0.5,\n maxCompressibility: 0.75,\n maxTruncation: null as unknown as number,\n expectJson: false,\n allowJsonFence: true,\n maxLangMismatch: 0.6,\n ngram: 3,\n redundancyScope: 'document',\n};\n\n/**\n * Runs every enabled detector and returns a structured verdict.\n *\n * Pure and synchronous: no network, no clock, no randomness. The same input\n * always produces the same verdict, which is what makes it safe to put on a\n * hot path and easy to unit test.\n *\n * Every detector runs even after one fails, so `reasons` shows the full picture\n * rather than whichever check happened to be ordered first.\n *\n * Never throws. A `null`, `undefined`, or otherwise non-string input is a\n * verdict (`EMPTY`), not an exception -- see the guard below for why.\n */\nexport function checkOutput(\n text: string | null | undefined,\n options: CheckOptions = {},\n): Verdict {\n const opts = { ...DEFAULTS, ...options };\n const reasons: Reason[] = [];\n const scores: Partial<Record<ReasonCode, number>> = {};\n const modes: Partial<Record<ReasonCode, TokenMode>> = {};\n let parsedJson: unknown;\n\n const add = (\n code: ReasonCode,\n score: number,\n threshold: number,\n message: string,\n mode?: TokenMode,\n ) => {\n scores[code] = score;\n if (mode) modes[code] = mode;\n if (score > threshold) reasons.push({ code, score, threshold, message, ...(mode && { mode }) });\n };\n\n /*\n * A caller who has `undefined` where the text should be is in exactly the\n * situation this package exists for: the request \"succeeded\" and produced\n * nothing. Types do not stop it -- an SDK whose field is optional, a JSON\n * envelope that shaped differently than documented, a `.content[0].text`\n * that was never there. Throwing a TypeError here would be the worst\n * possible answer, because it is not a DegenerateOutputError and so slips\n * straight through the very retry predicate the README recommends.\n */\n if (typeof text !== 'string') {\n scores.EMPTY = 1;\n reasons.push({\n code: 'EMPTY',\n score: 1,\n threshold: 0.5,\n message: `Response was ${text === null ? 'null' : typeof text}, not a string.`,\n });\n return { ok: false, reasons, scores };\n }\n\n const empty = emptinessScore(text);\n add('EMPTY', empty, 0.5, 'Response contains no usable content.');\n\n // Once the response is empty, the remaining content signals are noise.\n if (empty >= 1) {\n return { ok: false, reasons, scores };\n }\n\n if (opts.minLength > 0) {\n add(\n 'TOO_SHORT',\n shortnessScore(text, opts.minLength),\n 0,\n `Response is ${text.trim().length} chars, below the ${opts.minLength} minimum.`,\n );\n }\n\n /*\n * The spans the redundancy detectors read. One span -- the whole response --\n * unless `redundancyScope` says otherwise and the payload parses. See\n * `internal/json-scope.ts` for why a JSON array needs a different span.\n */\n const spans = redundancySpans(text, opts.redundancyScope);\n\n if (opts.maxRepetition != null) {\n // The worst span, not the average: a loop confined to one array element is\n // still a loop, and averaging is what hides it.\n const s = Math.max(...spans.map((span) => repetitionScore(span, { n: opts.ngram })));\n add('REPETITION', s, opts.maxRepetition,\n `${Math.round(s * 100)}% of ${opts.ngram}-grams are duplicates.`);\n }\n\n /*\n * The tail detector picks its own tokenizer from its own tail, so the\n * threshold has to be picked the same way -- `maxTailLoop` and\n * `maxCharTailLoop` describe different distributions and are not\n * interchangeable. Either can be null independently, which is what disabling\n * one mode looks like.\n */\n {\n // The mode travels with the span that produced the score, because the\n // threshold is chosen by it -- reporting the worst score against another\n // span's tokenizer would compare a number to the wrong distribution.\n let worst = { score: -1, mode: 'word' as TokenMode };\n for (const span of spans) {\n const detail = tailLoopDetail(span, { nonSpacedCutoff: opts.nonSpacedCutoff });\n if (detail.score > worst.score) worst = detail;\n }\n const { score, mode } = worst;\n const threshold = mode === 'char' ? opts.maxCharTailLoop : opts.maxTailLoop;\n if (threshold != null) {\n add('TAIL_LOOP', score, threshold,\n `Response ends in a repeating block covering ${Math.round(score * 100)}% of the tail.`,\n mode);\n }\n }\n\n if (opts.maxCompressibility != null) {\n const s = compressibilityScore(text);\n add('LOW_ENTROPY', s, opts.maxCompressibility,\n 'Response is far more compressible than natural language.');\n }\n\n if (opts.maxTruncation != null || opts.finishReason) {\n const s = truncationScore(text, { finishReason: opts.finishReason });\n add('TRUNCATED', s, opts.maxTruncation ?? 0.75,\n `Response appears cut off near: \"${excerpt(text.trim().slice(-60), 60)}\"`);\n }\n\n if (opts.expectJson) {\n const result = jsonScore(text, {\n allowFence: opts.allowJsonFence,\n requiredKeys: opts.requiredKeys,\n schema: opts.schema,\n });\n parsedJson = result.value;\n\n /*\n * One code for three ways of failing the same contract: the caller asked\n * for a payload of a given shape and did not get one. A schema mismatch\n * wants exactly the handling `INVALID_JSON` already gets -- retry, or fall\n * through to another provider -- so giving it a code of its own would widen\n * a frozen union and split existing handling for no gain.\n */\n add('INVALID_JSON', result.score, 0,\n result.reason === 'missing-keys'\n ? `JSON is missing required keys: ${result.missingKeys?.join(', ')}.`\n : result.reason === 'schema'\n ? `JSON does not match the schema: ${result.issues?.join('; ')}.`\n : 'Response is not parseable JSON.');\n }\n\n if (opts.expectLang) {\n const s = languageMismatchScore(text, opts.expectLang);\n add('LANG_MISMATCH', s, opts.maxLangMismatch,\n `Response does not look like '${opts.expectLang}'.`);\n }\n\n return {\n ok: reasons.length === 0,\n reasons,\n scores,\n ...(Object.keys(modes).length > 0 && { modes }),\n json: parsedJson,\n };\n}\n\n/** Error thrown by {@link assertOutput}, carrying the full verdict. */\nexport class DegenerateOutputError extends Error {\n readonly verdict: Verdict;\n /** Marks this as safe to retry against another provider. */\n readonly retryable = true;\n\n constructor(verdict: Verdict) {\n super(`Degenerate LLM output: ${verdict.reasons.map((r) => r.code).join(', ')}`);\n this.name = 'DegenerateOutputError';\n this.verdict = verdict;\n }\n}\n\n/**\n * Throwing wrapper, for dropping straight into an existing retry or fallback\n * chain that already keys off thrown errors.\n */\nexport function assertOutput(\n text: string | null | undefined,\n options: CheckOptions = {},\n): string {\n const verdict = checkOutput(text, options);\n if (!verdict.ok) throw new DegenerateOutputError(verdict);\n // Unreachable for non-strings: those score EMPTY 1 and throw above.\n return text as string;\n}\n","import type { CheckOptions, Verdict } from './types.js';\nimport { checkOutput } from './check.js';\n\n/**\n * Detectors that mean nothing until the response is complete.\n *\n * This is the whole problem with judging a stream. Partial output is short,\n * is cut off, and does not parse as JSON -- not because the model is failing\n * but because it has not finished talking. Run the full check on a half-built\n * response and `TOO_SHORT`, `TRUNCATED` and `INVALID_JSON` fire on every\n * healthy generation in the first few tokens, which is worse than no check at\n * all: it trains you to ignore the guard.\n *\n * What *is* meaningful early is redundancy. A model stuck in a loop is already\n * looping by the time it has emitted a few hundred characters, and no amount\n * of further generation makes it less true. So mid-stream runs exactly the\n * three detectors that measure repetition, and defers the rest to `end()`.\n */\nconst DEFERRED_TO_END: CheckOptions = {\n minLength: 0,\n maxTruncation: null,\n expectJson: false,\n expectLang: null,\n finishReason: undefined,\n\n /*\n * LOW_ENTROPY is deferred for a second reason: cost. The LZ77 pass is\n * 0.4ms at 500 characters and 11ms at its 4000-character sample cap, which\n * is 100x the other two detectors combined -- affordable once per response,\n * ruinous every few hundred characters of every stream.\n *\n * DEFERRING IT IS CONDITIONAL, NOT FREE. The condition is that the redundancy\n * detectors still running here reach a verdict *earlier* than LOW_ENTROPY\n * would have, on every script. Two things make that true today:\n *\n * - For spaced scripts, REPETITION catches what LOW_ENTROPY would, because\n * character-level collapse is also n-gram collapse.\n * - For Han, Kana and Thai, REPETITION is blind -- a loop with no\n * punctuation is a single word token -- and TAIL_LOOP's character mode is\n * what covers it. Measured at the 240-character warmup, that mode scores\n * 0.854-1.000 on every degenerate CJK fixture and fires on the first\n * check. At that same moment LOW_ENTROPY reads 0.453-0.805, i.e. below\n * its own 0.75 threshold on most of them: running it here would detect\n * these *later*, at 100x the cost.\n *\n * SO THIS BREAKS IF: character dispatch is disabled (`maxCharTailLoop: null`,\n * or `nonSpacedCutoff` raised out of reach), or `warmup` is raised past the\n * point where a loop's periodicity has established itself in the window. A\n * 15-character loop unit repeats 16 times in 240 characters against a\n * `minRepeats` of 3, so there is room -- but it is room, not immunity. If you\n * change either, re-measure before assuming this deferral is still safe;\n * otherwise CJK streams silently lose mid-stream detection entirely and the\n * only thing left is the end() check, which is after you have paid.\n */\n maxCompressibility: null,\n};\n\nexport interface StreamGuardOptions extends CheckOptions {\n /**\n * Characters of *new* text between checks. Default 400.\n *\n * Checking on every chunk would re-scan the buffer per token and turn a\n * linear stream into quadratic work. Batching costs a little detection\n * latency and buys a bounded cost per stream.\n */\n checkEvery?: number;\n /**\n * Characters that must arrive before any judgement. Default 240.\n *\n * A loop is not visible in the first sentence, and neither is its absence.\n * Below this the guard abstains rather than guessing -- the same rule the\n * detectors already follow for short samples.\n */\n warmup?: number;\n /**\n * Trailing characters each mid-stream check looks at. Default 2000.\n *\n * Two reasons, and the second matters more. Cost: without a window every\n * check re-scans the whole buffer, so a stream costs quadratic work in its\n * own length. Sensitivity: a model that produced four healthy paragraphs\n * and then began looping is diluted to nothing when measured across all\n * five, which is the same reasoning that makes `tailLoopScore` a separate\n * detector from `repetitionScore`. Recent text is the text in question.\n */\n window?: number;\n}\n\nexport interface StreamGuard {\n /**\n * Feed the next chunk.\n *\n * Returns a verdict only on the chunks where a check actually ran, and\n * `null` on the rest -- so `null` means \"not judged yet\", never \"healthy\".\n * Read `.ok` on what you get back.\n */\n push(chunk: string): Verdict | null;\n /**\n * Full check on the complete text, including the detectors deferred above.\n * Pass the provider's stop reason if you have it; truncation keys off it.\n */\n end(finishReason?: string): Verdict;\n /** Everything pushed so far. */\n readonly text: string;\n /** How many mid-stream checks have run. Useful when tuning `checkEvery`. */\n readonly checks: number;\n}\n\n/**\n * Watches a response as it arrives and reports degeneration before it finishes.\n *\n * The reason to bother: a model that has started looping will keep looping\n * until it hits `max_tokens`, and you pay for every one of those tokens plus\n * the latency of waiting for them. Catching it at character 300 of a 4000\n * character run and aborting turns a slow bad answer into a fast one.\n *\n * This never aborts anything itself -- it holds no controller and knows\n * nothing about your provider. It tells you; you decide.\n */\nexport function createStreamGuard(options: StreamGuardOptions = {}): StreamGuard {\n const { checkEvery = 400, warmup = 240, window = 2000, ...checkOptions } = options;\n\n let text = '';\n let sinceCheck = 0;\n let checks = 0;\n\n /*\n * The first check fires as soon as `warmup` is met; `checkEvery` only\n * spaces out the ones after it. Gating the first on both would make the\n * earlier of the two settings dead -- and it is the first check that\n * decides how many wasted tokens a loop gets to emit, which is the entire\n * point of watching a stream instead of its result.\n */\n const due = () => (checks === 0 ? text.length >= warmup : sinceCheck >= checkEvery);\n\n return {\n get text() {\n return text;\n },\n get checks() {\n return checks;\n },\n\n push(chunk: string): Verdict | null {\n if (typeof chunk !== 'string' || chunk.length === 0) return null;\n\n text += chunk;\n sinceCheck += chunk.length;\n\n if (!due()) return null;\n\n sinceCheck = 0;\n checks += 1;\n // The tail, not the head -- the detectors' own `maxSample` takes the\n // first N characters, which for a stream is the part already judged.\n const recent = text.length > window ? text.slice(-window) : text;\n return checkOutput(recent, { ...checkOptions, ...DEFERRED_TO_END });\n },\n\n end(finishReason?: string): Verdict {\n return checkOutput(text, {\n ...checkOptions,\n finishReason: finishReason ?? checkOptions.finishReason,\n });\n },\n };\n}\n\nexport interface GuardStreamOptions extends StreamGuardOptions {\n /**\n * Called the first time a mid-stream check fails. Abort your request here.\n *\n * The guard deliberately does not own the AbortController: the thing that\n * knows how to cancel a generation is the code that started it, and a\n * detection library that reaches into your transport is a library you\n * cannot use with the next transport.\n */\n onDegenerate?: (verdict: Verdict) => void;\n /**\n * Called once with the final verdict when the source ends normally. Skipped\n * when the stream was cut short, because a verdict on a deliberately\n * abandoned response would describe your own abort, not the model.\n */\n onEnd?: (verdict: Verdict) => void;\n /**\n * Stop yielding once degeneration is detected. Default true.\n *\n * Set false to keep passing chunks through while still being told -- useful\n * for a logging-only rollout, where you want the signal without changing\n * what the user sees.\n */\n stopOnDegenerate?: boolean;\n}\n\n/**\n * Wraps a chunk stream and cuts it off when the model starts looping.\n *\n * ```ts\n * const controller = new AbortController();\n * const guarded = guardStream(model.textStream, {\n * ...presets.chat,\n * onDegenerate: () => controller.abort(),\n * });\n * for await (const chunk of guarded) process.stdout.write(chunk);\n * ```\n *\n * Yields the source's chunks unchanged until then, so it drops into an\n * existing loop without touching what you do with the text.\n */\nexport async function* guardStream(\n source: AsyncIterable<string>,\n options: GuardStreamOptions = {},\n): AsyncGenerator<string, void, undefined> {\n const { onDegenerate, onEnd, stopOnDegenerate = true, ...guardOptions } = options;\n const guard = createStreamGuard(guardOptions);\n let degenerate = false;\n\n for await (const chunk of source) {\n yield chunk;\n\n const verdict = guard.push(chunk);\n if (!verdict || verdict.ok || degenerate) continue;\n\n // Once, not on every subsequent check -- a loop keeps failing by\n // definition, and an abort handler called forty times is a bug report.\n degenerate = true;\n onDegenerate?.(verdict);\n if (stopOnDegenerate) return;\n }\n\n if (!degenerate) onEnd?.(guard.end());\n}\n","/**\n * What a guard should do when the model answered with a tool call.\n *\n * Shared by every adapter for the same reason as `adapter-options.ts`: this is\n * a policy, and two hand-maintained copies of a policy is how one of them\n * quietly stops matching the other.\n *\n * ## The bug this exists to prevent\n *\n * A tool call is not text. OpenAI returns `content: null` alongside\n * `tool_calls`, and the AI SDK returns a `content` array with no `text` part --\n * so an adapter that concatenates text parts and hands the result to\n * `checkOutput` passes it `''`, which scores `EMPTY: 1` and throws. The\n * detector is right; it was asked the wrong question. Every tool-calling turn\n * of every agent fails, which is a false positive on the most common shape of\n * modern LLM traffic.\n *\n * So the rule is: **the presence of tool calls means the text, if any, is a\n * preamble rather than the answer.** Judge it as one, or not at all.\n *\n * ## This type is INTERNAL. It is not public API, at 1.0 or after.\n *\n * It is exported from no subpath and is not reachable by any import path a user\n * has. What is observable is the behaviour: adapters do not fail a response for\n * being a tool call. That behaviour is covered by semver; this module is not.\n */\nimport type { CheckOptions, Verdict } from '../types.js';\nimport { checkOutput } from '../check.js';\n\n/**\n * The detectors that ask \"is this a complete answer\", switched off.\n *\n * A preamble is not a complete answer and was never meant to be, so each of\n * these would be measuring the wrong thing:\n *\n * - `minLength` -- \"Let me look that up\" is sixteen characters and correct.\n * Under `presets.longForm` its 200-character minimum fails every tool call.\n * - `maxTruncation` -- a preamble ends without terminal punctuation as a matter\n * of course, which `truncationScore` reads as 0.55. Under a lowered\n * `maxTruncation` that fires on healthy output.\n * - `expectJson` -- on a tool-calling turn the JSON is in the call arguments,\n * which the provider has already validated against your schema. The prose\n * beside it is prose, and `presets.strictJson` would fail it for being so.\n *\n * `finishReason` is cleared with them: it is the input `maxTruncation` keys off,\n * and leaving it set re-enables the detector that was just switched off.\n *\n * What deliberately stays on is redundancy -- `REPETITION`, `TAIL_LOOP`,\n * `LOW_ENTROPY`. A model that loops in its preamble is still a model that is\n * looping, and those detectors measure that without caring whether the text is\n * a whole answer.\n */\nexport const TOOL_CALL_PREAMBLE: CheckOptions = {\n minLength: 0,\n maxTruncation: null,\n expectJson: false,\n finishReason: undefined,\n};\n\n/**\n * The verdict for a response that carried tool calls, or `null` when there is\n * nothing to judge.\n *\n * `null` is the no-text case, and it is the whole point: a response consisting\n * only of tool calls has no prose to measure, so the honest answer is silence\n * rather than a verdict on the empty string. Callers must treat `null` as \"not\n * judged\" and skip both the action and the `onVerdict` report -- an `EMPTY`\n * logged here would poison a calibration run with a spike of `EMPTY: 1` samples\n * that describe nothing but the agent's tool use.\n */\nexport function checkPreamble(text: string, options: CheckOptions): Verdict | null {\n if (text.trim().length === 0) return null;\n return checkOutput(text, { ...options, ...TOOL_CALL_PREAMBLE });\n}\n","/**\n * Middleware adapter for the Vercel AI SDK.\n *\n * Structurally typed against the SDK rather than importing from it, so this\n * subpath adds no dependency, runtime or otherwise -- `ai` stays an optional\n * peer. The shapes below are the parts of the provider spec this touches and\n * nothing more, which is also what keeps it working across spec versions:\n * `finishReason` is a plain string in v2 and an object in v4, and both are\n * accepted here.\n */\nimport type { Verdict } from './types.js';\nimport type { StreamGuardOptions } from './stream.js';\nimport type { AdapterGuardOptions, DegenerateAction } from './internal/adapter-options.js';\nimport { checkOutput, DegenerateOutputError } from './check.js';\nimport { createStreamGuard } from './stream.js';\nimport { checkPreamble } from './internal/tool-calls.js';\n\n/** `'stop' | 'length' | ...` in older specs, `{ unified, raw }` in v4. */\ntype FinishReasonLike = string | { unified?: string; raw?: string } | null | undefined;\n\ninterface StreamPart {\n type: string;\n /** Present on `text` content parts. */\n text?: string;\n /** Present on `text-delta` stream parts. */\n delta?: string;\n /** Present on the `finish` part. */\n finishReason?: FinishReasonLike;\n}\n\n/**\n * Whether a part is the model calling a tool.\n *\n * Matched by prefix rather than by an exact list because the spec has several\n * and has added to them across versions: `tool-call` on a finished generation,\n * and `tool-input-start` / `tool-input-delta` / `tool-input-end` while\n * streaming. A prefix keeps a part type added in a later `ai` major from\n * silently reading as prose, which is the direction that reintroduces the false\n * positive this guards against.\n */\nconst isToolPart = (part: StreamPart): boolean => part.type.startsWith('tool-');\n\ninterface GenerateResultLike {\n content?: StreamPart[];\n finishReason?: FinishReasonLike;\n}\n\ninterface StreamResultLike {\n stream: ReadableStream<StreamPart>;\n}\n\n/** Normalises both spec shapes to what `truncationScore` expects. */\nfunction finishReasonOf(value: FinishReasonLike): string | undefined {\n if (typeof value === 'string') return value;\n if (value && typeof value === 'object') return value.unified ?? value.raw;\n return undefined;\n}\n\nexport type { DegenerateAction };\n\n/**\n * Shares {@link AdapterGuardOptions} with `llm-output-guard/openai`, so the two\n * adapters cannot drift apart. Reading one set of docs is meant to be enough.\n */\nexport interface OutputGuardOptions extends StreamGuardOptions, AdapterGuardOptions {}\n\n/**\n * Guards a model against returning degenerate output, as AI SDK middleware.\n *\n * ```ts\n * import { wrapLanguageModel } from 'ai';\n * import { outputGuard } from 'llm-output-guard/ai-sdk';\n *\n * const model = wrapLanguageModel({\n * model: groq('llama-3.3-70b-versatile'),\n * middleware: outputGuard({ ...presets.chat, onDegenerate: 'abort' }),\n * });\n * ```\n *\n * On `streamText` this is where it pays: the guard watches deltas as they\n * arrive and cancels the generation the moment a loop is detectable, rather\n * than letting the model run to `max_tokens` on your budget.\n */\nexport function outputGuard(options: OutputGuardOptions = {}) {\n const { onDegenerate = 'throw', onVerdict, ...guardOptions } = options;\n\n const act = (verdict: Verdict, streaming: boolean): void => {\n onVerdict?.(verdict, { streaming });\n if (verdict.ok || onDegenerate === 'ignore') return;\n if (onDegenerate === 'throw') throw new DegenerateOutputError(verdict);\n };\n\n return {\n /**\n * A type-level tag only. It is present because the v3 middleware type\n * (`ai` v6) requires it, while v2 (`ai` v5) has no such field and v4\n * (`ai` v7) relaxed it to any string. `'v3'` is the one literal all three\n * admit, so a single object satisfies every supported major.\n *\n * This is load-bearing on an assumption: that `wrapLanguageModel`'s\n * `doWrap` destructures the hooks and never reads this field. That is true\n * of every version in the peer range, and it is checked -- `npm run\n * check:peer-ai` runs the adapter against each major, so a version that\n * started dispatching on the tag would fail there rather than in\n * production. **If that check is ever removed, remove this tag with it**:\n * without it the claim becomes an assumption again, and the failure it\n * would hide is the adapter being handed the wrong contract.\n */\n specificationVersion: 'v3' as const,\n\n /**\n * Non-streaming. The tokens are already bought by the time this runs, so\n * all it can do is stop a bad answer from being used as a good one.\n */\n async wrapGenerate<T extends GenerateResultLike>({\n doGenerate,\n }: {\n doGenerate: () => PromiseLike<T>;\n }): Promise<T> {\n const result = await doGenerate();\n const content = result.content ?? [];\n const text = content\n .filter((part) => part.type === 'text')\n .map((part) => part.text ?? '')\n .join('');\n\n /*\n * A tool call is an answer, just not a textual one. Judging its (absent)\n * text as a response would fail every tool-calling turn on `EMPTY` --\n * see `internal/tool-calls.ts` for why that is the detector being asked\n * the wrong question rather than the detector being wrong.\n */\n if (content.some(isToolPart)) {\n const verdict = checkPreamble(text, guardOptions);\n if (verdict) act(verdict, false);\n return result;\n }\n\n act(\n checkOutput(text, {\n ...guardOptions,\n finishReason: finishReasonOf(result.finishReason) ?? guardOptions.finishReason,\n }),\n false,\n );\n\n return result;\n },\n\n async wrapStream<T extends StreamResultLike>({\n doStream,\n }: {\n doStream: () => PromiseLike<T>;\n }): Promise<T> {\n const result = await doStream();\n const guard = createStreamGuard(guardOptions);\n let fired = false;\n let sawToolCall = false;\n let finishReason: FinishReasonLike;\n\n const guarded = result.stream.pipeThrough(\n new TransformStream<StreamPart, StreamPart>({\n transform(part, controller) {\n // Forward first: a chunk already generated has been paid for, and\n // withholding it buys nothing but a truncated answer.\n controller.enqueue(part);\n\n if (part.type === 'finish') finishReason = part.finishReason;\n if (isToolPart(part)) sawToolCall = true;\n if (part.type !== 'text-delta' || fired) return;\n\n const verdict = guard.push(part.delta ?? '');\n if (!verdict || verdict.ok) return;\n\n fired = true;\n onVerdict?.(verdict, { streaming: true });\n if (onDegenerate === 'ignore') return;\n\n /*\n * Both of these cancel the source stream, which is what actually\n * stops the provider generating -- the saving is not in skipping\n * chunks we already received but in the ones never produced.\n */\n if (onDegenerate === 'throw') {\n controller.error(new DegenerateOutputError(verdict));\n } else {\n controller.terminate();\n }\n },\n\n flush() {\n // A stream we cut short would only be reported as truncated by us,\n // describing our own abort rather than the model.\n if (fired) return;\n\n /*\n * Same rule as `wrapGenerate`, and it matters here even though\n * nothing throws on this path: a tool-call stream carries no text\n * deltas, so `end()` would report `EMPTY: 1` to `onVerdict` on\n * every one. Those samples are what a `calibrate` run is built\n * from, and a spike of them describes the agent's tool use rather\n * than any degeneration.\n */\n if (sawToolCall) {\n const verdict = checkPreamble(guard.text, guardOptions);\n if (verdict) onVerdict?.(verdict, { streaming: true });\n return;\n }\n\n onVerdict?.(guard.end(finishReasonOf(finishReason)), { streaming: true });\n },\n }),\n );\n\n // Everything the provider returned, with only the stream swapped -- the\n // cast is the spread losing `T`, not a change in what is handed back.\n return { ...result, stream: guarded } as T;\n },\n };\n}\n"]}
|
package/dist/ai-sdk.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as StreamGuardOptions } from './stream-
|
|
2
|
-
import { A as AdapterGuardOptions } from './adapter-options-
|
|
3
|
-
export { D as DegenerateAction } from './adapter-options-
|
|
1
|
+
import { S as StreamGuardOptions } from './stream-DUEP28_p.cjs';
|
|
2
|
+
import { A as AdapterGuardOptions } from './adapter-options-DpTwyLVK.cjs';
|
|
3
|
+
export { D as DegenerateAction } from './adapter-options-DpTwyLVK.cjs';
|
|
4
4
|
|
|
5
5
|
/** `'stop' | 'length' | ...` in older specs, `{ unified, raw }` in v4. */
|
|
6
6
|
type FinishReasonLike = string | {
|
package/dist/ai-sdk.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as StreamGuardOptions } from './stream-
|
|
2
|
-
import { A as AdapterGuardOptions } from './adapter-options-
|
|
3
|
-
export { D as DegenerateAction } from './adapter-options-
|
|
1
|
+
import { S as StreamGuardOptions } from './stream-DUEP28_p.js';
|
|
2
|
+
import { A as AdapterGuardOptions } from './adapter-options-ClbcmvaY.js';
|
|
3
|
+
export { D as DegenerateAction } from './adapter-options-ClbcmvaY.js';
|
|
4
4
|
|
|
5
5
|
/** `'stop' | 'length' | ...` in older specs, `{ unified, raw }` in v4. */
|
|
6
6
|
type FinishReasonLike = string | {
|
package/dist/ai-sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { checkPreamble } from './chunk-
|
|
2
|
-
import { createStreamGuard, DegenerateOutputError, checkOutput } from './chunk-
|
|
1
|
+
import { checkPreamble } from './chunk-Q6W2JCSE.js';
|
|
2
|
+
import { createStreamGuard, DegenerateOutputError, checkOutput } from './chunk-T4DJ6IFG.js';
|
|
3
3
|
|
|
4
4
|
// src/ai-sdk.ts
|
|
5
5
|
var isToolPart = (part) => part.type.startsWith("tool-");
|
package/dist/anthropic.cjs
CHANGED
|
@@ -225,6 +225,27 @@ function languageMismatchScore(text, expected, options = {}) {
|
|
|
225
225
|
return Math.min(1, (best - target) / best);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
// src/internal/json-scope.ts
|
|
229
|
+
function stringValues(value, out = []) {
|
|
230
|
+
if (typeof value === "string") out.push(value);
|
|
231
|
+
else if (Array.isArray(value)) for (const item of value) stringValues(item, out);
|
|
232
|
+
else if (value !== null && typeof value === "object") {
|
|
233
|
+
for (const item of Object.values(value)) stringValues(item, out);
|
|
234
|
+
}
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
237
|
+
function redundancySpans(text, scope) {
|
|
238
|
+
if (scope !== "jsonValues") return [text];
|
|
239
|
+
let parsed;
|
|
240
|
+
try {
|
|
241
|
+
parsed = JSON.parse(stripFence(text));
|
|
242
|
+
} catch {
|
|
243
|
+
return [text];
|
|
244
|
+
}
|
|
245
|
+
const values = stringValues(parsed);
|
|
246
|
+
return values.length > 0 ? values : [""];
|
|
247
|
+
}
|
|
248
|
+
|
|
228
249
|
// src/check.ts
|
|
229
250
|
var DEFAULTS = {
|
|
230
251
|
minLength: 1,
|
|
@@ -237,7 +258,8 @@ var DEFAULTS = {
|
|
|
237
258
|
expectJson: false,
|
|
238
259
|
allowJsonFence: true,
|
|
239
260
|
maxLangMismatch: 0.6,
|
|
240
|
-
ngram: 3
|
|
261
|
+
ngram: 3,
|
|
262
|
+
redundancyScope: "document"
|
|
241
263
|
};
|
|
242
264
|
function checkOutput(text, options = {}) {
|
|
243
265
|
const opts = { ...DEFAULTS, ...options };
|
|
@@ -273,8 +295,9 @@ function checkOutput(text, options = {}) {
|
|
|
273
295
|
`Response is ${text.trim().length} chars, below the ${opts.minLength} minimum.`
|
|
274
296
|
);
|
|
275
297
|
}
|
|
298
|
+
const spans = redundancySpans(text, opts.redundancyScope);
|
|
276
299
|
if (opts.maxRepetition != null) {
|
|
277
|
-
const s = repetitionScore(
|
|
300
|
+
const s = Math.max(...spans.map((span) => repetitionScore(span, { n: opts.ngram })));
|
|
278
301
|
add(
|
|
279
302
|
"REPETITION",
|
|
280
303
|
s,
|
|
@@ -283,7 +306,12 @@ function checkOutput(text, options = {}) {
|
|
|
283
306
|
);
|
|
284
307
|
}
|
|
285
308
|
{
|
|
286
|
-
|
|
309
|
+
let worst = { score: -1, mode: "word" };
|
|
310
|
+
for (const span of spans) {
|
|
311
|
+
const detail = tailLoopDetail(span, { nonSpacedCutoff: opts.nonSpacedCutoff });
|
|
312
|
+
if (detail.score > worst.score) worst = detail;
|
|
313
|
+
}
|
|
314
|
+
const { score, mode } = worst;
|
|
287
315
|
const threshold = mode === "char" ? opts.maxCharTailLoop : opts.maxTailLoop;
|
|
288
316
|
if (threshold != null) {
|
|
289
317
|
add(
|