llm-json-rescuer
v0.2.2
Published
JSON parser/rescuer for LLM responses: direct, progressive and robust modes, with configurable thought-tag handling
Maintainers
Readme
llm-json-rescuer
A tiny, practical JSON parser/recovery toolkit for LLM outputs (ChatGPT, Claude, Gemini, etc.).
Built to handle messy “almost-JSON” responses in runtime/production code, not just development.
It can recover JSON from:
- Markdown fences (
json ...) - Preambles like “Here is the JSON:”
- Wrappers like
<output>...</output> - “Thinking” blocks (
<think>...</think>,<thinking>...</thinking>) - Control/invalid characters and weird newlines
- Partially broken JSON (e.g. trailing commas)
Modes
- Direct: simple cleanup + parse (fast)
- Progressive: staged recovery pipeline (recommended; returns
attempts[]) - Robust: Progressive preset with more aggressive defaults
Everything is exported from the package root and also via subpaths
./parserand./utils.
Install
npm i llm-json-rescuerImports
From the root:
import { safeParseLLMProgressive, safeParseJsonDirect } from 'llm-json-rescuer';Via subpaths (clarity / tree-shaking):
import { safeParseLLMProgressive, safeParseLLMRobust } from 'llm-json-rescuer/parser';
import { extractAllJsonBlocks, isJsonString } from 'llm-json-rescuer/utils';Quick Start (recommended)
JavaScript (Node runtime)
import { safeParseLLMProgressive } from 'llm-json-rescuer';
const raw = `
Here is the JSON:
\`\`\`json
<think>thinking...</think>
{ "ok": true, "user": { "id": "1", "name": "Ana" } }
\`\`\`
Anything else you need?
`;
const res = safeParseLLMProgressive(raw);
if (res.success) {
console.log(res.data.user?.name); // Ana
} else {
console.error(res.error);
console.error(res.attempts); // strategy history
}TypeScript (typed output)
import { safeParseLLMProgressive } from 'llm-json-rescuer';
type Output = {
ok: boolean;
user?: { id: string; name: string };
};
const raw = `
Here is the JSON:
\`\`\`json
<think>thinking...</think>
{ "ok": true, "user": { "id": "1", "name": "Ana" } }
\`\`\`
Anything else you need?
`;
const res = safeParseLLMProgressive<Output>(raw);
if (res.success) {
console.log(res.data.user?.name); // "Ana"
} else {
console.error(res.error);
console.error(res.attempts);
}ℹ️ TypeScript types/generics don’t exist at runtime. If you see
SyntaxError: Unexpected identifier 'Output', you’re running TypeScript code as plain JavaScript — run it via TS tooling (ts-node / tsx / build step).
Node ESM note (common warning)
If you run a .js file that uses import ... from and your project’s package.json does not declare a module type, Node may warn:
[MODULE_TYPELESS_PACKAGE_JSON]
Fix (in your app, not this library):
- add
"type": "module"to your app’spackage.json, or - rename the file to
.mjs
Parsing Modes
1) Direct (fast & simple)
Best when the text is already close to valid JSON:
import { safeParseJsonDirect } from 'llm-json-rescuer/parser';
const raw = `OK: {"a":1,"b":2} thanks`;
const res = safeParseJsonDirect<{ a: number; b: number }>(raw, {
extractJsonBlock: true,
extractMode: 'first',
});
console.log(res.success ? res.data : res.error);2) Progressive (core recovery pipeline)
Runs a staged pipeline controlled by level:
import { safeParseLLMProgressive } from 'llm-json-rescuer/parser';
const res = safeParseLLMProgressive(raw, {
level: 2, // default
extractJsonBlock: true,
extractMode: 'first',
thought: { mode: 'after' }, // default
});
if (!res.success) {
console.log(res.error);
console.log(res.attempts);
}3) Robust (Progressive preset)
More aggressive defaults (level: 3):
import { safeParseLLMRobust } from 'llm-json-rescuer/parser';
const res = safeParseLLMRobust(raw, {
extractJsonBlock: true,
extractMode: 'first',
thought: { mode: 'after' },
});Thought handling (<think>...</think> etc.)
thought.mode supports:
keep: keep everythingstrip: remove all thought blocksafter: keep only what comes after the last closing tag (default)
Example:
import { safeParseLLMProgressive } from 'llm-json-rescuer/parser';
const raw = `<think>internal reasoning...</think>{ "ok": true }`;
console.log(
safeParseLLMProgressive(raw, { thought: { mode: 'after' } }).success
);API
All safe* functions return ParseResult<T> (never throw).
Non-safe variants throw LlmJsonRescueError (with attempt history).
safeParseJsonDirect<T>(raw, options?)parseJsonDirect<T>(raw, options?)safeParseLLMProgressive<T>(raw, options?)parseLLMProgressive<T>(raw, options?)safeParseLLMRobust<T>(raw, options?)
Types
BaseParseOptions,ProgressiveOptionsParseStrategyNameParseAttempt,ParseResult<T>
Utils
extractAllJsonBlocks(text)
import { extractAllJsonBlocks } from 'llm-json-rescuer/utils';
const blocks = extractAllJsonBlocks(`x { "a": 1 } y [1,2,3] z`);
console.log(blocks); // ['{ "a": 1 }', '[1,2,3]']isJsonString(input)
import { isJsonString } from 'llm-json-rescuer/utils';
console.log(isJsonString(`{"ok":true}`)); // true
console.log(isJsonString(`hello`)); // falsefixTrailingCommas(jsonStr)
Removes trailing commas before } or ].
convertToJsonSchema(data)
Generates a simple “JSON Schema-like” shape from an object.
Development
npm run lint
npm run lint:fix
npm test
npm run test:coverage
npm run buildLicense
MIT
