Strict JSON LLM outputs with multi-provider support (Anthropic / OpenAI / DeepSeek / Kimi / Google)
Project description
guarded-llm
Strict JSON output from any LLM, with retry + schema validation + multi-provider routing in one call.
Pre-release: not yet on PyPI; install from source.
What it does
LLMs hallucinate JSON. guarded-llm wraps a single call into a 4-layer
defense:
- Strip markdown fences and locate the JSON envelope in chatty output.
- State-machine fix common drift: trailing commas, single quotes,
NaN/Infinity, comments, unescaped interior quotes, BOM. - Parse with
json.loads. - Validate against a schema (JSON Schema via
LLMSchema, or any class exposing a.validate(d) -> (ok, err, instance)classmethod).
If any layer fails, the call is retried with the previous error injected back
into the prompt as a hint. After max_retries, it returns a structured
GuardrailResult with parsed, errors, attempts, and cost_usd.
Install
# From source (pre-release)
pip install git+https://github.com/dada8899/structural-isomorphism#subdirectory=packages/guarded-llm
# Once published
pip install guarded-llm
Optional extras for vendor SDKs (you don't need them — built-in adapters use
plain HTTP via requests):
pip install guarded-llm[anthropic] # if you want the official anthropic SDK
pip install guarded-llm[openai] # if you want the official openai SDK
pip install guarded-llm[dev] # pytest, ruff, mypy
Quickstart
from guarded_llm import guardrailed_llm_call, LLMSchema
schema = LLMSchema({
"type": "object",
"properties": {
"verdict": {"type": "string", "enum": ["KEEP", "REJECT", "SPLIT", "MERGE"]},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
},
"required": ["verdict", "confidence"],
})
result = guardrailed_llm_call(
provider="deepseek",
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Is gravity a self-organized criticality system?"}],
schema=schema,
max_retries=3,
budget_cap_usd=0.05,
)
if result.ok:
print(result.parsed) # {"verdict": "REJECT", "confidence": 0.9}
print(result.cost_usd) # 0.0012
print(result.attempts) # 1
else:
print("All retries failed:", result.errors)
Set DEEPSEEK_API_KEY in your env (or pass api_key= via **kwargs).
Features
- Multi-provider out of the box — DeepSeek, Anthropic Claude, OpenAI, Kimi (Moonshot). All built-in adapters use plain HTTP — no vendor SDK required.
- Pluggable — subclass
BaseProvider+ callregister_provider("name", cls)to add anything else (OpenRouter, Together, Google Gemini, local Ollama, …). - Schema validation — bring your own JSON Schema via
LLMSchema(...), or use the legacy dataclass schemas (Layer3CriticVerdict,Layer4Prediction,B3EnsembleReview) shipped from the structural-isomorphism V4 pipeline. - Automatic retry with error feedback — each retry's prompt includes the prior validation error so the model can self-correct.
- Budget cap — pass
budget_cap_usd=and aBudgetExceededErrorfires before you torch your wallet on a runaway loop. - Structured result —
GuardrailResultexposesparsed,errors,attempts,cost_usd, andraw_outputsfor debugging. - Drop-in compat — supports the legacy
guardrailed_llm_call(prompt_fn, llm_caller, schema_cls, max_retries)signature for existing callers.
How it compares
| Feature | guarded-llm | instructor | outlines | guidance |
|---|---|---|---|---|
| Multi-provider | ✅ 4 built-in | ✅ many | partial (HF-first) | partial |
| Works on closed-model APIs | ✅ | ✅ | mostly local | partial |
| JSON Schema input | ✅ | via Pydantic | ✅ | ✅ |
| State-machine fixer for drift | ✅ | retry only | grammar-constrained | grammar-constrained |
| No SDK lock-in (HTTP only) | ✅ | requires vendor SDK | ✅ | ✅ |
| Cost tracking | ✅ | ❌ | ❌ | ❌ |
| Budget cap | ✅ | ❌ | ❌ | ❌ |
| Footprint | tiny (3 deps) | medium | large (torch optional) | medium |
guarded-llm's niche: closed-model APIs where you can't grammar-constrain
the decoder, but you still want strict JSON without writing five layers of
defensive parsing. If you're using local HF models, prefer outlines'
grammar-level approach; if you're already deep in Pydantic, instructor is
elegant.
Provider environment variables
| Provider | API key env | Base URL env (optional) |
|---|---|---|
deepseek |
DEEPSEEK_API_KEY |
DEEPSEEK_BASE_URL |
anthropic |
ANTHROPIC_API_KEY |
ANTHROPIC_BASE_URL |
openai |
OPENAI_API_KEY |
OPENAI_BASE_URL |
kimi |
KIMI_API_KEY or MOONSHOT_API_KEY |
KIMI_BASE_URL |
You can also pass api_key=, base_url= directly in the call.
License
MIT — see LICENSE.
Contributing
This package lives inside the structural-isomorphism
monorepo at packages/guarded-llm/. PRs welcome — see the
repo issues page.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file guarded_llm-0.1.0.tar.gz.
File metadata
- Download URL: guarded_llm-0.1.0.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
faef638f99eb80275e487dea7032c64579168d2ab067621062e35fe72708883d
|
|
| MD5 |
3ea38622d60c480c7756f5ac43427407
|
|
| BLAKE2b-256 |
7d077307fe294b991075b829a465afd504b80df18a7dc0653d11debef5dcd4c5
|
File details
Details for the file guarded_llm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: guarded_llm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3e3de8a486346448bff327fc85d99766c6be51ccac066055398cf4c017eb62f
|
|
| MD5 |
c4a8ee51181074d00ab39594f85e272b
|
|
| BLAKE2b-256 |
867cc14d8d9f02d6a2ca97c2521b63dc4d9d0321885c5a7dbab43b373ec7ed04
|