llm-output-guard 1.11.0 → 1.11.2
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 +3 -1
- package/dist/bin.cjs +43 -4
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +43 -4
- package/dist/bin.js.map +1 -1
- package/package.json +22 -2
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Your retry layer watches for `429`, `5xx` and timeouts. It cannot see a model th
|
|
|
32
32
|
looped until `max_tokens`, returned `{}`, stopped mid-sentence, or answered in the
|
|
33
33
|
wrong language — because all of those arrive as a **successful request**.
|
|
34
34
|
|
|
35
|
-
This produces the signal that layer is missing. Zero dependencies, ~
|
|
35
|
+
This produces the signal that layer is missing. Zero dependencies, ~6 KB min+gzip,
|
|
36
36
|
synchronous, no network.
|
|
37
37
|
|
|
38
38
|
```bash
|
|
@@ -267,6 +267,8 @@ because only one of those is evidence.
|
|
|
267
267
|
## Design notes
|
|
268
268
|
|
|
269
269
|
- **Zero runtime dependencies**, enforced in CI. Node ≥ 18; works on edge, browser, Deno, Bun.
|
|
270
|
+
- **The size claim is a budget, not a memory.** `npm run size` bundles each entry, minifies and gzips it, and fails over budget — enforced in CI beside the zero-dependency check, because a number in prose is the cheapest thing in a repo to go stale.
|
|
271
|
+
- **Types resolve on old and new TypeScript alike.** Every subpath is listed in `typesVersions` as well as `exports`, so `moduleResolution: "node"` — still the default under `module: commonjs` — sees the adapters instead of only the root.
|
|
270
272
|
- **Pure and synchronous.** No network, no clock, no randomness — safe on a hot path, trivial to test.
|
|
271
273
|
- **Scores, not booleans.** Detectors report 0–1 and leave the threshold decision to you.
|
|
272
274
|
- **Abstains rather than guesses.** Samples too short to judge score 0.
|
package/dist/bin.cjs
CHANGED
|
@@ -1033,6 +1033,16 @@ function extractTurn(value) {
|
|
|
1033
1033
|
return typeof value === "string" && value.length > 0 ? { text: value } : null;
|
|
1034
1034
|
}
|
|
1035
1035
|
const record = value;
|
|
1036
|
+
if (typeof record.role === "string" && record.role !== "assistant" && record.role !== "model") {
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
const messageCalls = Array.isArray(record.tool_calls) ? record.tool_calls : Array.isArray(record.toolCalls) && typeof record.content === "string" ? record.toolCalls : null;
|
|
1040
|
+
if (messageCalls && (typeof record.content === "string" || record.content == null)) {
|
|
1041
|
+
return toTurn({ choices: [{ message: { ...record, tool_calls: messageCalls } }] });
|
|
1042
|
+
}
|
|
1043
|
+
if (typeof record.content === "string" && record.role != null) {
|
|
1044
|
+
return record.content.length > 0 ? { text: record.content } : null;
|
|
1045
|
+
}
|
|
1036
1046
|
if (Array.isArray(record.toolCalls)) {
|
|
1037
1047
|
const calls = record.toolCalls;
|
|
1038
1048
|
const native = calls.every(
|
|
@@ -1064,11 +1074,32 @@ function extractTurn(value) {
|
|
|
1064
1074
|
if (typeof record.text === "string") return { text: record.text };
|
|
1065
1075
|
return null;
|
|
1066
1076
|
}
|
|
1077
|
+
var TURN_KEYS = ["turns", "messages", "history", "conversation", "steps"];
|
|
1067
1078
|
function extractTurns(value) {
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1079
|
+
const read = (list) => {
|
|
1080
|
+
if (!Array.isArray(list)) return null;
|
|
1081
|
+
const turns = list.map(extractTurn).filter((turn) => turn !== null);
|
|
1082
|
+
return turns.length > 0 ? turns : null;
|
|
1083
|
+
};
|
|
1084
|
+
if (Array.isArray(value)) return read(value);
|
|
1085
|
+
if (!value || typeof value !== "object") return null;
|
|
1086
|
+
const record = value;
|
|
1087
|
+
const named = (from) => {
|
|
1088
|
+
for (const key of TURN_KEYS) {
|
|
1089
|
+
const found = read(from[key]);
|
|
1090
|
+
if (found) return found;
|
|
1091
|
+
}
|
|
1092
|
+
return null;
|
|
1093
|
+
};
|
|
1094
|
+
const here = named(record);
|
|
1095
|
+
if (here) return here;
|
|
1096
|
+
for (const nested of Object.values(record)) {
|
|
1097
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
1098
|
+
const found = named(nested);
|
|
1099
|
+
if (found) return found;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return null;
|
|
1072
1103
|
}
|
|
1073
1104
|
function describe2(item) {
|
|
1074
1105
|
const { label, verdict } = item;
|
|
@@ -1277,6 +1308,14 @@ a native turn, or the raw envelope from any adapter this package ships:
|
|
|
1277
1308
|
[{"text":"Reading.","toolCalls":[{"name":"read_file","arguments":{"p":"a.ts"}}]}]
|
|
1278
1309
|
{"run":"job-14","turns":[{"choices":[{"message":{"content":"..."}}]}]}
|
|
1279
1310
|
|
|
1311
|
+
The turns are found under "turns", "messages", "history", "conversation" or
|
|
1312
|
+
"steps" -- at the top level or one object down -- so a request body or a logged
|
|
1313
|
+
run needs no reshaping. Only the model's own messages are read: system, user
|
|
1314
|
+
and tool messages are skipped rather than counted as turns.
|
|
1315
|
+
|
|
1316
|
+
{"turns":[{"role":"assistant","content":"\u2026","tool_calls":[\u2026]},
|
|
1317
|
+
{"role":"tool","content":"\u2026"}]}
|
|
1318
|
+
|
|
1280
1319
|
A file that parses whole as one array is read as a single run, so a run logged
|
|
1281
1320
|
as one pretty-printed document works without reshaping.
|
|
1282
1321
|
|