Wassitai
Ask an LLM a question, get back a validated Python object — not a string you have to parse.
Wassitai sends your prompt with a schema, picks the best structured-output method the provider
supports, then extracts, repairs, validates and coerces the reply through a deterministic pipeline.
If the result still doesn't fit the schema, it re-asks the model. Ships as the importable package
wassitai, with a REST API and a browser playground on top.
pip install wassitai # library
pip install "wassitai[api]" # + REST API and playground
Code written against the pre-0.2 name still works: llm_parser remains importable as a deprecated
alias for wassitai until 2.0. If you have an old editable install of the llm_parser distribution,
run pip uninstall llm_parser first — pip won't replace it, and its source tree would keep shadowing
the alias.
Features
- 🤖 Any OpenAI-compatible provider — Groq, OpenAI, DeepSeek and OpenRouter as presets;
Together, xAI, Ollama, vLLM or a local server with just
base_url=andmodel= - 🧱 Five ways to write a schema — Pydantic model,
{"field": "type"}dict, JSON Schema, or a JSON or XML schema string - 🔁 Object, JSON or XML out — the same validated result, rendered however you need it
- 📊 Lists — ask for
list[Invoice], get a list back - 🧪 Repairs before it fails — lifts JSON out of markdown fences and prose, fixes trailing commas, smart quotes and Python-repr output, then re-asks the model if it still doesn't fit
- 🔒 Strict or flexible validation — exact types for production, coercion while prototyping
- 💡 Diagnostics you can act on — every stage reports what it did, a confidence score falls as repairs accumulate, and errors are a typed tree with fixes in the message
- 🎯 Pydantic all the way through — your model is the schema, the validator and the return type
- 🌐 A playground, not just a library — browser console plus a beginner's guide
- ✅ 329 tests — unit, integration and end-to-end, with no network calls
Python
from wassitai import Client
from pydantic import BaseModel
class Invoice(BaseModel):
number: str
total: float
client = Client.from_provider("groq", api_key="…", model="llama-3.3-70b-versatile")
invoice = client.generate("Extract the invoice from: …", schema=Invoice) # -> Invoice
# A list, by asking for a list schema:
invoices = client.generate("Extract all invoices: …", schema=list[Invoice])
# Never raises — for batch pipelines:
res = client.try_generate("…", schema=Invoice) # -> Result[Invoice]
if res.ok:
print(res.value, res.confidence)
# Text you already have, no provider call:
from wassitai import parse, render
invoice = parse('{"number": "A1", "total": 9.5}', Invoice)
text = render(invoice, "json")
A schema can be a Pydantic model, a plain {"field": "type"} dict, or a JSON Schema.
strict=True enforces exact types; the default coerces close-enough values ("3" → 3).
max_retries caps how often the model is re-asked (default 1).
REST API & playground
uvicorn wassitai.api.app:app --reload
Playground at http://localhost:8000/, beginner's guide at /static/guide.html,
OpenAPI reference at /docs.
| Method | Path | Does |
|---|---|---|
POST |
/v1/generate |
prompt + schema → validated object |
POST |
/v1/parse |
text + schema → validated object (no provider call) |
GET |
/v1/providers |
providers and their capabilities |
GET |
/health |
liveness |
curl -X POST http://localhost:8000/v1/generate \
-H "X-Provider-Key: $GROQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"provider":"groq","prompt":"Extract the invoice: …",
"schema":{"number":"str","total":"float"}}'
Provider keys travel per-request in the X-Provider-Key header (or come from <PROVIDER>_API_KEY
on the server) and are never stored, logged, or pre-filled into the browser. Errors map to status
codes: 422 validation · 401 auth · 429 rate limit · 502 provider · 504 timeout.
How it works
generate() → schema adapter → strategy → provider → pipeline → validated object.
- Strategy — resolved per provider in order
native(JSON-schema mode) →tool_calling→prompt_repair, which is the universal floor and needs nothing from the provider. - Pipeline — six ordered stages:
normalize → extract → syntax_repair → deserialize → validate → coerce. Each records a diagnostic, and the confidence score falls as repairs and re-asks accumulate. - Extending —
Provider,Strategy,SchemaAdapterandStageare the four interfaces; most providers need only a preset inwassitai/providers/__init__.py.
Errors form one tree under LLMParserError: ConfigurationError (your bug), ProviderError
(the network/provider's), and ParseError (the model's).
Providers
Built-in presets speak the OpenAI wire format: groq, openai, deepseek, openrouter.
Any other OpenAI-compatible endpoint (Together, xAI, Ollama, vLLM, local) works by passing
base_url= and model=. Native adapters for Anthropic, Gemini and Bedrock are planned behind the
same Provider interface.
Why Wassitai
| Wassitai | Typical structured-output library | |
|---|---|---|
| Providers | 4 presets, plus any OpenAI-compatible endpoint via base_url= |
An adapter to write per provider |
| Output | A validated object, or the same data as JSON or XML text | JSON only |
| Validation | Strict and flexible, chosen per call | One fixed mode |
| On a bad answer | Repairs it, then re-asks the model | Raises and stops |
| When it fails | Per-stage diagnostics and a confidence score | A single validation error |
| Ships a UI | Playground + beginner's guide | Library only |
Key differentiators
- Dual validation modes — coerce close-enough values while prototyping, enforce exact types in production, without changing your schema.
- It repairs before it gives up — malformed JSON is fixed, and a reply that still doesn't fit the schema is sent back to the model rather than thrown at you.
- It tells the truth about what happened — every stage records a diagnostic, and confidence drops as repairs and re-asks accumulate, so a "valid" object you shouldn't trust is visible.
- Any OpenAI-compatible endpoint — Together, xAI, Ollama, vLLM or a local server need a URL and a model name, not an adapter.
- JSON and XML — the same validated object renders to either.
- A playground, not just a library — a browser console that exercises the real REST API, with a guide written for people who have never called an LLM.
Use cases
- Development — flexible validation for rapid prototyping
- Production — strict validation for data integrity
- Research — compare providers and strategies on the same prompt and schema
- Education — plain-language guide and honest error messages
- Experimentation — the playground, with no code to write
From a checkout
pip install -e ".[api,dev]" # editable install with the API and test deps
pytest # 329 tests, no network calls
pytest -m unit # or: integration, e2e, provider
pytest --cov=wassitai --cov-report=html
CI runs the same suite on Python 3.9–3.13, plus ruff over the current engine, the REST API and the
llm_parser alias (the pre-0.2 core/ and llms/ modules are grandfathered).
Legacy API (removed in 2.0)
LLMParser.ask still works and still supports Gemini and HuggingFace, but is deprecated.
| Old | New |
|---|---|
LLMParser("groq", api_key=k).ask(q, schema=S, fmt="json") |
Client.from_provider("groq", api_key=k).generate(q, schema=S) |
...ask(q, schema=S, many=True) |
...generate(q, schema=list[S]) |
strict_validation=True |
generate(..., strict=True) |
| need the JSON string back | render(obj, "json") |
The Streamlit UI at ui/app.py (streamlit run ui/app.py) targets this legacy path; the
playground above is the current one.
License
MIT.
Contact
Issues and discussions on GitHub · kaa.kella.abdelaziz@gmail.com
Metadata
Release files for wassitai 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wassitai-0.2.0.tar.gz | 256.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wassitai-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 479.2 kB
Release files / wassitai-0.2.0.tar.gz
| Download URL | wassitai-0.2.0.tar.gz |
|---|---|
| Size | 256.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
34db0e5dcb72e0ab17e85781f99d81e555a4aef4fb0402f7a86735750f1b770d
|
|
BLAKE2b-256 checksum How to use checksums |
30f2326e071f1d2d3a8616452cea585ea36617e329c5f095da2b14194577d28c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|
Release files / wassitai-0.2.0-py3-none-any.whl
| Download URL | wassitai-0.2.0-py3-none-any.whl |
|---|---|
| Size | 222.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3bbbbb3d28367e0df960fc3d636adcc4fd1b190dd2b0c4505bfd065270f29dac
|
|
BLAKE2b-256 checksum How to use checksums |
2136cfddfe995a3d6fadd77550bef85fb49d80147a811bad8601fed619b30d9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|