Router
A router is a model name that chooses the model. Send hips-router-chat, hips-router-agent, hips-router-code or hips-router-research in model on chat completions, Responses or Messages, streamed or not, with your own tools, and HIPS chooses a model for each request among the models that can do that work, the fastest or the cheapest as you set it. The request is served and billed as the model chosen, and the answer names it.
Note
The Router reads each request in production: where two models or more can serve it, a decision service judges its task and difficulty first. When the read fails, gets no answer within its deadline, or the day's reads are used up (HIPS's whole day, or the workspace's share of it), the request is chosen by rules alone and served all the same, and x-hips-router says why in reason.
| Name | Use case | The models it can choose |
|---|---|---|
hips-router-chat | Chat: conversation, questions and answers, customer support and writing | Every published, priced language model |
hips-router-agent | Agent: requests that act through tools | Models whose tool calls are validated |
hips-router-code | Coding: coding agents and code work | Models that passed a coding agent's test, with their tool calls validated |
hips-router-research | Research: long context and reasoning | Models that advertise reasoning |
- Name
hips-router-chat- Use case
- Chat: conversation, questions and answers, customer support and writing
- The models it can choose
- Every published, priced language model
- Name
hips-router-agent- Use case
- Agent: requests that act through tools
- The models it can choose
- Models whose tool calls are validated
- Name
hips-router-code- Use case
- Coding: coding agents and code work
- The models it can choose
- Models that passed a coding agent's test, with their tool calls validated
- Name
hips-router-research- Use case
- Research: long context and reasoning
- The models it can choose
- Models that advertise
reasoning
A model joins and leaves a router by itself as it is imported, tested and priced; no list is kept by hand. A router chooses among catalog models only: never a workspace's own deployment (dep_...), never a draft, deprecated or retired model, and never a model the catalog offers only to buyers in certain countries. For the Coding router, a test with the coding agent of the route you call counts first: HIPS Code or OpenCode on chat completions, Claude Code on Messages, Codex on Responses. Where no model has passed that agent's test, a test with any of the four counts.
The request is the route's own, with a router's name in model. These samples read HIPS_API_KEY and HIPS_BASE_URL from the environment, as every sample on these pages does, and print the two headers that say what was chosen.
import osfrom openai import OpenAI client = OpenAI( api_key=os.environ["HIPS_API_KEY"], base_url=os.environ["HIPS_BASE_URL"], # https://ai.hips.com/v1) raw = client.chat.completions.with_raw_response.create( model="hips-router-code", # or hips-router-code:fast, hips-router-code:cheap messages=[{"role": "user", "content": "Write a Python function that checks whether a number is prime."}],)print(raw.headers.get("x-hips-model")) # the model chosenprint(raw.headers.get("x-hips-router")) # how it was chosenprint(raw.parse().choices[0].message.content)The same names work on POST /v1/responses and POST /v1/messages. For a coding agent, set the agent's model to the router's name, for example HIPS_MODEL=hips-router-code.
Each router has a preference for the workspace: Fastest sends each request to the model with the shortest measured time for its size, Cheapest to the lowest expected charge for its size at the rates its quote would bind. A router never set reads Cheapest. An owner or admin sets it on the Console's Router page; the change applies to the next requests and is recorded in the audit log. A suffix overrides the setting for one request:
| Model field | Preference | Source in the answer |
|---|---|---|
hips-router-code | The workspace's setting for Coding | setting |
hips-router-code:fast | Fastest, for this request alone | suffix |
hips-router-code:cheap | Cheapest, for this request alone | suffix |
- Model field
hips-router-code- Preference
- The workspace's setting for Coding
- Source in the answer
setting
- Model field
hips-router-code:fast- Preference
- Fastest, for this request alone
- Source in the answer
suffix
- Model field
hips-router-code:cheap- Preference
- Cheapest, for this request alone
- Source in the answer
suffix
- The measured time is the prompt's tokens over the measured prefill rate plus the expected output over the measured decode rate, on GPUs that hold the model loaded and free now. A model with no measured speed ranks after every model that has one, those among themselves by expected charge. Nothing is estimated from another model.
- The expected output is the request's own cap where it sets one, else the same length for every model, so a model with a long default is not made to look dear.
- A name and its suffix are lowercase, exactly as written. An empty or unknown suffix (
hips-router-code:,hips-router-code:best) is 400invalid_valueatmodel: "The suffix of a router is:fastor:cheap, as inhips-router-code:fast." - Any other name that starts with
hips-router, a router written in another case included, is 404model_not_found, and the message names the four. No catalog id, alias or pinned id starts withhips-router, so a router never collides with a model. - A router chooses language models on chat completions, Responses and Messages only. Named on
/v1/tasks,/v1/embeddingsor/v1/rerank, it is 400invalid_valueatmodel.
The Router makes no promise about which model serves a request. It chooses before the first byte of the answer, in this order:
- 01
The candidates. The router's models, narrowed to those that can serve this request in a pool it may use: an image part needs a model that takes images,
toolsone that supports tool calls,response_format,logprobs,logit_biasandreasoning_efforta model that advertises them (parallel_tool_callsnarrows nothing: it is a permission, served asfalseon a model without the feature), the prompt a window served now that holds it, and an output cap a model whose output limit reaches it. Then the requirements (x-hips-pool,x-hips-region,x-hips-pricing-mode), the pools the workspace may use and your guardrails. Then the supply now: a model no GPU can serve in any of those pools is out. - 02
The read. Where two models or more remain, the request may be read first: a decision service judges its task (
conversation,writing,extraction,reasoning,codeortool_use) and its difficulty (simple,moderateorhard) from a masked excerpt. A harder request is held to larger models, and the task ranks the models that fit it first: models that reason forreasoning, models tested with a coding agent forcode, validated tool calls fortool_use, JSON schema output forextraction. Without an answer the request is chosen by rules alone, among the same candidates with no size floor and no task preference. A request with a region requirement is never read. A read that fails, times out or finds the day's reads used up never fails the request: it is chosen by rules alone. - 03
The order. Models with a GPU ready now go before models that start on demand, since loading a model can take minutes. Within each, the preference's measure decides, ties going to the other measure, then to the catalog id, so every gateway chooses alike. Where the read asked for a larger model and none is ready now, a smaller one that is ready goes first, and the answer says
floor=below. - 04
The conversation. A conversation's next turn goes to the model its earlier turn was answered by, while that model is still a candidate, ready now and large enough for the new turn, so a coding agent's session stays on one model.
- 05
The tries. The first model is tried as if the request had named it. If it is refused before its first byte for capacity (
no_capacity,pool_unavailable,overloaded,context_size_not_measured), the next one is tried, up to three models in all, within the request's own deadline. After the first byte the request is that model's: a stream that fails is never restarted on another model.
Everything about the answer is the chosen model's: model in the body names it, never the router, x-hips-model carries its pinned id, and usage its counts in the route's own names. x-hips-router says how it was chosen:
Response headers
HTTP/2 200content-type: application/jsonx-hips-request-id: req_...x-hips-model: <pinned id of the model chosen>x-hips-router: hips-router-code; preference=cheapest; source=setting; read=decision; task=code; difficulty=hardParts list: x-hips-router, its members in order
<router>The router's name, without its suffix.
preferencefastest,cheapestThe preference this request was chosen by.
sourcesetting,suffixThe workspace's setting, or the request's suffix.
readdecision,cache,rulesRead first; read first, from an earlier reading of the same masked text; or chosen by rules alone.
reasona word belowWhy rules alone. Only with
read=rules.taskconversation,writing,extraction,reasoning,code,tool_useOnly when the request was read.
difficultysimple,moderate,hardOnly when the request was read.
floorbelowThe model chosen is smaller than the difficulty asked, since nothing larger was ready.
triesa countOnly when more than one model was tried before one was placed.
| Reason | Why the request was chosen by rules alone |
|---|---|
no_key | The gateway has no client for the decision service, so it cannot read |
one_candidate | Only one model could serve it, so there was nothing to read for |
same_choice | Every answer the read could give would try the same models in the same order, so none was asked |
region | The request carries a region requirement, and such a request is never read |
timeout | No answer in time |
error | The read failed, or the masking could not finish within its bound and nothing was sent |
refused | The decision service refused the read |
budget | The day's reads are used up, HIPS's or the workspace's share |
breaker | Reads are paused after failures |
saturated | Too many reads at once |
switched_off | HIPS switched reading off |
- Reason
no_key- Why the request was chosen by rules alone
- The gateway has no client for the decision service, so it cannot read
- Reason
one_candidate- Why the request was chosen by rules alone
- Only one model could serve it, so there was nothing to read for
- Reason
same_choice- Why the request was chosen by rules alone
- Every answer the read could give would try the same models in the same order, so none was asked
- Reason
region- Why the request was chosen by rules alone
- The request carries a region requirement, and such a request is never read
- Reason
timeout- Why the request was chosen by rules alone
- No answer in time
- Reason
error- Why the request was chosen by rules alone
- The read failed, or the masking could not finish within its bound and nothing was sent
- Reason
refused- Why the request was chosen by rules alone
- The decision service refused the read
- Reason
budget- Why the request was chosen by rules alone
- The day's reads are used up, HIPS's or the workspace's share
- Reason
breaker- Why the request was chosen by rules alone
- Reads are paused after failures
- Reason
saturated- Why the request was chosen by rules alone
- Too many reads at once
- Reason
switched_off- Why the request was chosen by rules alone
- HIPS switched reading off
A request the read could not answer in time is chosen by rules alone, and the header says so:
A request chosen by rules alone
x-hips-router: hips-router-code; preference=cheapest; source=setting; read=rules; reason=timeoutx-hips-router is on every answer to a request that named a router, a refusal after the name was read included: a refusal found among the candidates says read=rules with no reason, and a content moderation block, which comes before any choice, carries the name, preference and source alone. The answer's hips object carries the same as router, with model, the pinned id served, and each member only where it is known:
The hips object of a chat completion
"hips": { "cost_micros": 0, "latency_ms": 0, "quote": "quote_...", "router": { "name": "hips-router-code", "preference": "cheapest", "source": "setting", "read": "decision", "task": "code", "difficulty": "hard", "model": "<pinned id of the model chosen>" }}| Route | Where the router member is |
|---|---|
| Chat completions | The answer's top-level hips object; on a stream, the hips object of the final usage chunk (stream_options.include_usage) |
| Messages | hips on the message; on a stream, in message_delta |
| Responses | On a stream, the hips member of response.completed; a whole answer carries the headers alone |
- Route
- Chat completions
- Where the router member is
- The answer's top-level
hipsobject; on a stream, thehipsobject of the final usage chunk (stream_options.include_usage)
- Route
- Messages
- Where the router member is
hipson the message; on a stream, inmessage_delta
- Route
- Responses
- Where the router member is
- On a stream, the
hipsmember ofresponse.completed; a whole answer carries the headers alone
The choice is made before the first byte, so x-hips-model and x-hips-router are in a stream's head, and a refusal is an ordinary status and body, never an error inside the stream.
A routed request is billed exactly as the model chosen: its quote (x-hips-quote), its rates in the pool that served it, its usage units, and x-hips-cost-micros or the hips object's cost_micros. There is no router fee, and reading the request is never on your usage. The usage record keeps the model chosen and, beside it, the router's name, the preference, its source, how it chose and why rules, never the task or the difficulty. Rate limits count a routed request as one request with its own tokens.
Every code of the Errors page applies to a routed request as to any. The Router's own answers:
| Case | Answer | Param | On Messages |
|---|---|---|---|
| The router has no model for its use case now | 400 router_no_candidate | model | invalid_request_error |
What the request carries leaves none: an image, tools, a response_format, an output cap | 400 router_no_candidate | The first field at fault, in the chat completions names (messages[].content[].image_url, tools, response_format.type, max_tokens) | invalid_request_error |
| The requirements leave none: a pool, a region, stable pricing, or the pools the workspace may use | 400 router_no_candidate | x-hips-pool, x-hips-region, x-hips-pricing-mode, or model | invalid_request_error |
| Your guardrails allow none of its models, or keep the router itself out | 403 model_not_allowed, naming the guardrail | none | permission_error |
| The prompt is longer than every candidate's window served now | 400 context_length_exceeded, naming the largest window | messages | invalid_request_error |
| Candidates exist and none has a GPU that can serve it now, or every model tried was refused for capacity | 503 no_capacity with Retry-After | none | overloaded_error |
| The balance or a budget holds none | 402 insufficient_credit or budget_exceeded | none | billing_error |
- Case
- The router has no model for its use case now
- Answer
- 400
router_no_candidate - Param
model- On Messages
invalid_request_error
- Case
- What the request carries leaves none: an image,
tools, aresponse_format, an output cap - Answer
- 400
router_no_candidate - Param
- The first field at fault, in the chat completions names (
messages[].content[].image_url,tools,response_format.type,max_tokens) - On Messages
invalid_request_error
- Case
- The requirements leave none: a pool, a region, stable pricing, or the pools the workspace may use
- Answer
- 400
router_no_candidate - Param
x-hips-pool,x-hips-region,x-hips-pricing-mode, ormodel- On Messages
invalid_request_error
- Case
- Your guardrails allow none of its models, or keep the router itself out
- Answer
- 403
model_not_allowed, naming the guardrail - Param
- none
- On Messages
permission_error
- Case
- The prompt is longer than every candidate's window served now
- Answer
- 400
context_length_exceeded, naming the largest window - Param
messages- On Messages
invalid_request_error
- Case
- Candidates exist and none has a GPU that can serve it now, or every model tried was refused for capacity
- Answer
- 503
no_capacitywithRetry-After - Param
- none
- On Messages
overloaded_error
- Case
- The balance or a budget holds none
- Answer
- 402
insufficient_creditorbudget_exceeded - Param
- none
- On Messages
billing_error
router_no_candidate is never worth sending again unchanged: its message says why and what would serve. For example, while no model has passed a coding agent's test:
HTTP/2 400x-hips-router: hips-router-code; preference=cheapest; source=setting; read=rulesGET /v1/models lists the four routers first, each with its use case, the workspace's preference, its suffixes, how many models it can choose for your key's default requirements and guardrails (candidates), and how many of those have a GPU ready now (ready, null while the capacity reading is stale). GET /v1/models/{id} answers a router's name with or without its suffix. The Models reference has the rest of the list.
One router in GET /v1/models
{ "id": "hips-router-code", "object": "model", "created": 0, "owned_by": "hips", "hugging_face_id": null, "hips": { "task": "chat", "router": { "use_case": "Coding", "preference": "cheapest", "suffixes": ["fast", "cheap"], "candidates": 0, "ready": 0 } }}- The requirement headers apply to every model a router may choose and narrow its candidates; none is weakened to find a model. Under Auto the pool is chosen for the model chosen exactly as if you had named it, and
x-hips-poolnames the pool that served. - A model and pool access guardrail narrows the candidates to the models it allows, each judged as a request naming it would be. A router itself is kept out only where a guardrail names it: a deny list naming it, or a family such as
hips-router-*, or an allow list that names another router and not this one. - An allow list that names a router must also name the models it may choose:
hips-router-codewithlanguagelets the Coding router choose among every language model, whilehips-router-codealone allows no model, 403model_not_allowed. - Content moderation runs before any read or choice: a guardrail that blocks refuses the request first. A budget holds the chosen model's maximum charge, as for any request. Guardrails has the rules.
A batch runs one model, so a batch file whose lines name a router is refused at its check, 400 invalid_file_format at body.model on the first such line: "A router chooses a model for each request; a batch runs one model. Name a model in the file's lines." Batches are not offered in production yet.
- Router, in the Inference menu after Guardrails: one panel per use case with the name to call and its Copy, an example API call, what the router is for, its preference (Fastest or Cheapest; an owner or admin changes it, and anyone else sees why the control is closed), what it would choose now for a request of a sample size at each difficulty and without a read, under the workspace's tool defaults or a chosen key's requirements, how many models it can choose and why the others are out, the suffix that overrides it, and that it is billed as the model it chooses. Above the panels, the last 24 hours: how many requests were routed, how many were read first, how many were chosen by rules alone, and why.
- Models lists the four routers in a group of their own above the models; each opens its panel on the Router page.
- The Playground's model picker shows the routers first, with a preference control. Each answer says which model was chosen and how, and its Details show the router, the preference and where it came from, the read and the tries.
- Usage names a routed request "hips-router-code, served by <model>" and filters by router.
The read receives a masked excerpt of the request, at most 4,096 bytes: the newest user message's text, then the system text, with every email address, phone number, Social Security number, card number, IBAN, IP address and secret replaced by a typed placeholder, and the custom patterns of your content moderation guardrails masked too. Images are never read. The excerpt goes to a decision service run outside HIPS for the one call, and is never stored, logged or sent to a model's server; a masking that cannot finish within its bound sends nothing. A verdict and a conversation's model are kept under keyed hashes for a bounded time, never as text.
Related sheets
- Sheet
- 14 of 19
- Updated
- 2026-11-12