2 Commits

Author SHA1 Message Date
teknium1
5e17f210c1 fix(aux): scope the response_format rejection memo to the model and to capability errors
The process-level memo of routes that rejected a response_format type was
keyed by (endpoint host, type) only. On an aggregator host (openrouter.ai,
the Nous Portal, api.openai.com) one chat-only model's json_schema 400 then
stripped the field from every other model on that host for the rest of the
process, silently degrading schema enforcement for models that support it.
The memo was also fed by any error _is_structured_output_rejection matched,
including the by-shape rule (response_format + json_schema in the body),
which also matches schema-VALIDATION 400s from json_schema-capable providers
("Invalid schema for response_format 'json_schema': additionalProperties
must be false") - those say nothing about the next schema.

- key the memo by (endpoint, model, type); _build_call_kwargs passes the
  model to the lookup and every remember_* caller (both fallback candidate
  paths and the primary ladder rung) now hands over the rejecting error
- memoise only capability rejections (unavailable / not supported / unknown
  or unsupported parameter / extra inputs not permitted); the by-shape match
  still drives the one-shot retry but never feeds the memo
- a custom route whose base_url host is api.deepseek.com consults the
  DeepSeek profile (via the existing base_url->provider inference), so the
  profile gate also fires when the provider name was overridden to custom
- test module docstring: only title_generator and plugin_llm attach
  response_format on aux calls
2026-09-17 09:11:38 -07:00
teknium1
df1074b4e5 fix(aux): structured-output rejection no longer kills fallback candidates or costs a doomed first request
Two open atoms of #83390 (DeepSeek "This response_format type is unavailable now"):

* `_call_fallback_candidate_sync/_async` only special-cased auth errors, so when the primary
  aux provider failed (timeout, rate limit, payment) and the fallback landed on a provider that
  rejects `json_schema`, the 400 re-raised and the whole task died — the primary-path rung from
  #89589 never applied there. Both fallback paths now retry once without `response_format`.
* Every structured aux call (titles, kanban decomposer, goal judge, plugin structured calls)
  paid a guaranteed-fail request on providers that lack `json_schema` before the retry. A
  provider profile can now declare `unsupported_response_formats` (DeepSeek: json_schema, per
  https://api-docs.deepseek.com/guides/json_mode) and the recovery ladder remembers any route
  that rejected a type once (host:port scoped), so `_build_call_kwargs` — shared by the primary
  and fallback paths — omits the field before the first request. Dropping rather than
  downgrading to json_object matches the end state the retry already produced; json_object
  needs a JSON-mentioning prompt and some relays return empty content under it.

New logic lives in agent/auxiliary_structured_output.py; the facade only gains the fallback rung
next to the predicate it uses. tests/agent/conftest.py resets the process-level memo per test.

Fixes #83390, #105191. Closes duplicates #84976, #88830, #102849, #113064.
Co-authored-by: Legion-is-life <Legion-is-life@users.noreply.github.com>
2026-09-17 09:11:38 -07:00