Parser:
- Never return reasoning text as the reply: the no-output_text path
anchors strictly on the message "content" array; a reasoning-only body
(truncated by max_output_tokens, carries only "summary") now fails the
parse instead of leaking the chain-of-thought. Regression test added.
- _find_json_field skips field-name occurrences that are string values
(no ':' after), so {"type":"text","text":"hi"} parses instead of
failing on the value of "type".
- _same_json_object tracks brace depth, so a nested object between the
"text" key and the "output_text" tag (e.g. "annotations") no longer
rejects the part's own text.
- Chat-completions extraction is bounded to the choices array (new
_json_array_end helper), so "message"/"content" in a sibling object
(e.g. a top-level "warning") is not returned as the reply.
AUTO fallback:
- Model errors are classified only from a structured JSON error
envelope; a route-missing 404 page merely mentioning models (e.g.
listing /v1/models) no longer suppresses the fallback. Trade-off: a
model error in a non-JSON body costs one extra fallback attempt.
- Fall back on a curl-level failure: an endpoint that accepts the
connection but never responds no longer burns the 60s timeout without
trying the flavour that works.
- Fall back on an unparseable 2xx: a gateway answering unknown paths
with a 200 error page previously reset the hint and re-probed the same
dead flavour on every request. The body is parsed inside the attempt
loop and no longer re-parsed after it.
- The second attempt's 401/403/429/5xx now wins over the stashed 400/422
payload rejection, so an invalid key or rate limit is not hidden
behind a payload-shape error; a second 404 still defers to the first.
- When the first flavour's endpoint is missing but the other answers
(even unsuccessfully, e.g. 401), the resolved hint moves to the
surviving flavour, so the dead endpoint is not probed first forever.
Settings:
- Reserved payload keys are rejected only when setting a value; removal
is allowed again, so a setting persisted before its key became
reserved (e.g. "input") is no longer stuck in the config.