# Responses（OpenAI Responses 格式）

> /v1/responses：用新版 OpenAI SDK 的 responses.* 或 Codex CLI 接入，以及它和对话补全不一样的地方（无状态、两条走法、事件流）。

来源：https://www.scirouter.cn/docs/responses · 更新于 2026-10-06

```http
POST https://api.scirouter.cn/v1/responses
```

请求与响应是 OpenAI Responses API 的形状。新版 OpenAI SDK 的 `client.responses.*` 与 Codex CLI（`wire_api = "responses"`）这类按 Responses 格式发请求的客户端，换掉 base URL 和 Key 就能用；
平台上的对话模型都能这样调，不限于 OpenAI——背后线路不是原生支持 Responses 的，网关会替你转换（见下文「请求怎么到上游」）。

## 接入

OpenAI SDK 会在 base URL 后面拼 `/responses`，所以 base URL 就是 `https://api.scirouter.cn/v1`（带 `/v1`），与对话补全相同。Key 是 SciRouter 控制台新建的那一把（`sk-sr-` 开头，见[认证与 API Key](https://www.scirouter.cn/docs/authentication)），OpenAI 官方的 Key 在这里 401。

Python：

```python
import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["SCIROUTER_API_KEY"], base_url="https://api.scirouter.cn/v1")

r = client.responses.create(
    model="YOUR_MODEL_ID",
    instructions="你是一个严谨的科研助手。",
    input="这段序列的二级结构倾向？",
    max_output_tokens=1024,
)
print(r.output_text)
```

TypeScript：

```ts
import OpenAI from 'openai'

const client = new OpenAI({ apiKey: process.env.SCIROUTER_API_KEY, baseURL: 'https://api.scirouter.cn/v1' })

const r = await client.responses.create({
  model: 'YOUR_MODEL_ID',
  input: '这段序列的二级结构倾向？',
  max_output_tokens: 1024,
})
console.log(r.output_text)
```

curl：

```bash
curl https://api.scirouter.cn/v1/responses \
  -H "Authorization: Bearer $SCIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "YOUR_MODEL_ID",
    "input": "这段序列的二级结构倾向？",
    "max_output_tokens": 1024
  }'
```

## Codex CLI

在 `~/.codex/config.toml` 里加一个指向平台的 provider，并把默认模型换成平台上的模型 id：

```toml
model = "YOUR_MODEL_ID"
model_provider = "scirouter"

[model_providers.scirouter]
name = "SciRouter"
base_url = "https://api.scirouter.cn/v1"
env_key = "SCIROUTER_API_KEY"
wire_api = "responses"
```

然后 `export SCIROUTER_API_KEY=sk-sr-…`（SciRouter 控制台创建的 Key，不是 OpenAI 的）再启动 `codex`。Codex 每一轮都把完整对话（含它自己带回的推理项）放进 `input`、`store` 写 `false`，正好是平台的无状态口径（见下文）。
它的 `shell` / `apply_patch` 工具是函数工具，所有模型都能用；只有把线路标成「原生 Responses」的模型才能把加密的推理项续接回去，其它模型会丢掉推理项（不报错，见「请求怎么到上游」）。

## 请求

| 字段 | 必填 | 说明 |
|---|---|---|
| `model` | 是 | 平台的模型 id，也可以写后台给它配的别名；响应里的 `model` 是解析后的平台 id |
| `input` | 是 | 字符串，或输入项数组：`message`（`input_text` / `input_image` / `output_text` / `refusal`）、`function_call`、`function_call_output`、`reasoning`（客户端带回的，见下文）。不能为空 |
| `instructions` | 否 | 系统提示 |
| `max_output_tokens` | 否 | 正整数。超过模型或套餐的单次输出上限时按上限发出，并在响应头 `X-SciRouter-Max-Tokens-Clamped` 与 `scirouter.max_tokens_clamped` 里写明（不会悄悄截短）；不传时按模型的输出上限预扣额度 |
| `stream` | 否 | `true` 时流式，见下文 |
| `tools` / `tool_choice` / `parallel_tool_calls` | 否 | 函数工具（`{"type": "function", "name", "parameters", "strict"}`）照常用。厂商的服务端工具（`web_search`、`file_search`、`code_interpreter`、`computer_use`、`image_generation`、`mcp` 等）暂不支持：400 `validation_failed`，`details.reason` 为 `server_tool_unsupported` |
| `store` | 否 | **只认 `false`**：平台不保存响应。不传（SDK 默认不传、厂商默认 true）或写 `true` 都按 `false` 处理，响应里如实写 `"store": false`，透传给上游时也强制写成 `false` |
| `reasoning` / `text` / `temperature` / `top_p` | 否 | 按线路处理，见下一节 |
| `metadata` / `user` / `safety_identifier` | 否 | 用作平台的归因标签与终端用户标识（见[可观测性](https://www.scirouter.cn/docs/observability)），不转给上游 |

**不支持、会被拒绝的（400 `validation_failed`，`details.reason` 固定）**：`previous_response_id`、`conversation`、`prompt`、`input` 里的 `item_reference`（`stateful_unsupported`——平台无状态，请把完整的对话历史放进 `input`）；`background`（`background_unsupported`——请同步调用）。
`GET` / `DELETE` / `cancel` 这些按 id 操作已保存响应的接口不存在（404）。

请求体上限 32 MB（与 `/messages` 相同；Codex 每轮都带整段对话），超过 413 `payload_too_large`。

## 请求怎么到上游

- **模型背后是标了「原生 Responses」的线路**（后台在接口能力里勾选，只有 OpenAI / 自定义协议的线路能标）：请求原样转发到上游的 `/responses`，只改几处——`model` 换成上游的名字、去掉 `metadata` / `user` / `safety_identifier`、`store` 写 `false`、`max_output_tokens` 写生效值。
  `include`、`prompt_cache_key`、`text.verbosity`、`reasoning.summary`、客户端带回的带 `encrypted_content` 的推理项、厂商自定义字段都会到达上游；响应事件原样带回，只把 `model` 换回平台的 id，`id` 保留厂商给的（Codex 续接推理靠它）。
  「原样」说的是请求的格式与字段：鉴权、计费、限额与选线路照旧由平台做，发给厂商的凭据是平台在那家的，你的 SciRouter Key 不会到达厂商。
- **其他线路**（普通的 OpenAI 对话补全接口、Anthropic、Gemini 等协议）：网关把请求转成对话补全再调，结果再转回 Responses 的形状。
  `instructions` 成为 system 消息，`input` 里的消息、`function_call`、`function_call_output` 逐项转换（输出里的图片挪进紧随的用户消息），函数工具的 `strict` 保留，
  `max_output_tokens` → `max_tokens`，`reasoning.effort` → `reasoning_effort`，`text.format`（`text` / `json_object` / `json_schema`）→ `response_format`。
  **客户端带回的推理项（含 `encrypted_content`）与 `include: ["reasoning.encrypted_content"]` 会被丢掉、不报错**：它们只对签发的那家有意义，别家模型本来就拿不到；平台在请求日志的 `scirouter.converted_dropped` 里记下丢了什么。
  转回来的响应与原生的几处差别：`id` 是 `resp_<请求 id>`（与 `X-Request-Id` 对应），输出项的 id 形如 `msg_<请求 id>_0`、`rs_<请求 id>_0`、`fc_<call_id>`；别家模型的思考过程成为 `reasoning` 项的 `reasoning_text`（没有 `encrypted_content`）；工具调用在流的末尾整块发出（参数完整）。
- **请求里有只能原样转发的内容**（`input_file`、按 `file_id` 引用的图片、`include` 里厂商工具的输出、厂商定义的客户端工具如 `local_shell` / `apply_patch` / `custom`、`text.format` 为 `grammar` 等非标准格式、`context_management`）：只走标了「原生 Responses」的线路；
  这个模型没有这种线路时返回 503 `model_unavailable`，`details.reason` 为 `no_capable_endpoint`，`details.unsupported_content` 列出是哪些内容
  （如 `["include:code_interpreter_call.outputs"]`）——平台不会丢掉它们再作答。这类 503 带 `x-should-retry: false`。

## 响应

非流式：

```json
{
  "id": "resp_req_…",
  "object": "response",
  "status": "completed",
  "model": "YOUR_MODEL_ID",
  "store": false,
  "output": [
    { "id": "msg_req_…_0", "type": "message", "role": "assistant", "status": "completed",
      "content": [{ "type": "output_text", "text": "…", "annotations": [] }] }
  ],
  "usage": { "input_tokens": 42, "input_tokens_details": { "cached_tokens": 0 }, "output_tokens": 318, "output_tokens_details": { "reasoning_tokens": 0 }, "total_tokens": 360 },
  "scirouter": { "request_id": "req_…", "attempts": 1, "cost_quota": 18240, "…": "…" }
}
```

顶层的 `scirouter` 是平台的扩展块，内容与[对话补全](https://www.scirouter.cn/docs/chat-completions)里的相同，SDK 会忽略它。一次调用扣了多少配额看 `scirouter.cost_quota`。
回答写到 `max_output_tokens` 时 `status` 是 `incomplete`、`incomplete_details.reason` 是 `max_output_tokens`，和厂商一样。
`usage` 的 `input_tokens` 含命中缓存的部分（`cached_tokens` 单列），`output_tokens` 含推理（`reasoning_tokens` 单列）——与账单的维度一一对应。

流式（`"stream": true`）按 Responses 的事件发：每条带 `event:` 行与连续的 `sequence_number`，顺序是 `response.created` → `response.in_progress` → 每个输出项的 `response.output_item.added` →
`response.content_part.added` → `response.output_text.delta`… → `response.output_text.done` → `response.content_part.done` → `response.output_item.done`（推理项是 `response.reasoning_text.delta` / `done`，函数调用是 `response.function_call_arguments.delta` / `done`）→ `response.completed` 或 `response.incomplete`。和对话补全的流不一样的地方：

- 终态事件（`response.completed` / `response.incomplete`）里的 `response` 就是完整的结果，与非流式逐字段相同。平台把它扣到结算之后再发，事件顶层多一个 `scirouter` 字段（含这次的 `cost_quota`）；
- 终态事件之后还有一行 `data: [DONE]`（OpenAI 官方不发、Open Responses 规范要求发；官方 SDK 与 Codex 在终态事件处收尾，多这一行不影响）；
- 开始输出之后才出错时，发一个 `event: response.failed` 事件（`response.status` 为 `failed`，`response.error` 带 `code` / `message` / `request_id`），再 `[DONE]`。**没收到终态事件就结束的流，一律按失败处理**；
- 流里会有以 `:` 开头的心跳注释行，SDK 会忽略。

非流式请求最长约 300 秒，回答可能很长时用流式（见 [SDK 与框架](https://www.scirouter.cn/docs/sdks)「重试与超时」）。

## 错误

错误体是 OpenAI 的形状，与对话补全相同（连鉴权失败、路径写错也是），按小写的 `error.code` 分支，对照见[错误码](https://www.scirouter.cn/docs/errors)。本接口特有的 400 reason 见上文「请求」。

## 计价参数与模型变体

透传到「原生 Responses」线路时，`service_tier`（取 `auto`、`default` 以外的值）会让厂商按另一档单价收费：平台上架了覆盖它的**模型变体**（id 形如 `厂商/模型名:priority`）时自动改走变体、按变体的价格结算，响应头 `X-SciRouter-Variant-Switch` 写触发的参数；没有对应的变体则 400 `validation_failed`，`details.reason` 为 `price_param_unsupported`。
转换走法不带 `service_tier` 过去，不检查。规则与 [Messages](https://www.scirouter.cn/docs/messages) 相同。

## 请求头与响应头

| 头 | 方向 | 说明 |
|---|---|---|
| `X-SciRouter-Variant-Switch` | 响应 | 这次请求被自动改走了模型变体，值是触发的参数，见上文 |
| `X-SciRouter-Max-Tokens-Clamped` | 响应 | `max_output_tokens` 超过模型或套餐的单次上限、按上限发出时出现，形如 `requested=32000; effective=8192; limit=model` |
| `x-should-retry` | 响应 | 值为 `false` 时这次错误重试不会好（不带 `Retry-After` 的 503，见[错误码](https://www.scirouter.cn/docs/errors)） |

其余 `X-SciRouter-*`、`x-ratelimit-*` 头与对话补全相同，见[可观测性](https://www.scirouter.cn/docs/observability)。

## 缓存、幂等与重试

`Idempotency-Key` 与平台响应缓存的规则和对话补全相同，见[缓存与幂等](https://www.scirouter.cn/docs/caching)；同一个问题用三种格式调是三份缓存，不互相命中。
OpenAI SDK 默认自动重试 429 与 5xx，会先看 `x-should-retry`。控制台的请求日志按调用格式筛选时，本接口的调用是 `responses`。
