# 供应商选择

https://modelportal.ai/docs/provider-routing?lang=zh

用 `provider` 对象控制单次请求由哪家供应商、哪个报价承接。字段与 OpenRouter 的 `provider` 对象兼容，迁移时大多可以原样保留，差异见文末。

## 怎么传

- OpenAI 协议：写在请求体的 `provider` 字段里。
- 三种协议通用：请求头 `X-ModelPortal-Provider-Preferences`，值是 JSON 对象，字段与 `provider` 相同，最长 4096 字节。Anthropic 与 Gemini 的官方 SDK 不方便往请求体里加字段，用这个头最省事。
- 请求体和请求头同时带了偏好时返回 400 `invalid_request`，平台不做合并。
- `provider` 只给平台看，转发给供应商之前会删掉，请求头也不会转发。

```bash
curl https://modelportal.ai/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "messages": [{"role": "user", "content": "你好"}],
    "provider": {"order": ["PROVIDER_ID"], "data_collection": "deny"}
  }'
```

```bash
curl https://modelportal.ai/v1/messages \
  -H "x-api-key: YOUR_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -H 'X-ModelPortal-Provider-Preferences: {"only": ["PROVIDER_ID"]}' \
  -d '{"model": "MODEL_ID", "max_tokens": 1024, "messages": [{"role": "user", "content": "你好"}]}'
```

## 字段

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `order` | 字符串数组 | 按顺序优先尝试的供应商标识或报价标识；列表外的报价按默认规则排在后面。写供应商标识时，它的多个报价之间仍按默认规则排序 |
| `only` | 字符串数组 | 只允许这些供应商或报价 |
| `ignore` | 字符串数组 | 排除这些供应商或报价 |
| `allow_fallbacks` | 布尔 | 是否允许在同一模型的报价之间切换，默认 `false`，见[失败切换](/docs/fallbacks) |
| `sort` | 字符串 | `price`（比价价格升序）、`latency`（近 1 小时首字延迟中位数升序）、`throughput`（近 1 小时吞吐中位数降序） |
| `data_collection` | 字符串 | `allow`（默认）或 `deny`，见[数据政策](/docs/data-policy) |
| `quantizations` | 字符串数组 | 允许的量化精度：`int4`、`int8`、`fp4`、`fp6`、`fp8`、`fp16`、`bf16`、`fp32`、`unknown`（未披露） |
| `max_price` | 对象 | 单价上限，子字段 `prompt`、`completion`（USD / 百万 tokens）、`request`（USD / 次）、`image`（USD / 张），超过的报价被排除 |
| `data_regions` | 字符串数组 | 只用数据处理地区在列表里的报价，填两位大写国家或地区代码，如 `US`、`SG` |
| `exclude_regions` | 字符串数组 | 排除数据处理地区在列表里的报价 |
| `require_parameters` | 布尔 | 只接受 `true`。平台始终要求报价支持请求里的全部参数 |

值为 `null` 的字段按没填处理。账户和密钥也可以保存默认偏好（账户的在控制台「路由偏好」页设置，密钥页标题栏有入口），多处同时设置时按「请求 > 密钥 > 账户」逐项覆盖；限制类设置只会越叠越严：`ignore`、`exclude_regions` 取并集，`data_regions` 取交集，任何一处设了 `data_collection: deny` 就生效。

## 按报价标识锁定

`order`、`only`、`ignore` 里既能写供应商标识，也能写以 `route_` 开头的报价标识。要让请求只由某一个报价承接，在 `only` 里只写它：

```json
{"provider": {"only": ["route_xxxxxxxx"]}}
```

报价标识在模型详情页的报价列表和 `GET /api/v1/models/<模型 ID>` 返回的 `offerings[].offering_id` 里都能查到。被锁定的报价暂停、维护或不满足其他约束时返回 422 `no_eligible_endpoint`，`excluded` 里写明原因代码，见[常见错误](/docs/errors)。

## 校验

字段在路由之前校验，下面几种情况返回 400 `invalid_request`，错误信息指出 `provider.<字段>` 和原因，平台不会向任何供应商发出请求：

- 未知字段，或 OpenRouter 有、本平台没有实现的字段，例如 `zdr`（可以改用 `data_collection: deny`）。
- `require_parameters: false`。
- `order`、`only`、`ignore` 里有平台上不存在的供应商标识或报价标识。
- 类型或取值不合法，例如 `only` 写成了字符串。

## 与 OpenRouter 的差异

1. `allow_fallbacks` 默认是 `false`：首选报价失败时直接返回错误，不会自动换下一个。
2. 模型参数不支持就报错：请求里的参数没有任何候选报价支持时返回 400 `unsupported_parameter`，不会悄悄丢掉参数再转发。
3. `provider` 里的未知字段、未实现字段和不存在的标识都直接返回 400，不会被静默忽略。
