fix(intern-decision-serve): 0.1.2 treats empty/null images as absent
Audit finding on 0.1.1: a Jev client that always sends an images array with no images was rejected for nothing. Only a non-empty value is 422 now. Also aligns the contract's response example with the wire (model is the name@revision string, not an object). Re-accepted live: images []/null -> 200, ["a.png"] -> 422; JevBench all 202/231, hard 83/111, 0 changed rows across the bench's r1..r4 (924).
This commit is contained in:
@@ -32,7 +32,7 @@ Every POST takes and returns JSON and needs `Authorization: Bearer <token>`. `GE
|
||||
| GET | `/health` | none | 200 `{status: "ok", model, vram_cap_gib, max_tokens, max_decisions, max_questions_per_call: 16, chunking, workloads: []}` |
|
||||
| POST | `/decide` | `{id, state, question, options[2..16], orderings?, workload?}` | 200 one decision result |
|
||||
| POST | `/decide/shared` | `{state, decisions: [{id, question, options, orderings?}], workload?}` | 200 `{results: [...], timing: {...}}`, results in request order |
|
||||
| POST | `/v1/systemone` | `{state, model?, questions: {"<id>": {type, instructions, criteria?}}, images? (rejected)}` | 200 the engine's own response (`answers`, `usage`, `model`) — see § POST /v1/systemone |
|
||||
| POST | `/v1/systemone` | `{state, model?, questions: {"<id>": {type, instructions, criteria?}}, images? (422 iff non-empty)}` | 200 the engine's own response (`answers`, `usage`, `model`) — see § POST /v1/systemone |
|
||||
|
||||
`options` items are `{id, description}`. Validation is SemIf's, re-stated here because SemIf is
|
||||
gone. `id` and `question` are nonempty strings. `state` is a nonempty string, object or array,
|
||||
@@ -50,11 +50,11 @@ the answers.
|
||||
```
|
||||
request {"state": ..., "model": "<ignored>",
|
||||
"questions": {"<id>": {"type": "noul"|"choice"|"score", "instructions": ..., "criteria": ...}},
|
||||
"images": <rejected if present>}
|
||||
"images": <422 iff a non-empty value; [] and null count as absent>}
|
||||
response {"answers": {"<id>": {"type": "noul", "noul": p, ...}
|
||||
| {"type": "choice", "choice": label, "probabilities": {...}, ...}
|
||||
| {"type": "score", "score": v, "probabilities": {...}, ...}},
|
||||
"usage": {...}, "model": {"name": ..., "revision": ...}}
|
||||
"usage": {...}, "model": "<name>@<revision>"}
|
||||
```
|
||||
|
||||
- `model` is accepted with **any** value (Jev clients send `"jev-latest"`) and ignored. The
|
||||
@@ -67,9 +67,11 @@ response {"answers": {"<id>": {"type": "noul", "noul": p, ...}
|
||||
`calibration`, `backend`) ride along — additive fields Jev clients ignore.
|
||||
- **Limits, all enforced before the forward pass:** 1..16 questions per request (NO chunking —
|
||||
in Jev semantics the questions of one call share one prompt and chunking would silently
|
||||
change that); 422 with a clear message above 16. `images` present → 422 `images not
|
||||
supported` (the vision tower is dropped, INV-7). Over `MAX_TOKENS` → 422, the model's own
|
||||
pre-forward check. The same bearer token (401), the same single inference thread, the same
|
||||
change that); 422 with a clear message above 16. A non-empty `images` → 422 `images not
|
||||
supported` (the vision tower is dropped, INV-7); `[]` and `null` count as absent — a client
|
||||
that always sends the field must not be rejected for nothing (audit finding 2026-09-30).
|
||||
Over `MAX_TOKENS` → 422, the model's own pre-forward check. The same bearer token (401),
|
||||
the same single inference thread, the same
|
||||
`MAX_QUEUE` (429), `MAX_BODY_BYTES` (413), VRAM cap with 503 `out_of_memory` + recovery.
|
||||
- Per-question shape validation (type must be noul/choice/score; choice criteria an object;
|
||||
score criteria a list/object; 1..16 options; nonempty state; unique field names) is the
|
||||
|
||||
Reference in New Issue
Block a user