ADR-275: A model record carries its reasoning profile
Context
The OpenAI adapter decides three things per model from a table in code,
Open: whether the model is a reasoning model (then
sampling parameters such as temperature are not sent), which
reasoning_ values it accepts, and whether its tool calls go
through Chat Completions or Responses (ADR-203). A model
the table does not name needs a release before it works.
On 2026-11-09 gpt- was such a model. It was listed by
GET / and missing from the table, so a configured temperature
and every tool call failed with HTTP 400. The same measurement showed the
table itself wrong: OpenAI refuses max for every GPT-6 model, although
the model pages list it (the receipt is in
specs/).
No source can replace the table completely:
- OpenAI's model object carries
id,object,created,owned_andby shutdown_. It states no reasoning, effort or tool capability.date - OpenRouter's public
GET /states per model aapi/ v1/ models reasoningobject (mandatory,supported_) andefforts supported_. For the five GPT-6 models it agrees with OpenAI's own answers on reasoning, on a non-defaultparameters temperaturebeing unsupported, and on whethernoneis accepted (mandatory: falseexactly wherenoneworks). It disagrees on the scale: it listsmaxfor all five, which OpenAI refuses. It also liststoolsforgpt-, which direct Chat Completions refuses: OpenRouter describes its own endpoint, not OpenAI's routes.6. 1- terra - symfony/ai (
mainat1fc422e47d, 2026-11-06) keeps a hand-written OpenAI catalogue of about 70 models, throwsModelfor any other name and accepts additional models through its constructor;Not Found Exception gpt-is missing there too. Its OpenRouter bridge reads the live model list instead.6. 1- terra
Decision
1. The profile is resolved per model record, in this order
- Operator override on the model record: reasoning yes/no, an effort scale, a tool route. Each field is optional; an empty field defers to the next source.
- The bundled measured table (
Open) for an exact family name or a dated snapshot of it.Ai Model Profiles - The discovered profile stored on the record by the last discovery run, with its source (next section).
- The family default in code, such as the GPT-6 default: reasoning, tools through Responses, no effort scale.
- Unknown: not a reasoning model, legacy route, no effort.
The bundled table outranks discovered data because each of its rows was measured against the API; a third party's catalogue may describe its own endpoint instead. The operator outranks everything because the operator can measure their own deployment, which no table can.
2. The tool route is never discovered
Whether tools go through Chat Completions or Responses comes only from the
operator, the bundled table or the family default. Neither source above
states it for OpenAI's own endpoints; OpenRouter's tools flag describes
OpenRouter.
3. What each discovery source may set
- A provider's own model API, where it states reasoning data about
the endpoint being called: OpenRouter's
reasoningobject for a model configured on an OpenRouter provider. Reasoning yes/no and the effort scale are stored as OpenRouter states them, because they describe the endpoint the request goes to. SourceCapability.Source:: Discovery - OpenRouter's public catalogue as metadata for a direct OpenAI
model, matched by
openai/<model id>. Only reasoning yes/no is stored, never the scale, because the scale was measured to differ. SourceCapability, a new case: folding it intoSource:: Third Party Catalog Discoverywould claim the called provider confirmed it, which is the conflation ADR-160 exists to prevent. - No other discoverer stores a reasoning profile. Mistral, Gemini, Anthropic, Groq and Ollama listings state no reasoning flag the adapters consume.
The third-party lookup is off by default and switched on in the
extension configuration. It runs during model discovery only, never while
a request is being sent, and sends no credential and no content: it is one
anonymous GET of a public list. A failed lookup stores nothing and
leaves the record as it was.
4. Storage follows ADR-160's provenance pattern
The operator fields and the discovered fields are separate columns, so a discovery run never overwrites an operator decision and the backend can show which one applies. A missing discovered value is stored as empty, not as "no": "not measured" and "measured: not a reasoning model" stay different facts.
5. Consumers
Openresolves the profile through the record when the adapter was created from a model record (Ai Provider Provider), and through the table alone otherwise, as before.Adapter Registry:: create Adapter From Model () - The configuration form's parameter constraints use the same resolved profile, so a reasoning model is not offered a temperature.
- The model module shows the resolved profile and where each part came from.
Consequences
- A new OpenAI model works without a release once an operator sets its profile. The opt-in catalogue lookup supplies only the reasoning flag: for a model outside the known families it does not move tool calls to Responses and sets no effort scale, so a model that needs either still needs the operator to set the tool route or the scale.
- The bundled table stays the place for measured facts and still needs a change when OpenAI changes a model; the operator override covers the time until then.
Capabilitygains a case. Code that matches on it exhaustively must handle the new value.Source - OpenRouter's data can be wrong for OpenRouter too; it is shown with its source and date, and an operator override corrects it.
- The resolved profile applies only to adapter types served by
Open(OpenAI, Azure OpenAI, Together, Fireworks, Perplexity, custom). An OpenRouter model's discovered profile is shown on the record and applied nowhere: OpenRouter'sAi Provider reasoningmeans the model can produce reasoning tokens, not that it refuses sampling parameters, and its adapter sendstemperatureregardless. - An OpenRouter-configured model's stored effort scale is recorded, but the OpenRouter adapter does not send an effort yet; that is a separate change.