Wassitai
← Playground API reference

Turn messy AI text into clean data.

Large language models answer in free-form text. Wassitai takes that text and hands you back a validated object — the exact fields you asked for, with the right types — so your code can use it directly. No brittle string-parsing, no "sometimes it returns markdown." This guide assumes no prior experience.

The core idea

You give Wassitai two things:

  1. A prompt — what you want, in plain English (e.g. "Extract the invoice from this email").
  2. A schema — the shape of the answer you expect (e.g. a number and a total).

You get back an object that matches your schema, already checked for you. If the model's first answer is slightly malformed (extra markdown, a trailing comma, a number written as text), Wassitai repairs and validates it — and if it still doesn't fit, it asks the model to try again.

Already have the AI's text? You don't need to call a model at all — use Parse to run just the cleanup-and-validate step on text you already have.

Two ways to use it

Both run the exact same engine. The playground you came from is itself built on the REST API.

Quickstart · REST

Send a POST to /v1/generate with your prompt and schema:

curl -X POST $WASSITAI_URL/v1/generate \
  -H "X-Provider-Key: $YOUR_PROVIDER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "groq",
    "prompt": "Extract the invoice from: Invoice A-1, total $9.50 due April.",
    "schema": { "number": "str", "total": "float" }
  }'

Each piece:

To clean up text you already have (no model call, no key), POST to /v1/parse with text instead of prompt.

Quickstart · Python

pip install wassitai
from wassitai import Client
from pydantic import BaseModel

# 1. Describe the shape you want as a normal Python class.
class Invoice(BaseModel):
    number: str
    total: float

# 2. Point at a provider and ask.
client = Client.from_provider("groq", api_key="…")
invoice = client.generate("Extract the invoice: Invoice A-1, total $9.50", schema=Invoice)

print(invoice.number, invoice.total)   # A-1 9.5  — a real, typed object

Already have the text? Skip the model entirely:

from wassitai import parse

invoice = parse('{"number": "A-1", "total": 9.5}', Invoice)

Writing a schema

The schema is just "the fields I want back." You can write it three ways — pick whichever is easiest:

StyleLooks likeUse when
Simple map {"number": "str", "total": "float"} You just want a few fields. Easiest to start with.
JSON Schema {"type": "object", "properties": {…}} You need nested objects, lists, or precise rules.
Python class class Invoice(BaseModel): … You're in Python — you get editor autocomplete and types.

Field types in the simple map: str (text), int (whole number), float (decimal), bool (true/false). An unrecognised type name falls back to text — or is rejected when strict is on.

Reading the response

A successful response gives you more than just the data:

FieldWhat it means
okWhether it succeeded. true → read value; false → read error. (Over REST, failures also map to an HTTP error status — see below.)
valueYour validated object — the actual answer.
renderedPresent only when render is json/xml: the object re-serialised as text.
confidence0–100%. Starts at 100 and falls as the pipeline does more work to make the answer fit — lifting it out of surrounding prose, repairing syntax, coercing types, or re-asking the model. The bigger the intervention, the bigger the drop. 100 means the answer arrived usable exactly as sent.
diagnosticsA short trace of anything the engine had to fix (e.g. "extracted JSON from surrounding text"). Empty means the output was already perfect.
usageHow many tokens the request used (helps you track cost).
errorA human-readable message when ok is false — e.g. which field failed validation.

In the playground, the vertical pipeline spine shows these stages lighting up, and the ring shows the confidence score. A blue ✓ stage passed, gold ↻ means it repaired something, and red ✕ means it failed there.

The options, explained

OptionPlain-language meaning
providerWhich model service to use. The names GET /v1/providers returns — or any OpenAI-compatible endpoint via a custom base URL. (Generate only.)
modelWhich specific model to use. Leave blank for the provider's default.
strictChoices: false (default) or true. On: types must match exactly — the text "3" is rejected where a number is required. Off: close-enough values are converted for you. Off is friendlier while experimenting; on is safer for production data.
strategyHow Wassitai asks the model for structure. Choices: auto (default), native, tool_calling, prompt_repair — explained one by one below. (Generate only.)
renderWhat you get back. Choices: object (default), json, xml. With json/xml the serialised text arrives in the response's rendered field (the object is still in value).
max_retries / “Re-asks”How many times to re-ask the model after a validation failure before giving up. Default 1; use 0 to fail fast, or higher to try harder. (Generate only — parsing never calls a model.)
many / “Expect a list”For a list of items rather than one. In the playground it's a toggle; via the API/library you express it in the schema — REST {"type":"array","items":{…}}, Python List[Invoice].
temperatureHow much the model is allowed to vary its wording. 0 (the default) is the most repeatable — ask twice, get the same answer. Higher values (up to 2) are more inventive, which is rarely what you want when extracting data. Unlike the three below, this one is always sent, so a value you pick elsewhere is applied on top of a temperature Wassitai has already pinned. (Generate only.)
max_tokens / “Max tokens”A ceiling on how much the model may write back. Left blank the provider's own default applies. Raise it if a long answer is being cut off mid-way; lower it to cap what a single call can cost. (Generate only.)
top_p / “Top-p”An alternative way to control variety: the model picks only from the most likely words that together make up this share of the probability. 0.1 is very focused, 1.0 considers everything. Blank means the provider's default. Most people should change either this or temperature, not both. (Generate only.)
stop / “Stop sequences”Text that makes the model stop writing the moment it produces it. Useful when a model keeps adding commentary after the answer. Blank means none. (Generate only.)
experiment / “Experiment label”A name that groups runs together so two configurations can be compared later — for example tool-calling-vs-prompt-repair. It only has an effect when the run is actually stored (see the next row); on its own it changes nothing about the answer.
capture_consent / “Let this run be stored…”Off by default. When on and the server has storage configured, this run may be kept so the parser can be improved from real failures. Both conditions are required — consent alone stores nothing. What gets stored is the prompt, the schema, the model's raw answer and the result; never your API key, IP address or headers. Every response carries a run_id you can quote to have a specific run deleted, and stored runs are removed after 30 days.

The four strategies, one by one

A strategy is how Wassitai coaxes structured output out of a model. Providers differ in what they support, so there are three real methods plus auto:

StrategyHow it worksAvailability / notes
auto (default) Lets Wassitai pick the best method the provider actually supports, trying them in order of reliability: native → tool_calling → prompt_repair. Leave it here unless you're deliberately testing one method. It will never pick something the provider can't do.
native Uses the provider's built-in structured-output mode: your schema is handed to the model and it is constrained to return JSON that matches. The most reliable method. Only when the provider supports it (OpenAI-style json_schema / structured outputs). Forcing it on a provider that doesn't will fail.
tool_calling Describes your schema as a single "tool" (function) and forces the model to call it — the call's arguments are your object. Providers that support tool / function calling. Returns one object; for a list it falls back to prompt & repair.
prompt_repair The universal fallback: your schema is described in the prompt, then the reply is extracted from any surrounding text, repaired, validated, and — if it still doesn't fit — the model is asked to correct it. Works with any provider, even ones with no structured-output features. The most portable method, and the safety net auto ends on.

Not sure which to pick? Leave it on auto. The manual choices exist for when you want to force or compare a specific mechanism.

What the pipeline does

Every response runs through six small steps. You don't have to think about them — but this is what's happening under the hood:

normalizeTidy whitespace & encoding.
extractPull the data out of any surrounding chatter or markdown.
syntax_repairFix trailing commas, smart quotes, unbalanced brackets.
deserializeTurn the text into a real data structure.
validateCheck it against your schema.
coerceNudge close-enough types into the right ones.

If validation fails, Wassitai shows the model its own failed answer and asks it to correct it — up to a small retry limit — instead of giving up.

When things go wrong

The REST API maps failures to standard HTTP status codes, so you can handle them like any other request:

CodeMeaning
400That provider isn't one this API knows. GET /v1/providers lists them.
401Missing or invalid provider key.
413The request body is over the size cap. It is refused rather than truncated, so resending it unchanged won't help.
422The answer didn't fit your schema — or your schema couldn't be read, the request body was rejected, the answer couldn't be written in the format you asked for, or the run couldn't be set up as asked. The error_type field tells them apart.
429The provider rate-limited you — slow down and retry.
500Something failed on our side, and there is nothing you can change in the request to avoid it. An unhandled fault answers text/plain rather than JSON, so treat the body as untrusted and branch on the status code, not on what it says.
502The upstream provider errored.
504The provider took too long.

Every error body carries an error_type — with one exception, the unhandled 500 above, which is plain text and has no JSON to read at all, so check the status before you parse. That string — not the status code alone — is what to branch on. Several failures share 422: ParseError and its two narrower forms ExtractionError (the model answered prose) and RepairError (the JSON was past repairing), plus SchemaError, ValidationError, RenderError and ConfigurationError. Match on the names you handle and treat the rest as one fallback, rather than enumerating them exhaustively — the list grows.

In Python, a validation failure raises a ParseError. Its .diagnostics name the stage that failed and quote the validator's message, including which field didn't fit, and .raw_response holds what the model actually said. There is no .field_errors attribute on the exception; structured per-field entries are a REST-only convenience, at record.result.error.field_errors, when you send include_record.

Next steps