llm-output-guard 1.0.0 → 1.0.1
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 +63 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -318,18 +318,34 @@ gives you a number that describes neither.
|
|
|
318
318
|
|
|
319
319
|
## Detectors
|
|
320
320
|
|
|
321
|
-
| Code | Catches | Signal |
|
|
322
|
-
|
|
323
|
-
| `EMPTY` | Whitespace, lone punctuation, `{}`, empty fences | Content presence |
|
|
324
|
-
| `TOO_SHORT` | Non-empty but useless | Length vs. minimum |
|
|
325
|
-
| `REPETITION` | Loops and stutters | Duplicate word n-gram fraction |
|
|
326
|
-
| `TAIL_LOOP` | Good start, then a stuck ending | Periodicity in the trailing window, over words or characters |
|
|
327
|
-
| `LOW_ENTROPY` | Character-level collapse, token artifacts | Hand-rolled LZ77 compression ratio |
|
|
328
|
-
| `TRUNCATED` | Cut off mid-thought | `finish_reason`, unbalanced fences/brackets |
|
|
329
|
-
| `INVALID_JSON` | Prose around the payload, missing keys | Parse + key contract |
|
|
330
|
-
| `LANG_MISMATCH` | Answered in the wrong language | Function-word profile (coarse, opt-in) |
|
|
321
|
+
| Code | Catches | Signal | Exported as |
|
|
322
|
+
|---|---|---|---|
|
|
323
|
+
| `EMPTY` | Whitespace, lone punctuation, `{}`, empty fences | Content presence | `emptinessScore` |
|
|
324
|
+
| `TOO_SHORT` | Non-empty but useless | Length vs. minimum | `shortnessScore` |
|
|
325
|
+
| `REPETITION` | Loops and stutters | Duplicate word n-gram fraction | `repetitionScore` |
|
|
326
|
+
| `TAIL_LOOP` | Good start, then a stuck ending | Periodicity in the trailing window, over words or characters | `tailLoopScore`, `tailLoopDetail` |
|
|
327
|
+
| `LOW_ENTROPY` | Character-level collapse, token artifacts | Hand-rolled LZ77 compression ratio | `compressibilityScore`, `compressionRatio` |
|
|
328
|
+
| `TRUNCATED` | Cut off mid-thought | `finish_reason`, unbalanced fences/brackets | `truncationScore` |
|
|
329
|
+
| `INVALID_JSON` | Prose around the payload, missing keys | Parse + key contract | `jsonScore`, `stripFence` |
|
|
330
|
+
| `LANG_MISMATCH` | Answered in the wrong language | Function-word profile (coarse, opt-in) | `languageMismatchScore`, `languageProfile`, `supportedLanguages` |
|
|
331
|
+
|
|
332
|
+
Every detector is exported on its own if you only want one, and every name in
|
|
333
|
+
that last column is covered by semver — see **Stability**.
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
import { repetitionScore, tailLoopDetail, stripFence } from 'llm-output-guard';
|
|
337
|
+
|
|
338
|
+
repetitionScore(text); // 0..1, higher is worse
|
|
339
|
+
repetitionScore(text, { n: 4 }); // n-gram size
|
|
340
|
+
tailLoopDetail(text, { mode: 'char' }); // { score, mode } — which tokenizer ran
|
|
341
|
+
stripFence('```json\n{"a":1}\n```'); // '{"a":1}'
|
|
342
|
+
```
|
|
331
343
|
|
|
332
|
-
|
|
344
|
+
Each takes `(text, options?)` and returns a `0..1` score, with three exceptions
|
|
345
|
+
worth knowing: `shortnessScore(text, minChars)` takes its minimum positionally,
|
|
346
|
+
`stripFence` returns a string, and `jsonScore` / `tailLoopDetail` return a detail
|
|
347
|
+
object rather than a bare number. `supportedLanguages` is a value, not a
|
|
348
|
+
function — the array `['id', 'en', 'es']`.
|
|
333
349
|
|
|
334
350
|
## Presets
|
|
335
351
|
|
|
@@ -390,6 +406,42 @@ the bulk and a cluster of outliers is real separation observed in your data
|
|
|
390
406
|
rather than an assumption about rarity — and when that hole rests on one or
|
|
391
407
|
two samples, the report says so.
|
|
392
408
|
|
|
409
|
+
### The same thing, as a function
|
|
410
|
+
|
|
411
|
+
The CLI is a wrapper. If your scores already live somewhere the shell cannot
|
|
412
|
+
reach them — a metrics store, a warehouse query, a test — call `calibrate`
|
|
413
|
+
directly. It takes the same flat objects the JSONL format describes:
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
import { calibrate } from 'llm-output-guard';
|
|
417
|
+
|
|
418
|
+
const { n, summaries } = calibrate(
|
|
419
|
+
[
|
|
420
|
+
{ REPETITION: 0.03, TAIL_LOOP: 0 },
|
|
421
|
+
{ REPETITION: 0.91, TAIL_LOOP: 0.88, modes: { TAIL_LOOP: 'char' } },
|
|
422
|
+
// ...one entry per logged verdict
|
|
423
|
+
],
|
|
424
|
+
{ falsePositiveRate: 0.001 },
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
for (const s of summaries) {
|
|
428
|
+
s.code; // 'REPETITION'
|
|
429
|
+
s.mode; // 'word' | 'char', when the samples recorded one
|
|
430
|
+
s.suggested; // threshold flagging falsePositiveRate of this sample
|
|
431
|
+
s.gap; // { below, above, count, share } | null — stronger evidence
|
|
432
|
+
s.distribution; // { n, nonZero, min, max, p50, p90, p99, p999 }
|
|
433
|
+
s.caveats; // everything that makes `suggested` untrustworthy
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
`modes` rides along in the same object and is not read as a score. Log it, and
|
|
438
|
+
`summaries` comes back segmented — one entry per `code`+`mode` — for the reason
|
|
439
|
+
in the paragraph above. `summarise(code, scores, options)` is exported too, for
|
|
440
|
+
when you have one detector's numbers already grouped.
|
|
441
|
+
|
|
442
|
+
**Read `caveats` before `suggested`.** It is where a sample too small for the
|
|
443
|
+
requested rate says so, and a `suggested` number carries no warning of its own.
|
|
444
|
+
|
|
393
445
|
## On thresholds
|
|
394
446
|
|
|
395
447
|
A miss is annoying. **A false positive is worse**: a healthy response gets discarded and retried against a slower provider for nothing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-output-guard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Detect degenerate LLM output that arrives with a 200 OK. Zero dependencies, deterministic, composes with any retry or fallback layer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"./package.json": "./package.json"
|
|
39
39
|
},
|
|
40
40
|
"bin": {
|
|
41
|
-
"llm-output-guard": "
|
|
41
|
+
"llm-output-guard": "dist/bin.js"
|
|
42
42
|
},
|
|
43
43
|
"files": [
|
|
44
44
|
"dist"
|