Model Routes & Capabilities

“Does this model support tool calls?” sounds like a question about the model. It is not. It is a question about the model and the endpoint you call it on — and the same model can answer yes on one and no on the other. This page explains why, and how Entity Enricher settles the question by measuring it instead of believing a catalogue.

A model that supports a feature and refuses it

Send OpenAI's gpt-6 family a request with function tools on the classic /v1/chat/completions endpoint and the request is rejected outright:

400 Bad Request
Function tools with reasoning_effort are not supported.
Use /v1/responses or set reasoning_effort to 'none'.

Send the same model, the same tools and the same prompt to /v1/responses and it answers normally — on both structured-output channels, with or without a reasoning effort. Nothing about the model changed. Only the address did.

The consequence

A capability is not a property of a model alone. It is a property of a (model, endpoint) pair. Every catalogue that stores one true/false per model is therefore answering a question it cannot actually see.

What a route is

A route is simply the endpoint a request is sent to. Same provider, same model, same API key — but a different wire format, a different request shape and a different set of rules about what may be combined. OpenAI serves two of them, and most of the industry copied the older one.

RouteShape of the exchangeWho speaks it
Chat Completions
/v1/chat/completions
2023
Stateless: you send the whole list of messages, you get the next message back. Nothing is kept on the server between calls.OpenAI, and the de-facto industry standard copied by almost everyone else — xAI, DeepSeek, Alibaba, Groq, Together, Ollama and most OpenAI-compatible gateways.
Responses API
/v1/responses
2025
Returns a list of typed items — a reasoning item, a message, a function-tool call, a hosted-tool call — keeps state server-side, and can carry reasoning from one turn into the next.OpenAI only. A look-alike endpoint that speaks the Chat Completions shape does not automatically serve this one.

This is why “the OpenAI API” is ambiguous today: there are two, they overlap, and they do not enforce the same rules.

Why there are two APIs at all

Reasoning models broke the message model. A model's internal reasoning is not a message: it is not something it said to you, and it is not something you said to it. Chat Completions has only a list of messages, so it has nowhere to put reasoning — and what it cannot represent, it drops between turns.

The Responses API was designed around that gap. Reasoning is a first-class item in the returned list; it can come back encrypted and be replayed on the next turn, so the model continues from its own thinking instead of reconstructing it.

Which explains the refusal above

A function-tool call is a round trip: the model asks for a tool, your code answers, the model continues. On Chat Completions there is nowhere to keep the reasoning across that round trip, so the continuation would silently resume without it — the model would have to redo the thinking you already paid for, and would likely redo it differently.

So the provider refuses the combination instead of degrading quietly. The error message is not a limitation of the model; it is the older API admitting it has no slot for what this model produces.

Capabilities point in different directions

If every capability preferred the same endpoint, a route would be an implementation detail nobody had to think about. They do not.

Tools and web search want the Responses API

Structured output through the tool channel is exactly the combination Chat Completions refuses on a reasoning model, and the provider-hosted web-search tool is only accepted there.

Binary audio only works on Chat Completions

Attaching raw audio bytes is not something the Responses API path can carry, so an audio-capable model is reachable only through the older endpoint.

One model, two capabilities, two different endpoints. There is no single true/false that is honest about both — which is why Entity Enricher records a capability map per route, and why a run that needs two capabilities living on two different routes is refused by name rather than quietly losing one of them.

How Entity Enricher measures a route

The capability probe does not send a synthetic hand-written request. It drives the real agent path — the same code an enrichment run takes — with the capability under test forced on, and lets the provider's answer decide. What is measured is therefore what the application can actually do, not what the API could theoretically accept.

The whole ladder — structured-output channels, vision, PDF, audio, web search, reasoning and its per-request effort control — runs once per route the router can choose. For almost every provider that is a single route, and the probe costs exactly what it always did; only the OpenAI-shaped providers, which really can be reached two ways, are measured twice.

The request was accepted and the model produced the expected kind of answer.

Recorded as a measured true for that route — and it outranks every scraped claim.

No

The provider explicitly rejected the capability, or the client refused to build the request at all.

The only outcome allowed to write a false. A rejection is also free: it is never billed.

Sconosciuto

Anything else: a rate limit, a missing access tier, a timeout, a provider 5xx, an unrecognised error.

Nothing is written, and the next probe can settle it.

Why the default is “unknown” and never “no”

The two mistakes do not cost the same. A wrong unknown costs one re-probe: the capability is simply measured again later. A wrong no is silent and permanent — it downgrades the model everywhere, removing it from pickers and from the routing decision, and nobody ever finds out why. So an unrecognised failure proves nothing, and is recorded as proving nothing.

Accepted is not the same as understood

An HTTP 200 proves the request was accepted. It does not prove the model actually read the attachment — a provider can happily accept an image and hand it to a model that never looks at it. So every probe that carries a payload carries one whose answer can be checked:

FunzionalitàPayload sentQuestion asked
VisioneA solid red image“What colour dominates it?”
Input PDFA PDF containing exactly one word“Which word is in the document?”
Input audioAn audio file with a countable number of tones“How many separate beeps?”

Two separate results, deliberately

The content answer is recorded beside the verdict and never flips the capability flag. A small model that misreads a red square still supports vision — the transport worked, the modality was carried, the model was simply weak. Conflating “cannot” with “did badly” would quietly delete a working capability over one wrong word; keeping them apart tells you both things at once.

Claims versus measurements

Public model catalogues such as LiteLLM or PricePerToken state capabilities as a flat fact about a model, with no endpoint attached. That is not sloppiness — there is no field for it. But it means a claim can be perfectly right in general and still wrong for the endpoint your run uses.

A scraped claim

“This model supports tool calls.” True somewhere. Silent about where. Useful as a starting point for a model nobody has probed yet.

A measurement

“On this endpoint, this model accepted tool calls at this moment.” It names its route, so it can be compared with the route a run will actually take.

Naming the route is also what keeps a measurement honest over time. A verdict taken on an endpoint the model no longer uses is not better evidence than a claim — it is evidence about something else. Without the route recorded alongside it, an old measurement would be indistinguishable from a current one and would outrank the catalogue for the wrong endpoint. With the route recorded, it simply does not apply, and the claim stands until a measurement on the right endpoint replaces it.

Reading it in the product

Both the model catalogue inside the app and the public model catalogue carry a Route selector next to the filters. It changes which route the capability icons on each row are answering for.

Served route (the default)

Every row answers for the endpoint that model is actually served on. This is the view that matches what you get when you run an enrichment, and it is the one to read unless you are investigating a specific endpoint.

A named route

Pin the whole table to one endpoint and the icons answer for that endpoint only — which is how you see a capability appear or disappear as you switch between them, and how you compare two models on equal terms.

A model with only one reachable route shows no selector at all: there is nothing to choose, and its icons already answer for the only endpoint it has. The selector appears exactly when the ambiguity is real.

What this means for you

Nothing, most of the time — and that is the point. You never pick a route. You pick a model and describe your run, and the router chooses the endpoint that can carry what the run needs: tools and web search go where they are accepted, an audio attachment goes where audio is carried.

The one case you will notice is a genuine conflict — asking for web search and attaching an audio file to the same OpenAI model, for example, since no single endpoint serves both. That run is refused explicitly, naming the two capabilities and what each route can carry, rather than running and silently dropping one of them. Drop one requirement, or pick a model that serves both, and it proceeds.

And when a capability icon surprises you, the Route selector is the explanation: you are almost always looking at a model whose answer depends on where the question was asked.