The format
Validation and repair
Every line is checked against your app's components as it streams, and the API asks the same model to rewrite only the lines that break a rule.
A model gets things wrong: an attribute that doesn't exist, a value outside its list, a component it made up. The core catches each one on its line, as the answer streams, and the API repairs only those lines.
Lenient where the intent is clear
- Values match without case, and a few synonyms count:
errormeansdanger,columnmeansbar. These are reported as notes, not errors. - A bad value is fixed at its edge: the default if there is one, or an optional attribute is dropped. A bad table row drops only that row.
- An unknown component is dropped and its content kept as text, so nothing the model said is lost.
- Names models use on their own (
<Table>,<Column>,<Buttons>) are read as what they mean.
Strict where it isn't
Each problem is a diagnostic on its line, with a class:
| Class | What it means |
|---|---|
hallucinated-component |
a component that isn't in your catalog |
signature-mismatch |
an attribute the component doesn't have, or of the wrong kind |
required-field |
a required attribute left out |
enum-mismatch |
a value outside an attribute's list, with no default to fall back on |
malformed-syntax |
a tag that doesn't close, or closes the wrong thing |
unreadable-tag |
a line that opens like a component but isn't a tag the core can read |
unsafe-url |
an address that could run code, like javascript: |
A cut-off answer is reported as truncated, never passed off as whole.
Repair
When an answer has errors, the API sends the model the broken lines with what's wrong with each and asks for those lines again, with nothing else rewritten. Broken nesting, like a tab closed with its parent's tag, is mended with no model at all.
- It's on by default.
"repair": falsein the options turns it off. - A blank answer gets one fresh try.
- A repair runs on the same model and key as the answer, and counts as part of the same call.
- It stops after 30 seconds and keeps what it has.
What you see
Through /v1/ui (what connectAgent reads), a repaired answer replaces the one that streamed, and a morphscreen.repair event says what changed. You don't need to do anything.
Through /v1/chat/completions, the last chunk's morphscreen field carries the core's verdict, and when a repair was needed, the repaired text:
{
"morphscreen": {
"valid": true,
"truncated": false,
"components": 12,
"diagnostics": [],
"repair": { "repaired": true, "errors_before": 2, "rounds": 1, "nesting": false, "ms": 1840, "lines": [7, [12, 14]], "text": "...", "valid": true }
}
}Draw repair.text in place of what streamed. Without streaming, the answer you get is already the repaired one.
On the dashboard
Every call's line shows whether its answer was valid, which components had errors, and what repair did: the dashboard.