Extract structured, validated JSON from any LLM — OpenAI, Anthropic, Gemini — with schema validation, semantic rules, and auto-retry.
Project description
llm-extractor
Extract structured, validated JSON from any LLM.
pip install llm-extractor — then stop fighting JSON parsing bugs, provider-specific APIs, and silent semantic failures. One unified interface to extract structured data from OpenAI, Anthropic, and Gemini — with automatic retries, semantic rules, and full observability.
The Problem (2026)
Even with native structured outputs, Python developers still hit:
| Pain | Reality |
|---|---|
| Provider fragmentation | OpenAI, Anthropic, Gemini all use different structured output APIs |
| Semantic failures | Valid JSON with nonsense values (price: -999, email: "not-an-email") |
| Silent failures | Model returns {} or truncated object — no error raised |
| Dumb retries | Most code retries blindly with the same broken prompt |
| Zero observability | You know it failed but not why or how often |
llm-extractor fixes all five.
Installation
pip install llm-extractor # core only
pip install "llm-extractor[openai]" # + OpenAI
pip install "llm-extractor[anthropic]" # + Anthropic
pip install "llm-extractor[google]" # + Gemini
pip install "llm-extractor[all]" # all providers
Quick Start
from llm_extract import extract, Schema, SemanticRule
# 1. Define your output schema
schema = Schema({
"name": str,
"age": int,
"email": str,
"score": float,
})
# 2. Add semantic rules
schema.add_rule(SemanticRule("age", min_value=0, max_value=150))
schema.add_rule(SemanticRule("score", min_value=0.0, max_value=100.0))
schema.add_rule(SemanticRule("email", pattern=r"^[^@]+@[^@]+\.[^@]+$"))
# 3. Extract structured output — works across all providers
result = extract(
prompt="Extract info: John Doe, 34 years old, john@example.com, scored 87.5",
schema=schema,
provider="openai", # or "anthropic", "gemini", "auto"
model="gpt-4o-mini",
api_key="sk-...",
max_retries=3,
)
print(result.data)
# {'name': 'John Doe', 'age': 34, 'email': 'john@example.com', 'score': 87.5}
print(result.attempts) # 1
print(result.provider) # 'openai'
Pydantic Models
from pydantic import BaseModel
from llm_extract import extract
class Product(BaseModel):
name: str
price: float
in_stock: bool
tags: list[str]
result = extract(
prompt="Extract: Blue Widget, costs $29.99, currently available, tagged as gadget and home",
schema=Product,
provider="anthropic",
model="claude-haiku-4-5-20251001",
api_key="sk-ant-...",
)
product: Product = result.typed_data(Product)
print(product.price) # 29.99
Semantic Rules
from llm_extract import SemanticRule, Schema
schema = Schema({"status": str, "count": int, "ratio": float})
# Enum constraint
schema.add_rule(SemanticRule("status", allowed_values=["active", "inactive", "pending"]))
# Range constraint
schema.add_rule(SemanticRule("count", min_value=0))
schema.add_rule(SemanticRule("ratio", min_value=0.0, max_value=1.0))
# Regex pattern
schema.add_rule(SemanticRule("email", pattern=r"^[^@]+@[^@]+\.[^@]+$"))
# Custom validator function
schema.add_rule(SemanticRule("count", validator=lambda v: v % 2 == 0, message="count must be even"))
Observability
from llm_extract import extract, ExtractObserver
observer = ExtractObserver()
result = extract(
prompt="...",
schema=schema,
provider="openai",
model="gpt-4o-mini",
api_key="...",
observer=observer,
)
# Per-call report
report = observer.report()
print(report.total_attempts) # 2
print(report.validation_failures) # [ValidationFailure(field='age', reason='below min_value 0')]
print(report.raw_responses) # ['{"age": -5, ...}', '{"age": 34, ...}']
print(report.latency_ms) # [342, 289]
print(report.tokens_used) # {'input': 120, 'output': 45}
Multi-Provider Fallback
result = extract(
prompt="...",
schema=schema,
provider="auto", # tries providers in priority order
fallback_chain=[
{"provider": "openai", "model": "gpt-4o-mini", "api_key": "sk-..."},
{"provider": "anthropic", "model": "claude-haiku-4-5-20251001", "api_key": "sk-ant-..."},
{"provider": "gemini", "model": "gemini-1.5-flash", "api_key": "AIza..."},
],
max_retries=2,
)
print(result.provider) # whichever succeeded
Async Support
import asyncio
from llm_extract import aextract
async def main():
result = await aextract(
prompt="...",
schema=schema,
provider="openai",
model="gpt-4o-mini",
api_key="...",
)
print(result.data)
asyncio.run(main())
Raise on Failure
from llm_extract import extract, ExtractValidationError
try:
result = extract(..., raise_on_failure=True)
except ExtractValidationError as e:
print(e.result.failures) # list of ValidationFailure
print(e.result.raw) # last raw LLM response
JSON Schema Input
from llm_extract import extract, Schema
schema = Schema({
"type": "object",
"properties": {
"title": {"type": "string"},
"year": {"type": "integer"},
"rating": {"type": "number"}
},
"required": ["title", "year", "rating"]
})
result = extract(prompt="...", schema=schema, ...)
OpenAI-Compatible Endpoints
result = extract(
prompt="...",
schema=schema,
provider="openai",
model="mistral-7b-instruct",
api_key="your-key",
base_url="https://your-openai-compatible-endpoint/v1",
)
Why llm-extractor?
- Unified API — one interface for OpenAI, Anthropic, Gemini, and any OpenAI-compatible endpoint
- Schema-first — define once with
dict,pydantic.BaseModel, or JSON Schema - Semantic rules — enforce business logic, not just types
- Smart retries — correction prompts tell the model exactly what went wrong
- Full observability — every attempt, failure, token count, and latency recorded
- Zero magic — no hidden prompt injection, no global state, fully inspectable
License
MIT
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 llm_extractor-1.0.0.tar.gz.
File metadata
- Download URL: llm_extractor-1.0.0.tar.gz
- Upload date:
- Size: 22.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0382bf93a426ccc9e686ee111373d037e34a163797a08bb2b8f549afb8aca85a
|
|
| MD5 |
91b4e208b71a7f4a0c21963e99838c99
|
|
| BLAKE2b-256 |
2c6feeae123ae5a748e5ab9ec210a4e749f6c24047094323a7b44699dfee29ab
|
File details
Details for the file llm_extractor-1.0.0-py3-none-any.whl.
File metadata
- Download URL: llm_extractor-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
402625e35fed2af97b4dbf8d5960d1fccb094cd55d1f6df1da98351283a06609
|
|
| MD5 |
7128a0ec55aa6381cddcfe563e92a89d
|
|
| BLAKE2b-256 |
559e3d5fcc09cdb5a431f3b7239df6797b19d8cd6f82051410637cdb5af9008b
|