A Python library that validates and auto-repairs JSON output from LLMs against Pydantic schemas with pluggable business rules.
Project description
SchemaSplint
A lightweight, zero-dependency Python library that validates, auto-repairs, and enforces business rules on JSON outputs generated by Large Language Models (LLMs) such as Google Gemini, OpenAI GPT, and Anthropic Claude.
What It Does
schemasplint acts as a guardrail layer between raw LLM outputs and your application code. It ensures that JSON returned by AI models strictly conforms to expected structure and domain logic before your application consumes it.
- Schema Validation: Guarantees raw LLM JSON matches your expected Pydantic models.
- Categorized Error Reporting: Classifies failures cleanly into
MissingField,TypeMismatch,MalformedJSON, andBusinessRuleViolation. - Deterministic Auto-Repair: Fixes common LLM formatting mistakes (markdown fences, trailing commas, non-JSON conversational text, numeric string coercion) instantly without calling or paying for extra AI API calls.
- Pluggable Business Rules: Custom guardrail checks for domain constraints (e.g., preventing overlapping schedule slots, enforcing numeric range bounds).
- Embedded SQLite Logging: Records validation metrics (pass rate, latency in ms, error breakdown, auto-repairs) to a local SQLite database for observability.
Why SchemaSplint? (The Problem It Solves)
LLMs are probabilistic and frequently produce output flaws that break production applications:
- Markdown Fences & Noise: Wrapping JSON in
```json ... ```or prepending conversational greetings ("Here is your JSON:"). - Invalid Syntax: Trailing commas before closing brackets or quotes in numeric fields.
- Type Mismatches: Outputting strings like
"42"when your code expects an integer42. - Logical Violations: Producing syntactically valid JSON that violates domain rules (e.g., booking two overlapping appointments at 10:00 AM).
Instead of retrying expensive LLM calls or writing custom regex patches for every prompt, schemasplint deterministically repairs formatting errors and validates business constraints in less than a millisecond.
Installation
pip install schemasplint
(Requires Python 3.10+ and Pydantic v2+)
Quickstart
from pydantic import BaseModel
from schemasplint import Guard, Logger, OverlapRule, RangeRule
# 1. Define your Pydantic schema
class Task(BaseModel):
label: str
start_time: str
end_time: str
priority: int
class DailySchedule(BaseModel):
user_id: int
tasks: list[Task]
# 2. Configure guard with pluggable business rules & logging
logger = Logger("schemasplint.db")
guard = Guard(
schema=DailySchedule,
rules=[
OverlapRule(items_field="tasks", start_field="start_time", end_field="end_time"),
RangeRule(field="user_id", min_value=1),
],
logger=logger,
)
# 3. Messy LLM JSON response string
raw_llm_response = """
Here is the requested schedule:
```json
{
"user_id": "101",
"tasks": [
{"label": "Team Standup", "start_time": "09:00", "end_time": "09:30", "priority": "1"},
{"label": "Deep Work", "start_time": "10:00", "end_time": "12:00", "priority": "2"},
]
}
"""
4. Validate with auto-repair enabled
result = guard.validate(raw_llm_response, auto_repair=True)
if result.success: print("Validation Succeeded!") print(f"User ID: {result.parsed_data.user_id}") print(f"Tasks Count: {len(result.parsed_data.tasks)}") if result.repaired: print("Auto-repair log:", result.repair_changes) else: print("Validation Failed!") for err in result.errors: print(f"[{err.error_type.value}] {err.field}: {err.message}")
5. Check SQLite validation summary metrics
summary = logger.get_summary() print(f"Pass Rate: {summary['pass_rate']}% across {summary['total_runs']} runs")
---
## Benchmark Results
Tested against 120 programmatically generated examples across 10 known LLM-output failure categories (markdown fences, trailing commas, conversational wrapper text, numeric-string type mismatches, missing fields, broken syntax, and business-rule violations).
- **Pass rate without repair:** 20.0%
- **Pass rate with SchemaSplint auto-repair:** 60.0%
- **Improvement:** +40.0 percentage points

| Category | Raw Pass % | Repaired Pass % |
|---|---|---|
| broken_syntax | 0.0% | 0.0% |
| clean_valid | 100.0% | 100.0% |
| combined_messy | 0.0% | 100.0% |
| conversational_wrapper | 0.0% | 100.0% |
| markdown_fence | 0.0% | 100.0% |
| missing_field | 0.0% | 0.0% |
| numeric_string | 100.0% | 100.0% |
| overlap_violation | 0.0% | 0.0% |
| range_violation | 0.0% | 0.0% |
| trailing_comma | 0.0% | 100.0% |
*Categories like `missing_field`, `broken_syntax`, `overlap_violation`, and `range_violation` correctly stay at 0% even after repair — these represent cases where data is truly missing or a real business rule is broken, which a deterministic repair layer should never silently paper over rather than surface as an error.*
Reproduce this yourself:
```bash
python benchmark/generate_corpus.py
python benchmark/run_eval.py
License
MIT License
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 schemasplint-0.1.0.tar.gz.
File metadata
- Download URL: schemasplint-0.1.0.tar.gz
- Upload date:
- Size: 15.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
655de4fde02e54234a6fac261f1009dc3cb3e516da75b05f685509121124d0d9
|
|
| MD5 |
3dab737a0ed6e1b74b867f397d234c60
|
|
| BLAKE2b-256 |
82b13ed1bbef19303fe10e85358bc3f8073009cc8cdbc3bed1a212a92d65160c
|
File details
Details for the file schemasplint-0.1.0-py3-none-any.whl.
File metadata
- Download URL: schemasplint-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4dec030510e45ee08d2e2880346e3300d964987f2f6b3646b6556dcd3342bb61
|
|
| MD5 |
65a9ee8fa66a1c7d2eb53efded0d9794
|
|
| BLAKE2b-256 |
3b99f9afb6a6ef4a84c40c38e137f2bb51c048c367794ba65cfad0970ef497f5
|