* feat(usage): show upstream response model mismatches Persist observed response models separately from routing and billing facts. Surface mismatches with the existing model arrow and native title tooltips. Keep response observations isolated across retries and WebSocket reuse. Co-authored-by: Codex <noreply@openai.com> * fix(usage): calculate costs from the outbound model Co-authored-by: Codex <noreply@openai.com> --------- Co-authored-by: Zyy <zyycn@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com>
83 KiB
Codex Proxy RS 接口
本文列出 v3 源码中的公开 HTTP 接口,路由以
backend/crates/gateway-api/src 中的 router 为准。配置 Codex 请先看 客户端配置;
运行实例是否包含这些功能,应结合其版本和 revision 确认。
1. 鉴权与公共约定
OpenAI 数据面客户端接口
所有 /v1/* 请求都使用管理端创建的 Client Key:
Authorization: Bearer sk_...
自动生成的 Key 保持 sk_ 格式;迁入的自定义 Key 使用保存时的原值,不限制前缀或固定长度。
无论格式如何,只有已保存且启用的 Client Key 能通过鉴权。
Codex 原生生图配置还会携带 X-OpenAI-Actor-Authorization: proxy-managed。
它仅用于客户端识别服务端托管认证,不能代替 Client Key。网关和 OpenAI Provider 都会过滤该请求头,
上游账号身份只由服务端选中的账号提供;不要把真实账号 token 放进该标记。
Client Key 通过账号分组限定路由范围:未绑定分组时可使用全部账号,绑定一个或多个分组时只能使用
已启用分组成员的并集。分组可以混合 openai 与 xai 账号;同一请求只会在模型能力明确匹配且满足
重放安全边界时跨 Provider fallback。
运行设置可以分别配置 minCodexDesktopVersion 与 minCodexCliVersion。两者只接受 SemVer,null
表示不限制。API 在 Client Key 鉴权成功后识别官方 Desktop/CLI 请求头;适用门禁的客户端没有合法版本,或版本
低于对应门槛时,所有 /v1/* HTTP 请求和新 WebSocket 握手在访问上游前返回 426 Upgrade Required。
未知客户端保持兼容,不应用版本门禁。
Desktop 应用版本优先取 version 头,未提供时取 User-Agent 中的 (Codex Desktop; <版本>)。
ChatGPT 远程控制使用 (codex_chatgpt_<平台>_remote; <版本>) 形式的 User-Agent 后缀,已知平台包括
android 和 ios。网关按该命名格式识别非空的平台名,后缀须完整,平台名和版本均不能含空白、括号或分号。
这类 Desktop 请求未提供应用版本时不应用版本门禁;Core 版本和
远程客户端版本不能替代 Desktop 应用版本,因此也无法保证其满足 Desktop 最低版本要求。
携带 version 头或 Desktop 应用版本后缀时仍按上述规则校验,非法版本不会因远程标记而放行。
低版本响应使用 OpenAI 风格错误格式:
{
"error": {
"message": "Codex CLI 0.151.0 is below the minimum required version 0.152.0. Upgrade Codex CLI and retry.",
"type": "invalid_request_error",
"code": "client_version_too_old",
"client": "codex_cli",
"current_version": "0.151.0",
"min_version": "0.152.0"
}
}
因缺失或非法版本被门禁拒绝时,code 为 client_version_unavailable,current_version 为 null。
管理接口
所有 /api/admin/* 请求都需要以下任一鉴权方式:
- 管理员登录后得到的
cpr_sessionCookie(服务端会话身份必须为admin); x-api-key: <admin-api-key>。
同源管理端的登录和退出根据浏览器 Origin 自动设置会话 Cookie:HTTP 来源省略 Secure,
HTTPS 来源以及缺失、null 或非法来源保留 Secure。HttpOnly、SameSite=Lax 始终保留。
不根据 X-Forwarded-Proto 等转发头降级;HTTPS 反代使用 HTTP 回源不影响 Cookie。
部署要求见 公网访问。
请求无需自带 x-request-id;缺失时服务端自动生成 UUID 并在响应头回传同一 request ID。
api.request_id_header 可改变注入与回传的 header 名,管理端鉴权不依赖该名字。
管理端响应统一带 Cache-Control: no-store。
配置了 CORS 白名单 origin 时,跨域请求以凭据模式放行,仅允许 GET/POST 方法和
authorization、content-type、x-api-key 与 request ID 四个请求头,不使用通配符。
普通成功响应使用以下信封:
{
"code": 200,
"message": "OK",
"data": {}
}
所有 /api/admin/* 错误(包括 JSON/Query rejection、未知路由和错误 HTTP method)统一返回
application/json:
{
"code": 40001,
"message": "请求参数不合法",
"data": null
}
管理端本地产生的 message 是可安全展示的中文文案,不包含内部异常或原始上游正文。稳定业务码如下:
| HTTP | code |
含义 |
|---|---|---|
| 400 | 40000 |
请求体不是合法 JSON |
| 400 / 405 / 415 / 422 | 40001 |
通用请求、方法、Content-Type 或字段错误;HTTP 状态保留具体语义 |
| 400 | 40002 |
时间范围不合法 |
| 401 | 40101 / 40102 / 40103 |
会话缺失、过期或吊销 / 登录凭据错误 / 管理 API Key 错误 |
| 403 | 40301 |
有效会话的身份无权访问目标接口;不清除会话 |
| 404 | 40401 |
资源或管理接口不存在 |
| 409 | 40901 |
资源状态冲突 |
| 429 | 42901 |
登录尝试过多 |
| 500 | 50001 |
服务内部错误 |
| 502 | 50201 |
上游服务请求失败 |
| 502 | 50202 |
不可逆上游操作的执行结果未知;刷新状态后再决定是否重试 |
| 503 | 50301 |
依赖服务暂不可用 |
未知 /api/admin/* 路径使用 40401,不会落入 SPA;已存在路径使用错误 method 时返回 405、
40001,并保留标准 Allow header。request ID 继续通过配置的响应 header 返回。
50201、50202 和 50301 的 message 提供可安全展示的具体原因;缺少安全消息时使用通用提示。
认证错误和未知内部错误使用固定文案,不公开 Provider 内部 message、原始响应或凭据。
手动刷新令牌时,按错误类型处理:
code |
场景 | 调用方处理 |
|---|---|---|
40901 |
刷新容量、账号租约占用或账号快照冲突 | 按提示等待,或重新查询账号后操作 |
40001 |
缺少或无效的凭据、已确认刷新令牌失效或账号停用 | 检查凭据或重新授权 |
50201 |
明确的上游失败,且未归类为凭据永久失效 | 根据安全消息处理上游拒绝、限流或服务异常 |
50202 |
无法确认令牌轮换结果,包括 xAI 返回无效的新凭据 | 先核对账号状态,不要自动重发可能已轮换的一次性令牌 |
50301 |
刷新服务或依赖暂不可用 | 检查出站连接与依赖服务后重试 |
OpenAI 上游 401 按 50201 返回,不代表管理员会话失效,也不要求管理端重新登录。
Codex PAT 验证服务不可用和身份响应无效分别返回 50301、50201 及对应的安全提示。
管理写入一致性
管理写入不要求客户端提供全局配置版本。会改变路由快照或安全配置的写入由后端在事务内推进
内部 config_revision,并用于快照发布与审计。账号更新和分组查询/写入的部分响应会返回
configRevision 作为已提交事实,但它不是客户端 mutation 的前置条件。
2. 健康检查
| 方法 | 路由 | 鉴权 | 说明 |
|---|---|---|---|
GET |
/healthz |
无 | Core、Store 和后台任务健康时返回 204,否则返回 503 |
3. OpenAI 数据面与模型目录
除下述 Responses 入站解压保护外,Responses、Images 和 standalone Search HTTP body、 WebSocket message 和 frame 不设置网关私有长度上限;协议可接受性由上游决定。
| 方法 | 路由 | 说明 |
|---|---|---|
POST |
/v1/responses |
OpenAI Responses JSON;stream=true 返回 SSE,否则返回完整 JSON |
GET |
/v1/responses |
通过 HTTP Upgrade 建立 Responses WebSocket |
POST |
/v1/alpha/search |
Codex standalone web search;JSON 请求与响应正文原样转发 |
POST |
/v1/images/generations |
通过 OpenAI Provider 发起图像生成;JSON 请求与响应正文原样转发 |
POST |
/v1/images/edits |
通过 OpenAI Provider 发起图像编辑;JSON 请求与响应正文原样转发 |
GET |
/v1/models |
返回当前 Client Key 账号范围内各 Provider 的可用公开模型并集;有两种响应形态,见下 |
GET |
/v1/models/{model_id} |
返回 OpenAI 兼容的单模型详情 |
Codex 的 review 等子代理请求仍使用 /v1/responses,并通过 x-openai-subagent 请求头携带子代理类型;
网关不提供独立的子代理请求路径。
POST /v1/responses 在鉴权后按 Content-Encoding 解压,再解析 JSON;支持单一 gzip、
deflate(zlib 封装)和 zstd,缺省、空值或 identity 直接使用原始正文。gzip 多成员与 zstd
多帧连续解码,整体展开结果最多 64 MiB,超限在继续展开前返回 400 request_too_large;zstd
回溯窗口同样最多 64 MiB,不能满足该限制的帧按解码失败处理。这个限制保护入站解压资源,不是
模型上下文或 Token 上限;未压缩正文不受此长度限制。
不支持的编码、逗号分隔的叠加编码和重复 Content-Encoding 头返回
400 unsupported_content_encoding;压缩正文损坏、截断或解压后不是合法 JSON 返回
400 invalid_json。本地错误不包含原始正文或解压库细节。WebSocket 文本帧不经过这条解压路径。
Responses 不透传下游的逐跳头、反代元数据(如 cf-*、x-forwarded-*、forwarded、via、
cdn-loop)以及 Accept-Encoding / Content-Encoding。链路元数据和编解码能力
由各段传输层独立管理;其余业务扩展头继续透传,不使用固定业务头白名单。
此规则同时适用于上游 HTTP 和 WebSocket,不影响上游响应的 cf-ray 等诊断信息。
API Key 与 OAuth 共用模拟客户端画像(User-Agent、originator、version)和业务头透传规则,
包括会话、线程、Lite 和其他业务扩展头。
上游认证只来自选中的账号;API Key 不携带 OAuth Cookie、ChatGPT 账号身份或下游的
X-OpenAI-Actor-Authorization 托管认证声明。
Responses 也不透传 x-stainless-*、Origin、Referer、sec-ch-ua* 和 sec-fetch-*
携带的下游 SDK/浏览器环境或页面来源。过滤规则适用于所有下游客户端,与 User-Agent 无关;
原始入站头仍供 CORS、鉴权和本地观测使用。
session_id 请求头仅作为入站会话别名,上游通过 session-id 表达;
两者同时存在时仍优先使用 session-id。正文中的 client_metadata.session_id、
prompt_cache_key 不受这条请求头规则影响。
thread-id、turn metadata 等 Codex 协议字段及未知业务扩展不受下游环境头过滤规则影响;
traceparent、tracestate 不因属于追踪字段而被删除。
Responses 上游编码会移除 Codex 不接受的顶层 temperature、max_output_tokens 和
prompt_cache_retention。缺少顶层 store 时补齐 false,与官方 Codex 客户端一致;
显式提供的值保持原样。HTTP/SSE 与 WebSocket 共用这条正文兼容规则。
prompt_cache_key、reasoning、include 等 Codex 参数继续保留。过滤只作用于顶层,
不删除工具参数 schema、输入内容或 client_metadata 内的同名业务字段;其他未知字段继续透传。
请求头过滤不提供客户端匿名化;系统提示词、工具定义、工具结果、工作目录及其他业务 metadata 保持原有语义,可能包含客户端环境信息。
Responses WebSocket 仅接受文本 response.create,同一连接串行执行。当前响应期间收到的后续业务帧
留在有界接收队列中,待当前响应完成终结和写出后再逐条校验、准入与执行,不因请求提前到达而断开。
接收队列容量为 32 个事件,超载仍关闭连接;Ping/Pong、客户端关闭和服务关闭不等待队列中的请求执行。
OAuth 账号在客户端使用 HTTP/SSE 时仍可能选择上游 WebSocket。API Key 账号默认使用 HTTP/SSE,
可在账号上配置 prefer_websocket;必须依赖 WS 的预热、非持久化新链和连接内续接不使用 HTTP-only 账号。
客户端配置的 supports_websockets 只控制第一段连接,不是服务端传输策略开关。
上游在响应终态前发送 Close 1000 仍属于失败,不能按“正常关闭”计为成功。
已建立模型执行的 Responses、Images 和 Search HTTP 响应按以下规则返回关联 ID:
x-gateway-request-id 为模型执行 ID;x-request-id 保留有效上游值,只有上游
x-oai-request-id 时复用其值,没有上游 ID 时使用模型执行 ID。x-oai-request-id 不是必需字段,
也不要求客户端识别它;OpenAI 与 xAI 路由使用相同规则。失败响应的关联 ID 不采用会话 opening ID,
错误正文读取失败时仍返回已知上游 ID;已采集的 turn state 等允许的会话头继续按原合同交付。
尚未建立执行的入口拒绝继续使用 middleware 的入口关联。
WebSocket 在尚未交付上游业务事件时合成的错误保留已确认的失败状态,以及 Provider 提取的结构化
message/type/code;没有结构化错误时使用稳定安全文案,不把原始 HTML 或截断正文当作 message。
合成错误自身的 headers 携带允许下发的响应头:优先保留实际失败的上游 request ID,无上游 ID 时
提供网关关联 ID,并用 x-gateway-request-id 独立标识网关请求。已经取得的原始上游错误帧不重写。
客户端可能对特定状态另行统一展示;这不构成网关改写真实状态码的理由。
GET /v1/models 默认返回 OpenAI 兼容列表 {"object": "list", "data": [...]};请求携带非空
client_version query 参数(Codex 客户端)时改为返回 Codex 专用目录合同 {"models": [...]}。
OpenAI Provider 按客户端传入的 client_version 请求上游目录,完整保留每个模型 JSON 对象,包括
base_instructions、model_messages、service_tiers、工具与能力字段,以及未知嵌套字段、显式 null
和字段缺失的区别。
模型别名仅替换 slug,不替换上游展示名、提示词、能力或 priority;保持原生模型顺序,新增别名附在后面。
目录按当前路由快照的模型存在性及账号模型政策过滤,避免公布已知无法路由的模型;新模型需待后台目录对账后进入列表。
xAI 没有 Codex 原生目录,继续使用明确的通用画像适配。
目录账号只能来自本次 Client Key 冻结的账号范围。OpenAI 在其中按账号 ID 排序,使用首个成功读取的
合格账号,最多尝试三个账号;同名模型不跨账号或套餐混拼字段。因此单账号、无别名时可保持该账号的
模型对象一致,多账号/多 Provider 聚合不代表“与某个官方账号的整个目录完全一致”,也不会固定后续推理账号。
读取失败返回 503 model_catalog_unavailable,不以简化模板或空成功响应覆盖客户端缓存。
原生目录可能返回缓存结果,成功缓存有效期为 5 分钟。每次查询都检查账号资格和 Client Key 范围。
成功目录响应带 Cache-Control: private, no-store,不借用上游 ETag 标识经过选择/别名/聚合后的正文。
service_tiers 的 name 用于生成 /fast 等命令,id 是请求使用的 service_tier 值,二者不能互换。
上游未声明或明确返回空数组时不补档位,也不根据模型名或旧 additional_speed_tiers 字段推断 Fast。
该合同对齐
官方 Codex 0.154.0 的模型元数据
与其动态服务档位命令;客户端需重新加载模型目录才能使用更新后的档位信息。
Codex 专用目录中的 context_window 与 max_context_window 分别表示默认上下文窗口和客户端本地
覆盖的上限。OpenAI Provider 原样保留上游对应字段,缺失与 null 不互相转换;网关不通过部署配置
覆盖这些值。Codex 客户端配置 model_context_window 后,按该值与非空 max_context_window 的较小值
使用窗口;上限为空时保留客户端本地值。xAI 目录只声明一个窗口,其 Provider 继续以该值作为客户端覆盖上限。
OpenAI 路径保留客户端 Responses wire 语义:请求 body 的未知字段和字段顺序保持不变(受控模型
映射除外),HTTP SSE 与 WebSocket 的上游业务事件字节原样转发,response ID 按 opaque 值处理而不
假设 UUID 或固定长度;OpenAI 上游错误 envelope 和允许下发的 opaque header 值也不由 canonical
观测结果重写。Images 请求不读取或重建 JSON,也不要求或映射模型字段;它固定使用 OpenAI Provider,
只在原始字节之外完成账号选择、鉴权头替换和端点路由,成功与失败响应正文同样保持原始字节。
/v1/alpha/search 使用相同的 OpenAI Provider 原生端点边界:body(包括 model)不解析、不映射,
x-codex-turn-metadata 在移除客户端账号身份并按当前 lease 重写 installation ID 后转发;上游账号
Authorization、Cookie、account ID、originator 和 User-Agent 均由代理安全重建。xAI 是 Grok wire 与
Responses wire 之间的协议转换层,转换只在 xAI Provider 内完成。
上游结构化错误的 message/code/type 会透传给客户端,其中内嵌的账号指纹 UUID 已脱敏。模型映射是
全局精确映射,未命中时模型名原样交给候选 Provider;分组只限定账号集合,不参与模型改名。
OpenAI 明确返回 server_is_overloaded、slow_down 或模型容量不足错误时,代理在允许安全重放且
尚未交付输出的前提下,先做最多 3 次同账号指数退避,再通过现有调度换号。默认间隔从 500ms 开始,
上游 Retry-After 参与退避计算,单次等待不超过 8 秒;重试同时受请求总尝试次数和截止时间约束。
server_is_overloaded、slow_down 等可计分的结构化错误按已发送的失败尝试计入 Smart 账号
健康分。失败率使用账号级平滑与时间衰减,影响后续普通选路,已有可用账号的
会话亲和仍优先。容量不足不触发 Provider 全局熔断,也不作为账号额度耗尽;启用账号自动冻结时,
达到容量失败阈值会另外写入临时冷却。
最终交付的上游错误仍按上述透明边界保留原始状态码、错误码和正文。
明确额度耗尽触发账号隔离与安全换号,
包括 WebSocket 握手返回的 429;不会因其长 Retry-After 而转入同账号传输恢复等待。
4. 浏览器认证
统一登录与会话
管理员和密钥登录共用 /api/auth/*。登录模式 mode 只用于选择凭据验证方式,不直接授予权限;
验证成功后,由后端写入身份和绑定 ID。一个浏览器只持有一份 cpr_session HttpOnly Cookie,
原始 Key 不进入 URL、Pinia 或浏览器存储。登录页的切换只改变本地表单,不改变 URL。
管理员进入管理端;Key 登录后进入 /key-usage,只读取当前会话绑定 Key 的数据。
| 方法 | 路由 | 请求 | 说明 |
|---|---|---|---|
POST |
/api/auth/login |
{ mode: "admin", username?, password } 或 { mode: "key", apiKey } |
验证凭据、创建会话;成功后撤销请求携带的旧会话 |
GET |
/api/auth/status |
无 | 从 Cookie 恢复服务端身份,返回 { authenticated, session } |
POST |
/api/auth/logout |
无 | 删除当前会话并清除 Cookie;存储失败返回 503,不假装退出成功 |
登录返回 data: { role: "admin" | "key", expiresAt };status 已登录时的 session 使用同一结构,
未登录时为 { authenticated: false, session: null }。role 由服务端已验证身份推导,不接受客户端声明。
不返回凭据或绑定 ID。
会话由服务端保存,Cookie 属性为 Path=/; HttpOnly; SameSite=Lax,Max-Age /
Expires 对齐固定有效期,Secure 沿用上述 Origin 规则。轮询不会续期。
管理员有效期由 admin.session_ttl_minutes 控制;密钥有效期由 client.session_ttl_minutes 控制,默认 1440 分钟。
每次恢复密钥会话时重新确认 Key 存在且启用;停用或删除后会话失效,重新启用不会恢复已撤销会话。 依赖不可用时返回 503,不返回已认证或假装未登录。预算耗尽不妨碍登录。
密钥会话访问管理接口返回 403,不清除仍然有效的会话。
浏览器会话不能替代 /v1/* 的 Bearer Key,数据面 Key 也不能替代浏览器会话。
所有 /api/auth/* 响应带 Cache-Control: no-store;未知路径和错误 method 返回 JSON,不落入 SPA。
两种登录共享来源桶和全局桶,分别为每 60 秒 10 次 / 200 次;来源取连接 IP,不信任任意转发头。
拒绝时使用 42901 和 Retry-After。
认证错误共用 40101(会话失效)、40102(凭据错误)和 40301(权限不足)。
前端只在明确的会话失效时统一退出,不按 URL 或每个接口上的身份标记分发。
Key 用量查询
以下接口仅接受 Key 身份的 cpr_session,不接受 Bearer Key 或管理 API Key。管理员会话返回 40301;
缺失、失效或已停用的 Key 会话返回 40101。所有响应带 Cache-Control: no-store,未知路径和错误方法返回 JSON。
| 方法 | 路由 | 查询 | 说明 |
|---|---|---|---|
GET |
/api/key-usage/overview |
startTime、endTime、model? |
用量汇总、趋势、当前额度和北京时间今日健康时间线 |
GET |
/api/key-usage/records |
同上,另含 kind?、currentPage?、pageSize? |
当前 Key 的成功请求或错误记录 |
起止时间使用 RFC3339,开始必须早于结束,一次最多 31 天。模型按完整名称匹配;
不接受 Key ID、账号、Provider 等范围参数或其他未知字段。页码默认 1,每页默认 20,允许 1–100 条;
kind 为 success(默认)或 error。分页响应为 { items, currentPage, pageSize, total }。
overview 返回 asOf、startTime、endTime、key、summary、trend、healthTimeline。
key 仅包含名称、掩码前缀、并发/RPM、日与七日限额、已用 USD 及重置时间;零限额表示不限,
未启动窗口的重置时间为 null。额度使用现有结算账本,不受日志日期或模型筛选影响。
健康时间线沿用管理端的 96 个北京时间日内桶与可用性语义,不受历史范围和模型筛选影响。
汇总和趋势返回请求数、输入、输出、缓存读写、推理、总 Tokens 与 USD 成本;输入已包含缓存读写,
推理为输出的明细,不得把缓存或推理重复计入总消耗。趋势另含 time 与 bucketSeconds。
costUsd 为十进制字符串或 null;costIncomplete 表示部分请求计费不完整,不能把已知费用当作完整总费用。
空请求范围的成本为 "0",缺失定价保持 null。
日志只返回时间、公开请求模型、推理强度、接口、上下游传输方式、当前请求的 IP / User-Agent、
Token 明细、费用明细、用时/首字与状态。Token 和费用复用现有展示合同;延迟仅包含当前请求的首事件、
首推理、首文本和总耗时,不含账号容量或调度诊断。
成功记录的 status 为 success,不伪造未保存的 HTTP 状态;错误记录为 error,只返回客户端状态码,
缺失的 Token/费用明细为 null。不返回账号资料、Key ID、上游模型或请求标识、原始错误正文或诊断内容。
5. 账号
账号 API 使用统一路由,不存在 Provider Instance 或 Provider 专属账号路由。需要 Provider 的请求只接受
provider: "openai" | "xai"。
| 方法 | 路由 | 主要 query/body | 说明 |
|---|---|---|---|
GET |
/api/admin/accounts |
page、pageSize、provider、groupId、search、status、排序字段 |
分页查询账号与汇总 |
GET |
/api/admin/accounts/detail |
accountId |
查询账号详情、额度和本地用量 |
GET |
/api/admin/accounts/export |
accountIds、confirm=export_sensitive_accounts |
显式导出最多 200 个账号的敏感 Provider 文档 |
POST |
/api/admin/accounts/import |
{ provider, data, settings?, outboundProxyId? } |
导入或按上游身份更新账号,可同时应用调度、分组设置与默认代理 |
POST |
/api/admin/accounts/import-tasks |
{ submissionId, items: [{ provider, data, settings?, outboundProxyId? }] } |
接受后台导入,返回 HTTP 202 和任务摘要 |
GET |
/api/admin/accounts/import-tasks |
无 | 当前管理员仍保留的任务,按创建时间倒序 |
GET |
/api/admin/accounts/import-tasks/detail |
taskId |
任务摘要和逐条结果,不含原始凭据 |
POST |
/api/admin/accounts/import-tasks/stop |
{ taskId } |
跳过未开始的条目,已开始的条目继续完成 |
POST |
/api/admin/accounts/refresh |
{ accountId } |
手工刷新 OAuth credential(idToken / accessToken / refreshToken),不刷新额度 |
POST |
/api/admin/accounts/recover |
{ accountId } |
管理员显式清除该账号的本地错误/额度/cooldown 事实并重新启用,不访问上游 |
POST |
/api/admin/accounts/rotate |
OpenAI rotation 字段 | 更新指定 OpenAI 账号的 OAuth token 或 API Key 上游设置 |
POST |
/api/admin/accounts/update |
{ accountId, enabled, concurrencyLimit, weight, groupIds, notes?, modelAccess?, outboundProxyId?, outboundProxyUrl? } |
一次更新账号备注、调度状态、并发上限(null 表示继承运行参数)、权重(1–100)、所属分组与出站代理 |
POST |
/api/admin/accounts/batch-update |
{ accountIds, enabled?, concurrencyLimit?, weight?, groupIds?, modelAccess?, outboundProxyId?, outboundProxyUrl? } |
一次事务更新所选账号;仅修改提供的字段,至少提供一项修改 |
POST |
/api/admin/accounts/delete |
{ provider, accountIds } |
批量删除 1–200 个账号 |
GET |
/api/admin/accounts/quota |
accountId |
读取当前额度,不强制访问上游 |
GET |
/api/admin/accounts/quota-forecast |
accountId |
按需读取周/月容量预测、源窗口剩余估算与采样依据,不刷新上游额度 |
POST |
/api/admin/accounts/quota/refresh |
{ accountId } |
访问 Provider 并刷新额度,同时同步额度所属状态 |
GET |
/api/admin/accounts/personal-info |
accountId |
按需汇聚 OpenAI/Codex 官方个人资料、累计活动与订阅信息,不更新额度或 credential |
GET |
/api/admin/accounts/reset-credits |
accountId |
查询 OpenAI 上游主动额度重置卡,不读取本地库存 |
POST |
/api/admin/accounts/reset-credits |
{ accountId, creditId?, redeemRequestId } |
使用 UUIDv4 幂等键消费一张 OpenAI 上游重置卡 |
GET |
/api/admin/accounts/models |
accountId |
优先读取该 Provider + 套餐的模型 cache,缺失时有限实时拉取 |
POST |
/api/admin/accounts/models/refresh |
{ accountId } |
强制拉取最新模型并覆盖 cache |
GET |
/api/admin/accounts/connection-test |
accountId、modelId |
通过 SSE 返回实时连接测试事件,不作为业务 Responses 用量记录 |
POST |
/api/admin/accounts/oauth/start |
{ provider, name, accountId?, outboundProxyId?, outboundProxyUrl? } |
创建 OpenAI 或 xAI OAuth flow;accountId 表示重新授权 |
POST |
/api/admin/accounts/oauth/complete |
{ provider, flowId, callbackUrl, settings? } |
消费 OAuth callback;首次授权可附带账号设置,重新授权保留原设置 |
账号列表支持以下稳定值:
provider:all、openai、xai;groupId: 分组 ID、ungrouped,或省略以不过滤;status:normal、quota_exhausted、rate_limited、disabled、error;sortBy:email、status、planType、usage、lastUsedAt、expiresAt;sortDirection:asc、desc。
账号限流详情在 quota 中返回:rateLimitReason 为 upstream_rate_limit(上游临时限流)、
capacity_freeze(容量错误触发自动冻结)或 null。recoveryProbeRequired 表示解除冻结是否需要成功探测;
此时 rateLimitedUntil 是最早探测时间,到期后仍保持 rate_limited,直到探测成功或手动恢复。
未要求探测时,该字段表示冷却结束时间。所有此类情况统一显示“限流中”,仅详情原因和恢复条件不同。
账号列表和详情返回 notes(无备注时为 null)。编辑时省略或 null 保留原备注;字符串最多 500 个 Unicode
字符,允许换行和制表符,保存时去除首尾空白,空字符串清空备注。备注独立于上游身份,导入时未显式提供备注、
重新授权、凭据刷新及批量调度更新均保留已有备注。
账号视图和 Dashboard 账号概览中的 planType 保留原始套餐值;planTypeDisplay 由后端先按 Provider 解析名称,
再统一为大驼峰格式,前端直接展示该字段,例如 Free、SuperGrokPro、EduPlus。
OpenAI 的 self_serve_business_prolite 等 Team 套餐显示为 Business;新套餐也使用相同格式。
OpenAI 主动额度刷新和正常响应携带的明确套餐会同步到账号,支持升级与降级;
空值或 unknown 不覆盖已有套餐,同族泛化值(如 team)保留已知的具体套餐子类型。
账号套餐为空或 unknown 时,后端优先用已保存的上游额度响应
中的明确套餐值补全 planType 和 planTypeDisplay;两处均无套餐信息时才显示“未知套餐”。
outboundProxyId 绑定已保存且最近测试成功的代理;省略或 null 保留当前绑定,空字符串清除绑定。
outboundProxyUrl 兼容 HTTP、HTTPS、SOCKS5、SOCKS5H 代理 URL,可带用户名和密码;不能与 ID 同时设置。
编辑时省略或 null 表示保持原配置,空字符串表示清除代理并直连。列表和详情只返回
不含认证信息的 outboundProxyEndpoint(直连时为 null);只有显式敏感导出包含完整 URL。
指定代理后,推理、OAuth 服务端交换/刷新及账号辅助请求使用同一出口;代理失败不会退回直连。
浏览器打开的第三方 OAuth 授权页仍使用浏览器自身网络。
账号出口与连接隔离见 架构说明。
账号模型限制
账号列表和详情返回 modelAccess: { mode, models },模型 ID 区分大小写并精确匹配:
mode |
models |
含义 |
|---|---|---|
all |
[] |
不额外限制模型,默认值 |
allowlist |
非空 ID 数组 | 仅允许指定模型 |
denylist |
非空 ID 数组 | 排除指定模型 |
最多 256 项,每个 ID 最多 256 字节;重复 ID 去重,不接受空白、控制字符、__ 前缀或 * 通配符。
允许保存当前上游目录尚未返回的 ID。限制按全局模型映射后的上游 ID 判断,不扩大账号本身的上游权限。
例如 Plus 设置 allowlist 并选中 luna 的实际 ID,Pro 设置 denylist 并选中同一 ID,即可严格分流;
Pro 使用 all 时也能参与 luna 调度。套餐名称不自动生成或修改规则。
单账号更新或批量更新省略 modelAccess 时保留原值,显式提交 { "mode": "all", "models": [] } 清除限制。
批量接口的调度、分组、模型与代理字段均可省略;省略的字段保持各账号原值。
提供 groupIds 时替换完整分组集合,提供 concurrencyLimit: null 时恢复继承运行参数。
限制适用于 Responses HTTP、WebSocket 及其带压缩触发的请求选号,包括重试、亲和和换号;没有合规账号时沿用
无可用账号错误,不会回退到被禁止的账号。已开始请求使用冻结的政策,新请求使用已发布的新配置。
Images、独立 Search 及管理员连接测试不受该文本模型限制;连接测试成功只证明指定账号的上游能力。
/v1/models 和单模型查询按当前 Key 范围内账号政策过滤;原生目录保留已有来源选择和完整模型对象。
独立代理管理 / Managed Proxies
所有端点要求管理员身份。所有响应只返回去掉认证信息的 endpoint,不会返回完整 URL。
| 方法 / Method | 路径 / Path | 请求 / Request | 结果 / Result |
|---|---|---|---|
GET |
/api/admin/proxies |
page、pageSize(1-200)、search(名称) |
{ items, page } |
GET |
/api/admin/proxies/accounts |
proxyId、page、pageSize(1-200)、search(账号名称或邮箱) |
{ items, page } |
POST |
/api/admin/proxies/accounts/remove |
{ proxyId, accountId } |
{ configRevision } |
POST |
/api/admin/proxies/create |
{ name, proxyUrl, location? } |
201 { record, configRevision } |
POST |
/api/admin/proxies/update |
{ id, revision, name, proxyUrl?, location? } |
{ record, configRevision } |
POST |
/api/admin/proxies/test |
{ id, revision } |
最新代理记录 / Proxy record with test result |
POST |
/api/admin/proxies/delete |
{ id, revision } |
{ configRevision } |
record 包含 id、name、endpoint、hasAuthentication、revision、accountCount、location、
lastTestAt、lastTest: { success, latencyMs, exitIp, message }、createdAt、updatedAt。
未测试时 lastTestAt / lastTest 为 null。连通性失败返回 HTTP 200 和 lastTest.success=false;
记录版本过期、重复 URL、删除已绑定的代理返回 409,并发测试满载返回 429。
代理列表只返回关联账号数量。关联账号按需查询,每项包含 id、name、email、provider、enabled、
authenticationKind、planType、planTypeDisplay 和 groups: [{ id, name, color, enabled }],
不返回账号凭据。默认每页 20 条,按名称、ID 稳定排序;搜索不区分大小写,匹配名称或邮箱的字面子串。
不存在的代理返回 404,未绑定账号或没有匹配结果时返回空页。数量与当前页对应同一查询快照。
移除关联账号只清除指定账号的代理绑定与连接地址,使其改为直连,保留凭据、调度参数与分组。 若账号已不再绑定请求中的代理,则返回 409。
更新省略 proxyUrl 保留认证;连接配置改变时清除测试结果并更新所有绑定账号。
测试结果只在请求中的版本仍匹配时保存。
location 为 null 或完整对象 { country, region, city, timezone }。国家代码为两位大写 ASCII 字母;
地区、城市禁止控制字符,去除首尾空白后须为 1–128 个字符;时区必须是有效 IANA 名称,例如 Asia/Tokyo。
创建时省略或 null 表示继承全局;更新时省略表示保留,null 清除覆盖,完整对象替换覆盖。
只改位置不清空连通性测试结果,也不更改账号凭据版本。
关联账号的 OpenAI/Codex Responses 请求(HTTP/SSE、WebSocket)优先使用代理位置,否则使用全局
运行设置中已开启的 requestLocation;两者均未开启时保留客户端原有位置和时区。全局覆盖按请求冻结,
新请求使用保存后的设置,无需重启;代理覆盖在每次执行时读取,
换号或换出口按该次选定账号解析。位置只影响带来源标记的环境上下文日期/时区和 Web Search 的结构化位置,
不改变用户普通文本、epoch 时间戳、真实出口 IP、服务或管理端时区、数据驻留约束及 xAI 请求。
测试固定经代理访问双栈端点 https://api64.ipify.org?format=json,返回本次连接实际使用的 IPv4 或 IPv6 出口地址,
不分别验证两种地址族的连通性。超时 15 秒,每进程最多同时测试 4 条。
探测器复用 OpenAI 的证书信任配置:优先读取非空的 CODEX_CA_CERTIFICATE,
其次读取 SSL_CERT_FILE,并保留系统根证书;证书配置错误不会回退为不验证证书。
出口测试通过不表示 Provider 账号权限或额度可用;账号可用性使用账号连接测试。
导入请求可以携带顶层 outboundProxyId,在令牌交换前解析为默认出口;文件中显式的代理配置优先。
文件及 AT/RT 导入从凭据交换到落库期间保护所选代理;此时修改、删除或写入测试结果返回 409,
避免已轮换的凭据因代理状态变化而丢失。完成导入或请求取消后自动释放保护。
OAuth 等待回调期间不持有保护;提交仍拒绝已删除、连接配置改变或测试失败的代理。
账号连接测试 SSE
GET /api/admin/accounts/connection-test 固定探测请求指定的账号,不参与普通账号轮换。成功流沿用
test_start、request、content、test_complete 事件;失败事件为:
{
"type": "error",
"source": "upstream",
"gatewayErrorCode": "rate_limited",
"sendState": "sent",
"error": "upstream unavailable",
"providerErrorCode": "usage_exhausted",
"providerErrorType": "invalid_request_error",
"upstreamStatus": 429,
"upstreamContentType": "application/json",
"upstreamBody": "{\"error\":{...}}"
}
source为gateway、provider或upstream:分别表示尚未进入 Provider、Provider 本地且未发送、 已发送/可能已发送或已经捕获到上游事实。gatewayErrorCode是GatewayErrorKind的稳定机器值,管理端据此生成中文摘要。sendState为not_sent、sent、ambiguous,非 Provider 错误为null。error、providerErrorCode、providerErrorType、upstreamStatus、upstreamContentType和upstreamBody是实际捕获的原始诊断字段;缺失时为null,不会由本地猜测或翻译。
后台导入任务
管理端通过后台任务导入账号,关闭页面不会取消执行。submissionId 为客户端生成的 UUID;同一管理员在任务记录
保留期间使用相同标识和相同输入重新提交,会返回已有任务,内容改变则返回 409。修改输入须使用新标识。
每个任务接受 1–200 个 items,请求体上限为 4 MiB。每个条目遵循下节的 Provider 文档与设置合同,独立处理
并返回结果。批量 AT / RT 按非空输入顺序拆成单账号条目,因此可逐条统计;
JSON 文件按 Provider 文档分项,不拆解内部代理引用或改变 Provider 对文档的原子性与部分成功语义。
一个文档可导入多个账号,条目成功数与入库账号数可能不同。
摘要字段为 taskId、createdAt、finishedAt(未结束为 null)、stopRequested、total、counts。
counts 包含 pending、running、succeeded、failed、unknown、skipped 和 importedAccounts。
详情追加 items: [{ index, provider, status, accountIds, message }],index 从 1 开始;status 对应上述前六类状态。
列表返回 { items: [摘要] }。结果未知时保留 unknown,先核对账号目录再决定是否重新导入;服务端不自动重试凭据交换。
停止请求可重复调用,已结束的任务保持原结果;未知、已过期或其他管理员的任务 ID 返回 404。
所有后台导入共享 3 个执行槽位; 最多接受 8 个未结束任务、保留 100 个任务,达到上限返回 429。成功、失败或跳过后立即释放对应输入, 终态结果保留 1 小时后自动清理。服务重启会丢失任务与未执行输入,已提交的账号不受影响; 任务记录仍在保留期内时,可通过列表接口查询当前管理员的任务及进度。
账号导入与 OAuth
导入的 data 必须是 JSON object,Admin API 请求上限为 64 MiB;Provider 可以收紧限制,
当前 xAI 导入上限为 16 MiB。内部 schema 由目标 Provider 独占解释:
- OpenAI 接受单账号 OAuth 或 API Key 文档、
accounts数组(最多 200 项)、CPR 账号 bundle 和含代理引用的 sub2api 导出; - OpenAI OAuth token 字段接受
accessToken、refreshToken、idToken,以及官方auth.json中的access_token、refresh_token、id_token,可以嵌套在tokens等账号 object 内; 每项至少包含 AT 或 RT。仅含OPENAI_API_KEY的客户端代理配置不是 OAuth 账号导入材料; RT-only 会在导入时换取 AT,AT-only 不具备自动续期能力; - OpenAI 与 xAI 的账号条目接受
outboundProxyUrl;OpenAI 还会解析 sub2api 的proxy_key和顶层proxies。 代理在 token 刷新前绑定。缺失、重复、停用、带到期时间或配置回退策略的 sub2api 代理会拒绝导入; - xAI 从单账号 object 或
accounts数组中提取 OAuth token;并发、优先级等字段不参与认证; - xAI 批量导入逐条独立校验:失败条目跳过并记录日志,不中断其余条目,仅当没有任何条目成功时整个导入才报错;
- xAI API Key 不是受支持的账号 credential;
- OAuth 导入由目标 Provider 完成必要的 token exchange 或身份投影。API Key 导入校验凭据格式与地址,不调用 OAuth 或 ChatGPT 身份接口;可用性由模型目录和连接测试验证。
API Key 账号使用以下独立凭据形态:
{
"provider": "openai",
"data": {
"authentication_kind": "api_key",
"name": "团队上游",
"base_url": "https://api.example.com/v1",
"api_key": "<upstream-api-key>",
"transport": "http"
}
}
base_url 是 API 前缀,追加 responses、models、images/generations、images/edits 或 alpha/search,
不会自动补 /v1;支持根路径和自定义前缀。
地址仅允许 HTTPS,HTTP 只允许本机回环;拒绝 URL 中的认证信息、查询串和 fragment。
transport 可省略(默认 http)或设为 prefer_websocket。API Key 使用 Bearer 认证与普通 JSON,
不携带 OAuth Cookie 或 ChatGPT 身份。上游模型列表使用标准 /models 格式,按账号和凭据版本隔离;
目录用于模型发现,不作为能力白名单,未列出的模型仍交由上游判断。
API Key 每次导入创建独立账号;更新已有账号使用 rotate。导出会显式包含密钥,沿用敏感导出的确认合同。
sub2api 的 platform=openai、type=apikey 使用 credentials.base_url / credentials.api_key;
导入时按其端点规则将服务根、版本前缀或完整 /responses 地址转换为 API 前缀,缺省地址为官方 /v1。
非空模型映射、请求头覆盖、其他协议和启用的 extra.openai_* 设置尚未适配,返回输入错误,需先移除并在本项目重新配置。
API Key 账号与 OAuth 共用现有 Responses、Images 和 standalone Search 请求与响应链路,
包括 Responses Lite 和原生压缩协议的透传,实际支持情况由上游决定;不提供独立 compact 路由。
模型目录、连接测试和本地用量统计可用;OAuth 刷新/重新授权、ChatGPT 额度、个人资料、订阅和重置卡不适用。
客户端的 image_generation 和 X-OpenAI-Actor-Authorization 声明不改变账号的实际能力。
账号列表和详情的 API Key usage 汇总该账号创建后仍保留的本地请求记录,windowLabelDisplay 为 通用额度;
日志清理会影响累计范围,不代表上游余额。OAuth 账号仍按实际周/月额度窗口统计。
批量导入 AT / RT 使用 accounts JSON 数组,最多 200 项,不接受纯文本 token 列表。例如:
{
"provider": "openai",
"data": {
"accounts": [
{ "accessToken": "eyJ..." },
{ "accessToken": "eyJ...", "refreshToken": "rt_...", "idToken": "eyJ..." }
]
}
}
RT-only 使用同一形状,只提交 refreshToken。不得把真实 token 写入日志、issue、fixture 或文档。
账号导入与首次 OAuth complete 可附带 settings: { enabled, concurrencyLimit, weight, groupIds, notes?, modelAccess? }。
提供 settings 时前四项均必填,concurrencyLimit: null 继承运行参数,否则为 1–4294967295 的整数;
weight 为 1–100,groupIds 为完整分组集合。设置应用于本次导入的全部账号,包括匹配到的已有账号,
与凭据在同一事务内提交;分组不存在时整次回滚。省略 settings 时新账号使用默认设置并保持未分组,
已有账号保留原有分组、权重与并发设置。可选 notes 与编辑备注使用相同的校验和清空语义,省略或 null 保留已有备注;
管理端新建表单留空时省略 notes。重新授权不接受 settings,credential refresh 和未携带 settings 的 rotation 保留账号设置。
账号列表的每个 item 返回轻量 groups: [{ id, name, enabled }]。
OpenAI 的 CPR 导出保持 OAuth 账号的既有 token 与过期时间字段。OpenAI 与 xAI 的 CPR 账号条目均包含
modelAccess,重新导入时恢复;导入 settings.modelAccess 显式提供时覆盖条目中的政策。
旧文档不带此字段时,新账号默认 all,已有账号保留原值;凭据刷新、重新授权和目录刷新均保留政策。
sub2api 的 credentials.model_mapping 不转换为本项目的账号模型限制。
OpenAI rotation 请求字段为:
{
"provider": "openai",
"accountId": "acct_...",
"idToken": "...",
"accessToken": "...",
"refreshToken": "..."
}
API Key rotation 使用 { provider: "openai", accountId, baseUrl, transport, apiKey?, settings? }。
省略 apiKey 保留当前密钥,空字符串无效;账号 ID 和认证类型不能通过轮换转换。
rotation 可选携带 settings,字段与 POST /api/admin/accounts/update 相同,其中 accountId 必须与外层一致。
凭据与设置在同一事务中保存,任一校验或持久化失败均不落库;省略 settings 保留现有分组、调度等设置。
GET /api/admin/accounts/detail 对 API Key 账号额外返回 credentialConfiguration: { base_url, transport },不回显密钥。
更新会推进凭据 revision 并失效目录与连接;旧版本会话不可静默续接到新上游。
OAuth start 使用:
{
"provider": "openai",
"name": "account name",
"accountId": null
}
重新授权已有账号时,start 请求仍携带 provider 和展示用 name,只额外提供目标 accountId;
客户端不得提交 credentialRevision、旧 token 身份或其他并发控制字段。complete 请求也不重复提交
accountId,后端通过 flowId 中保存的目标绑定完成授权。
OpenAI 身份、额度与状态
- OAuth 文件导入接受 camelCase 与 snake_case 的三个 token 字段,内部统一保存为
accessToken、refreshToken、idToken,不接受含义模糊的token。 仅有 refresh token 时先换取 access token。 - 普通 OAuth 的身份补全复用官方
token_data.rs::parse_chatgpt_jwt_claims:优先解析idToken,缺失字段再由accessToken补齐;email优先 JWT 顶层值、其次https://api.openai.com/profile.email,用户 ID 优先chatgpt_user_id、其次user_id。该路径不调用whoami,也不信任导入文档顶层的userId/accountId。 at-开头的 Codex Personal Access Token(PAT)可直接粘贴到现有 AT 导入 入口,或使用{"accessToken":"at-..."}JSON;也接受官方auth.json的personal_access_token字段 (兼容personalAccessToken)。导入时向 OpenAI auth 的/api/accounts/v1/user-auth-credential/whoami验证令牌,以响应中的用户 ID、账号 ID 和套餐建立身份,email可缺失;不从导入文件的身份字段或附带的 ID token 回退补齐。验证失败不导入,接口区分提示 PAT 格式无效、被上游拒绝、验证服务不可用和身份响应无效,不回显令牌或原始上游响应。 PAT 不保存附带的 refresh token、ID token 或推测的过期时间,不参加 OAuth RT 刷新; 失效后需取得新 PAT 再导入。普通 JWT 导入行为不变。- 首次 OAuth 保留回调
state、PKCE 与官方 token exchange,并持久化idToken、accessToken、refreshToken。刷新响应中的三个 token 字段均按官方语义独立轮换:返回新值时替换,省略时分别保留 现值。重新授权也保留这些回调保护,但只轮换目标账号的 token。回调地址只承载code/state, 不以 host/path 形式作为拒绝条件。 - OAuth 账号文件导入和 OAuth complete(包括重新授权)在 credential 提交后后台尝试一次额度观测,不等待 观测完成才返回成功。观测失败只记录告警,不回滚已提交的账号;手工或后台 RT 刷新只更新 token, 不隐式等同于手工额度刷新,也不更新既有账号资料或 OAuth principal。xAI 导入与 OAuth complete 使用相同的提交后观察流程。
- OAuth pending flow 先取得带过期时间的独占 claim,只有账号事务提交成功后才消费。失败会释放 claim, 但上游 authorization code 本身通常只能交换一次;已完成过 token exchange 时应重新创建 OAuth flow。
GET /accounts/quota只读取最后一次落库快照;POST /accounts/quota/refresh才访问上游。access token 已过期时,额度刷新要求先走 credential 刷新或重新授权,不会拿过期 token 探测额度。- OpenAI 已耗尽账号每 30 分钟主动复核一次,也会在最早未恢复窗口的
resetAt + 2 分钟到期后 提前复核。后台每 30 秒检查触发条件;同一重置边界复核后仍未恢复时回到 30 分钟重试, 避免旧 reset 持续触发请求。各窗口独立确认恢复,时间到期本身不会直接解除账号耗尽。 POST /accounts/recover是管理员对本地事实的强制恢复:它清除 Redis cooldown 和已保存的额度/错误, 把账号重新启用并恢复为可调度 credential;它不验证上游账号是否已经恢复,下一次真实请求仍可重新写入 失败事实。- 成功额度观测会 revision-fenced 写入 quota;明确
Allowed投影为normal,明确耗尽投影为quota_exhausted。额度观测不会清除凭据过期、无效或封禁事实;这些事实统一投影为error,并由errorReason区分。额度接口的 401/403 也不足以判定 refresh token 永久失效,credential 终态只由 OAuth refresh 的明确永久错误写入。 - 正常 Responses 请求会解析上游响应的 rate-limit headers,合并进同一 quota 快照并同步状态。Free、 K12 等套餐共用该状态机;套餐只参与账号展示和按套餐隔离的模型目录 cache,不存在 K12 专属额度路径。
- 账号详情的 Token 统计、模型排行和列表 Token 汇总优先使用账号级周额度窗口,无可统计的周窗口时
使用月额度窗口;
usage.windowLabelDisplay随选中的窗口返回“周额度窗口”或“月额度窗口”。查询边界 严格为[resetAt - windowSeconds, resetAt),不是自然周/月或最近 7/30 天;额度刷新若返回了更早的 重置时间,会按新边界重新聚合。没有边界完整、可归属到账号的周/月窗口时显示无数据,标签为 “周/月额度窗口”,不回退到 5 小时、日窗口或历史累计。 金额原值保持完整精度,USD 展示值 小于 1 美元时最多保留四位小数,其余保留两位。
周/月额度预测
GET /api/admin/accounts/quota-forecast?accountId=... 使用管理员鉴权,每次查询返回独立的预测结果。
该接口不刷新上游额度;需要重新观测时,先调用 POST /api/admin/accounts/quota/refresh,再查询预测。
响应 data 包含 accountId、generatedAt 和 forecasts(weekly、monthly):
targetDays、extrapolated:对应周期存在真实账号级窗口时采用实际时长;缺少对应窗口时,使用 可统计的周/月窗口按 7/30 天折算,明确标记extrapolated: true。不把短期限流或模型专属桶当作账号容量。estimatedTokens/estimatedUsd及对应*Display:完整目标周期的近似容量, 公式为样本用量 × 100 / sampledPercent × 目标窗口秒数 / 源窗口秒数。remainingTokens/remainingUsd及对应*Display:额度快照时源窗口的剩余估算, 公式为样本用量 × (100 - usedPercent) / sampledPercent;不随目标周期折算,不代表当前可消费余额。source:源窗口名称label、已用比例usedPercent/usedPercentDisplay、 额度观测时间observedAt/observedAtDisplay、用于过期检查的resetAt,以及选中采样区间 已记录的tokensDisplay/usdDisplay。source: null表示没有可选的源窗口。observedAt表示额度观测时间,与本次查询的generatedAt不同。unavailableReason:不能估算时的说明,正常为null。有效进度少于 5 个百分点不预测;lowSample表示有效进度不足 10 个百分点,或增量采样少于 2 个完整段,仅是质量提示,不承诺精度。incompleteCost/incompleteTokens仅提示费用 / Token 记录存在缺口,不阻断已有数据的预测。 Token 使用已记录的正数用量,费用使用已知的有效 USD 金额;对应数据完全缺失时该项为null, 两项均不可估算时才返回无可用数据的说明。明确记录的零 USD 仍可估算为零,未知费用不能当成零。 不按请求数量补齐未知消耗;漏记、失败或缺少计量可能使估算偏低,不代表完整账单或真实剩余额度。
内部采样优先采用近期分段,条件不足时才使用窗口累计估算。同一额度段内,以历史额度和截至相应
完成时间的累计用量建立基线,每累计至少 5 个百分点形成一段,使用最近 3 个完整段及未满一段的尾部。
上式中的 sampledPercent 是内部有效额度进度(百分点),不是时间进度或对外响应字段。
只对合并区间计算比值,不平均逐请求小分母比值;重复读数不增加段数,大于 1 个百分点的回落或累计
计数倒退会中断采样,不能静默跨越。缓存命中属于输入,不重复相加;其他币种或缺少有效金额不能当成零 USD。
采样查询 [max(resetAt - windowSeconds, accountAddedAt), observedAt) 内开始的请求,
仅把 completedAt <= observedAt 的完整交付用量计入当前分子;历史分子按各点的完成时间累计,
同完成时间使用同一累计值。历史点抽样不缩减区间内的累计用量,接口不返回原始 Provider 文档。
OpenAI 复用已有限流协议解析器匹配额度桶、槽位、时长和明确的套餐。仅允许最多 2 秒的重置时间抖动, 超出或套餐改变则截断历史基线;不将宽时间容差当作上游窗口身份。历史文档缺少独立额度观测时间, 只能以请求完成时间近似配对,不证明上游扣额与本地完成同步。无法积累有效增量时,仅在账号于窗口开始前 已加入且没有已知采样断点的情况下使用累计估算;中途加入账号可以在新基线之后积累,无需等待下次重置。
预测没有独立持久化或调度状态,也不参与账单结算。站外消耗、历史日志清理、异步观测延迟、未成功交付的 上游消耗和模型组合变化仍可能造成误差;等价 USD 费用不是官方订阅价格或固定额度承诺, 30 天折算也不是自然月额度。记录覆盖率不等于预测准确率,不输出未经校准的置信区间。
OpenAI 个人信息
GET /api/admin/accounts/personal-info?accountId=... 需要管理员会话,当前由 OpenAI/Codex OAuth
账号提供。后端并发读取资料统计与订阅,一次返回;每次请求均重新查询,不自动重试或
刷新 credential,不读取本地 usage/billing 记录,也不缓存或估算统计结果。
响应 data 包含:
| 字段 | 类型 | 含义 |
|---|---|---|
profile |
object 或 null | 官方个人资料、累计统计与活动洞察;查询失败为 null |
profileError |
string 或 null | 资料查询失败时的安全错误提示;成功为 null |
subscription |
object 或 null | 当前绑定账号的订阅周期;无可用周期或查询失败为 null |
两部分的查询结果独立:资料失败时仍返回可用订阅,订阅失败时仍返回资料。账号不存在、查询参数不合法或 无管理员会话时,仍返回标准错误;请求期间账号身份或 credential revision 改变时拒绝整份结果。
官方资料与累计统计
profile 包含:
displayName、username、imageUrl:官方账号资料;summary:累计文本 Token、单日峰值 Token、最长任务时长、当前连续天数和最长连续天数;dailyUsage:按日期返回的 Token 活动;activityInsights:快速模式占比、上游原样返回的推理强度及占比、Skill 探索/使用数、聊天总数, 以及插件与 Skill 调用排行。
官方未返回的字段保持 null,不使用本地数据补齐;hasStatsError: true 表示账号资料可用,但官方统计
部分不可用。access token 已过期或官方返回 401 时,通过 profileError 提示先刷新 credential 或重新授权。
订阅信息
后端使用当前凭据、绑定的上游账号 ID 和账号出站代理访问 /backend-api/subscriptions?account_id=...,
不枚举其他账号,不返回上游订阅 ID 或原始响应。
subscription 为 null(未获得可用订阅周期),或包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
startsAt |
RFC 3339 字符串或 null | 上游本期开始时间 |
expiresAt |
RFC 3339 字符串 | 上游本期结束时间,不代表自动续费账号最终失效 |
willRenew |
boolean 或 null | 自动续费状态;未知不推断为 false |
billingPeriod |
string 或 null | 上游计费周期标识 |
billingCurrency |
string 或 null | 上游计费币种 |
observedAt |
RFC 3339 字符串 | 本次查询时间 |
订阅不写入额度快照或数据库,不参与账号状态或调度;单次上游查询最多 5 秒、响应最多 64 KiB,不重试。 上游失败或未提供有效周期时返回未知,不据此标记免费、过期或禁用;请求期间账号身份或 credential revision 变化时丢弃结果。
OpenAI 主动额度重置卡
GET /api/admin/accounts/reset-credits?accountId=... 每次都查询 OpenAI 上游;后端不把卡片列表写入
PostgreSQL 或 Redis。
查询响应:
{
"availableCount": 1,
"credits": [{
"id": "credit_...",
"status": "available",
"title": "...",
"expiresAt": "2026-08-31T12:00:00Z",
"resetType": "..."
}]
}
消费请求的 redeemRequestId 必须是小写、带连字符的 canonical UUIDv4;creditId 可省略,由上游选择
可用卡。一次请求发出后若传输结果不明确,重试必须复用完全相同的 redeemRequestId、creditId 和
账号。服务在单副本进程内按账号串行消费,并在 credential 需要刷新时以同一命令重试一次;它不会对不明
结果自动创建新消费。
若服务无法确认不可逆消费是否完成,返回 HTTP 502 / 业务码 50202;客户端应先刷新卡片与额度状态,
并在确需重试时复用原 redeemRequestId。明确的上游 HTTP 拒绝仍使用 50201,不会误标为结果未知。
{
"accountId": "acct_...",
"creditId": "credit_...",
"redeemRequestId": "8fbf302d-11df-4bd5-82e4-08e4b3df7874"
}
消费响应只返回上游结果 code 和可选 credit。消费端确认成功后应重新 GET 卡片列表,并显式调用
POST /api/admin/accounts/quota/refresh 回读官方额度;不得直接改写本地 resetAt。xAI 不支持该能力。
6. 账号分组
分组是 Provider-neutral 的账号集合;一个组可包含任意 Provider 账号,一个账号也可属于多个组。
| 方法 | 路由 | 主要 query/body | 说明 |
|---|---|---|---|
GET |
/api/admin/account-groups |
page、pageSize、search、enabled |
分页查询分组;返回账号可用性、并发槽位(Redis 不可用时 usedSlots=null)及成功请求 USD 用量 |
POST |
/api/admin/account-groups/create |
{ name, description, color } |
创建空分组;color 严格为 #RRGGBBAA,返回时统一大写 |
POST |
/api/admin/account-groups/update |
{ id, name, description, color } |
更新名称、描述和颜色 |
POST |
/api/admin/account-groups/enable |
{ id } |
启用 |
POST |
/api/admin/account-groups/disable |
{ id } |
禁用;已绑定 Key 保持受限,不回退到全部账号 |
POST |
/api/admin/account-groups/delete |
{ id } |
删除未被 Client Key 引用的组 |
列表数据为 { items, page, configRevision },其中 item 返回 memberCount、按 Provider 聚合的
providerCounts 和 clientKeyCount。查询分组成员使用账号列表的 groupId 筛选,
不提供独立的分组成员路由;账号的 Provider 不代表整个分组的 Provider。
7. Client Key
| 方法 | 路由 | 主要 query/body | 说明 |
|---|---|---|---|
GET |
/api/admin/client-keys |
cursor、limit、search、sortBy、sortDirection |
游标分页查询 |
POST |
/api/admin/client-keys/create |
创建字段 | 创建带账号范围的 Client Key |
GET |
/api/admin/client-keys/reveal |
id |
显式读取完整明文 Key |
POST |
/api/admin/client-keys/update |
更新字段 | 原子更新名称、分组范围和限额 |
POST |
/api/admin/client-keys/enable |
{ id } |
启用 |
POST |
/api/admin/client-keys/disable |
{ id } |
禁用 |
POST |
/api/admin/client-keys/delete |
{ id } |
删除 |
创建字段为 name、可选 label、groupIds、maxConcurrency、requestsPerMinute、可选
dailyLimitUsd、weeklyLimitUsd 和 customKey。更新请求携带 id,不接受 customKey。
groupIds 必须显式提交:空数组派生 routingScope: "all",非空数组派生
routingScope: "groups"。响应同时返回分组引用 groups,以及从当前有效账号池派生、仅供展示的
providerKinds。创建和 reveal 响应会返回完整明文 Key,调用方
必须立即安全保存。
密钥列表的 search 仅匹配名称和标签,不匹配密钥值或可见前缀;搜索不区分大小写,使用字面量前缀匹配。
创建和更新时去除名称首尾空白,并按忽略大小写、首尾空格的名称查重,重复返回 409。
更新排除当前记录,并发写入同样执行查重;失败不会留下审计或配置版本变更。
历史重名数据不自动改名,已有凭据继续有效;再次保存时需使用未被其他密钥占用的名称。
customKey 仅用于创建:省略、null 或空字符串时继续自动生成;非空时按原值保存,不追加前缀、
不截断、不修剪空白,也不要求固定长度。Key 须为 HTTP Bearer 可传输的非空可见 ASCII 字符,
支持标点,空格、控制字符和非 ASCII 文本会返回 400。重复 Key 返回 409,包括并发创建时。
应用不另设 Key 长度上限;创建请求和 Authorization 头仍受 Web 服务器及反向代理的通用大小限制。
更新接口不接受 customKey,防止修改策略时意外替换正在使用的凭据。
列表仅展示最多前 10 个字符,且至少隐藏一半字符;单字符 Key 的可见前缀为空。
完整值仍只通过创建和显式 reveal 返回,不进入普通 Debug 或审计。
自定义 Key 创建示例:
{
"name": "迁入的客户端",
"customKey": "legacy-platform-key/example+=",
"groupIds": [],
"maxConcurrency": 2,
"requestsPerMinute": 0
}
自定义 Key 的分组权限、日/周限额和并发规则按本平台配置执行,不导入其他平台的历史用量。
金额字段为非负十进制字符串,最多 10 位整数与 10 位小数,"0" 表示不限额。
创建时省略金额字段默认为零;更新时省略或 null 保留当前值,修改限额不会清空已用金额。
maxConcurrency 和 requestsPerMinute 是非负整数,零表示不限。
列表返回 dailyLimitUsd、weeklyLimitUsd、dailyUsedUsd、weeklyUsedUsd(均为字符串)、
dailyResetsAt、weeklyResetsAt(RFC3339 或 null)。
记账和限额比较保留完整精度。
日窗口按北京时间零点重置;周窗口从首次准入当天零点起持续七天,到期后在下一次使用时重新开启。
费用按请求完成时间归属窗口。并发按同一 Key 的执行中请求累计,包含 SSE 与每个 WebSocket
response.create;空闲连接不占名额,内部重试不重复占用。
修改 Key 策略对既有 WebSocket 连接的下一次请求同样生效,已开始的请求保持原有快照。
运行设置可以分别启用 Key 与账号的有界排队。Key 并发满时按 Key 等待;账号先使用其它可调度候选,
适用账号均暂时满载后按账号等待。RPM、金额限额、失效账号和上游冷却不通过排队绕过。
队列满返回 429 / concurrency_queue_full,排队超时返回 429 / concurrency_queue_timeout;
WebSocket 使用对应错误事件。等待期间不发送上游请求,取消后释放等待位置,排队重查不重复计入 RPM。
SSE 在取得有效执行前不发送保活帧,因此此阶段保留 HTTP 错误状态;已开始交付的失败沿用流内错误合同。
任一已结算金额达到限额后拒绝新请求,已准入请求可完成并使金额超过阈值。
HTTP 返回 429,error.code 为 key_daily_budget_exceeded 或 key_weekly_budget_exceeded,
并附 Retry-After;WebSocket 每次 response.create 执行相同检查并返回协议错误事件。
只累计上游上报或按用量与模型价格计算出的 USD 费用;无法取得费用的尝试按零累计,
保留错误和用量诊断,不产生待核账记录或阻断。内部重试中已经取得的费用仍会累计。
预算存储不可用时返回 503、key_budget_unavailable。
自动结算按网关请求 ID 幂等执行。账本独立于使用统计日志,记录保留至删除 Key,
不受 usageRetentionDays 影响。
8. 运行设置
| 方法 | 路由 | 说明 |
|---|---|---|
GET |
/api/admin/settings |
读取运行设置 |
POST |
/api/admin/settings/update |
原子替换全部运行设置 |
GET |
/api/admin/settings/client-downloads/codex-desktop/windows |
提取 Codex Desktop Windows 离线安装直链;refresh=true 强制刷新进程内短缓存 |
GET |
/api/admin/settings/admin-api-key |
只返回管理 API Key 是否存在 |
POST |
/api/admin/settings/admin-api-key/delete |
删除管理 API Key |
POST |
/api/admin/settings/admin-api-key/regenerate |
重新生成并一次性返回完整管理 API Key |
设置更新字段包括:
requestLocationEnabled
requestLocation
modelMappings
refreshMarginSeconds
refreshConcurrency
maxConcurrentPerAccount
maxWaitingPerKey
maxWaitingPerAccount
concurrencyWaitTimeoutSeconds
requestIntervalMs
rotationStrategy
minCodexDesktopVersion
minCodexCliVersion
usageRetentionDays
opsEventRetentionDays
auditRetentionDays
accountAutoFreezeEnabled
accountAutoFreezeThreshold
accountAutoFreezeWindowSeconds
accountAutoFreezeDurationSeconds
accountAutoFreezeProbeEnabled
accountAutoFreezeProbeModel
accountAutoFreezeAdaptiveConcurrency
requestLocationEnabled 是必填布尔值,默认 false:关闭时不覆盖客户端原有位置和时区;开启时使用已保存的
requestLocation。关闭不会清空自定义值,代理自定义位置仍优先。
requestLocation 是必填的完整对象 { country, region, city, timezone },不接受 null;初始保存值为
{ country: "US", region: "Ohio", city: "Piketon", timezone: "America/New_York" }。
字段约束与代理位置一致。全局自定义开启后,OpenAI Responses 使用全局位置,
关联代理配置了自定义位置时优先使用代理值。保存后通过现有配置发布机制对新请求生效,
已开始请求及其重试保持同一份全局值;普通文本、绝对时间戳和数据驻留要求不受影响。
maxWaitingPerKey 与 maxWaitingPerAccount 是全局统一的排队容量,取值 0~1,000,默认 0(关闭);
每个 Key、每个账号各自独立计数,没有单对象覆盖字段。执行并发为 5、最大排队数为 5 时,
该对象最多容纳 5 个执行请求与 5 个等待请求。Key 并发为 0(不限)时跳过 Key 排队。
concurrencyWaitTimeoutSeconds 取值 1~120,默认 30,从首次入队开始计时,密钥与账号两层共享该等待时限;
切换账号或内部重试不重新计时,等待同时计入请求总超时。该时限不用于中断已开始的上游生成。
设置更新请求须包含这三个字段,新请求使用更新后的快照。
rotationStrategy 可取 smart、quota_reset_priority、round_robin、sticky。
两个 minCodex*Version 字段为 string | null,只设置最低版本,不存在最大版本字段。
账号自动冻结(accountAutoFreezeEnabled)默认关闭。启用后,在统计窗口内按尝试累计容量类上游错误(server_is_overloaded
等与 5xx 不可用),达到阈值后把该账号冻结为带恢复倒计时的 rate_limited 状态。accountAutoFreezeThreshold
取值 2~1,000(默认 12,按普通请求的 attempt 计数,含请求内同账号重试,不含诊断探测与本地连接保护错误);accountAutoFreezeWindowSeconds
取值 60~3,600(默认 600,随每次失败滑动顺延);accountAutoFreezeDurationSeconds 取值 300~604,800
(默认 7,200,即 2 小时,探测失败后按该时长顺延)。accountAutoFreezeProbeEnabled 开启时恢复 worker
在到期后执行真实探测调用,成功才解冻;探测过程中和服务重启后继续阻止普通请求。关闭自动冻结或探测后,
已有冻结等待当前冷却结束再恢复调度。accountAutoFreezeProbeModel 为 string | null,留空时自动
选择账号可用的第一个模型。accountAutoFreezeAdaptiveConcurrency 开启时冻结期间把账号并发上限下调到
观测在途峰值的 80%(下限 2,只降不升)。这会持久修改账号并发设置;跟随全局默认的账号也会设为独立上限,
解冻后不自动恢复,管理员可手动改回。
Windows 离线包接口固定解析 Microsoft Store Product ID 9PLM9XGG6VKS 的 Retail 包,不接受调用方提供
产品 ID、上游地址、ring 或文件名。后端只返回通过包名、架构、Microsoft CDN host/path、scheme 和失效
时间校验的 x64 / arm64 MSIX 直链,不代理安装包字节。Store 内容通道返回 HTTP/80 临时地址时保留
原始 scheme,不强制改写为该 host 不保证支持的 HTTPS。动态链接不足 10 分钟即失效时不会下发;某个架构
解析失败时只将该架构降级到 OpenAI 官方 HTTPS 稳定 MSIX,并通过 warning 说明。响应形状为:
{
"resolvedAt": "2026-09-01T06:30:00Z",
"cached": false,
"warning": null,
"packages": [
{
"architecture": "x64",
"source": "microsoft_store",
"version": "26.825.6671.0",
"fileName": "OpenAI.Codex_26.825.6671.0_x64__2p2nqsd0c76g0.msix",
"sizeBytes": 744250000,
"downloadUrl": "http://dl.delivery.mp.microsoft.com/filestreamingservice/files/...",
"expiresAt": "2026-09-01T07:30:00Z"
}
]
}
source 为 microsoft_store 或 official_openai。Store 的四段 package version 只用于下载展示,不参与
Desktop 三段 SemVer 门禁,也不会自动回写最低版本设置。门禁规则见
鉴权与公共约定,解析器职责见 架构文档。
9. 备份
全部备份端点位于 /api/admin/settings/backups/*,使用管理接口响应信封,字段为 camelCase,响应带 Cache-Control: no-store。
| 方法 | 路由 | 请求 | 说明 |
|---|---|---|---|
GET |
/api/admin/settings/backups |
无 | 读取存储配置(含明文 Secret)、验证状态与调度配置 |
POST |
/api/admin/settings/backups/storage/update |
S3 配置 | 更新存储配置;secretAccessKey 为空字符串会校验失败 |
POST |
/api/admin/settings/backups/storage/test |
无 | 测试已保存的存储配置(Put/Head/Get/Delete 探针) |
POST |
/api/admin/settings/backups/schedule/update |
调度配置 | 更新 Cron、时区与保留策略 |
GET |
/api/admin/settings/backups/records |
查询参数 | 分页查询备份记录 |
POST |
/api/admin/settings/backups/create |
{ expiresInDays? } |
创建手动备份,返回 202 Accepted;expiresInDays 为过期天数(0 或缺省表示不过期) |
POST |
/api/admin/settings/backups/download-url |
{ backupId } |
创建 5 分钟有效预签名下载地址(仅 completed) |
POST |
/api/admin/settings/backups/delete |
{ backupId } |
请求删除(进入 deleting,由 Worker 收敛硬删除) |
读取设置响应(Secret 以明文返回):
storageRevision, endpoint, region, bucket, accessKeyId, secretAccessKey, prefix,
forcePathStyle, verified, scheduleEnabled, cronExpression, scheduleTimezone,
retentionDays, retentionCount, nextRunAt, lastVerifiedAt, updatedAt
更新存储请求字段:
endpoint, region, bucket, accessKeyId, secretAccessKey, prefix, forcePathStyle
secretAccessKey 为空字符串会校验失败;由于 GET 会回传已保存的明文 Secret,保存时始终整体提交当前值。已有备份记录时,endpoint/region/bucket/forcePathStyle 不允许变化(存储身份锁定,409);只允许轮换凭据与修改 prefix。
保存相同配置保留验证状态、定时计划及配置版本。存储配置实际变化时,会同时使验证失效、暂停定时计划并清空下次运行时间;连接测试通过后需重新启用计划。
更新调度请求字段:
scheduleEnabled, cronExpression, scheduleTimezone, retentionDays, retentionCount
cronExpression 为 5 段格式;retentionDays/retentionCount 为 0 表示禁用对应清理。启用计划前必须已保存完整存储配置且通过连接测试。
记录列表查询参数:
page, pageSize, status, trigger
status 可取 queued/dumping/uploading/completed/failed/deleting;trigger 可取 manual/scheduled。记录响应字段:
id, triggerKind, status, scheduledAt, objectKey, sizeBytes, sha256, attemptCount,
errorCode, errorMessage, startedAt, completedAt, expiresAt, createdAt, updatedAt
expiresAt 在创建时确定:手动备份来自 expiresInDays,计划备份来自当时的
retentionDays;到期后由 Worker 进入删除流程。
连接测试响应:
{ ok, stage, code, message }
stage 为 putObject/headObject/getObject/deleteObject。探测成功后以 storageRevision CAS 写入 lastVerifiedAt;测试期间配置变化则丢弃结果。
备份错误映射(AdminErrorCode 既有体系):
| HTTP | 场景 |
|---|---|
400 |
配置、Cron、时区或状态参数无效 |
404 |
备份记录不存在 |
409 |
已有活跃任务、状态冲突或存储身份锁定 |
502 |
S3 兼容服务返回无效或失败响应 |
503 |
PostgreSQL、pg_dump 或对象存储暂不可用 |
审计动作:backup.s3_config_updated、backup.s3_connection_tested、backup.schedule_updated、backup.created、backup.download_url_created、backup.delete_requested。审计详情与记录表均不保存 Secret、数据库连接串或预签名 URL query。
10. Dashboard、用量与错误
| 方法 | 路由 | 说明 |
|---|---|---|
GET |
/api/admin/dashboard/summary |
Dashboard 汇总;支持 kind、startTime、endTime |
GET |
/api/admin/dashboard/trend |
Dashboard 趋势;`kind=usage |
GET |
/api/admin/usage/records |
请求记录分页列表 |
GET |
/api/admin/usage/records/detail |
按 id 查询请求详情 |
GET |
/api/admin/usage/records/summary |
当前筛选条件的请求汇总 |
GET |
/api/admin/usage/insights/overview |
用量、成本与成功率洞察 |
GET |
/api/admin/usage/insights/diagnostics |
按维度聚合诊断 |
GET |
/api/admin/operations/errors |
运维错误分页列表 |
用量查询可组合页码/游标、时间范围、Provider、Client Key、账号、模型、route、transport、状态码、
request/response/upstream ID、outcome 与搜索文本。诊断 dimension 可取 model、account、
apiKey、provider、transport、failureClass、status。
管理端请求列表与详情分别保留 requestedModel(客户端请求)、upstreamModel(网关发送)与
upstreamResponseModel(上游返回)。返回模型缺失时为 null,不使用请求或映射模型补齐。
OpenAI 优先采用服务端 openai-model / x-openai-model 报告(流内报告可覆盖初始响应头),
没有报告时采用正文明确声明的 response.model;xAI 采用原始正文声明。正文模型以终态优先,
缺少终态声明时保留首次声明。这些值仅表示上游报告,不作为模型真实性证明,也不参与路由、
聚合或本地计价模型选择。历史数据只回填此前已保存的 OpenAI 模型报告,其余保留未知。
请求记录列表的 search 使用字面量前缀匹配,支持请求 ID、Client Key ID / 名称、
账号 ID、账号邮箱与名称、请求 / 上游模型 ID、上游请求 ID。密钥名称不区分大小写,其他字段区分大小写。
密钥名称按当前密钥记录检索,改名后使用新名称,删除后仍可按 Client Key ID 查询历史记录。
账号邮箱与名称按请求记录的历史快照检索,
不随当前账号修改或删除而改变;%、_ 和 \ 均按普通字符处理,不作为搜索通配符。
请求记录按模型执行 ID 或上游 ID 查询;只有入口 ID 时需结合时间与入口日志定位。 请求记录和错误列表支持按密钥名称搜索,不支持密钥值或可见前缀搜索。
已进入模型执行会话、但尚未登记执行尝试的失败请求也进入错误及详情查询, 包含无可用账号、准备失败、启动/准备超时与取消。此时 attempt 数为零,未确认的 Provider、账号及 上游传输为空;可用模型执行 ID 在“全部平台”下查询,不从路由候选推断实际调用平台。 已登记的尝试即使尚未收到首个上游事件,也会保留真实 attempt。 鉴权、解析、路由和准入等入口拒绝不属于该范围;请求观测仍是可能延迟或丢弃的异步投影。
汇总与洞察中的请求数与 outcome 分布覆盖筛选范围内全部请求;token、缓存、延迟与成本聚合仅统计
已完整交付客户端的成功推理响应。OpenAI 的 generate: false 连接与上下文准备记录归类为
requestKind: "prewarm",不进入用量列表、账号用量或额度预测的 Token / 费用覆盖统计,但仍可按
请求 ID 读取审计详情,响应中的额度观测仍可用于预测配对。此分类以实际 generate 字段为准,
不能仅凭客户端的同名 metadata 或输出 Token 为零排除普通推理;其他 Provider 不套用该规则。
详情接口按 id 可读取成功、失败或未完成请求,返回 trace(未采集记录为 null)和
relatedRequests[](requestId / relation / outcome / completedAt);relation 为 recovered_by 或
recovers。trace 是执行终态时的有界脱敏时间线,包含 request、attempt 和 exchange 关联、阶段、
事件摘要及淘汰计数;普通用量列表不携带此字段。
新采集的未知 JSON 键名与值只保留结构和摘要;事件摘要中的 eventType 为已知事件名称字符串、
未知名称的 { bytes, sha256 } 摘要,或缺失时的 null。旧 trace 不做清理或回填,
其中的 sanitized 标记不能作为可直接公开的保证。
管理端下载的诊断包 schemaVersion: 2 用于人工反馈,不是备份或导入格式。它包含关联 ID、错误分类摘要、
请求与错误事件各自的状态、attempt、时间线阶段和计时;不自动导出 message/raw error、任意 metadata、
trace event data、请求响应正文和头部。availability 与 omitted 明示未采集、不完整或主动省略的内容,
null 不代表没有发生错误。版本、环境及原始错误片段仍需操作者另行补充并审阅脱敏。
错误记录中的“已自动恢复”表示系统关联到了后续成功请求,不会把原来的失败记录改为成功。
upstreamSendState = ambiguous 表示无法确认该次上游执行结果,不代表后续恢复请求失败;
恢复关联也不等于逐字节验证过两次请求正文。
Dashboard 的 accountUsage[] 由后端提供 usageWindow、metricLabel、metricValue。
usageWindow 复用账号额度窗口合同,缺失额度事实时为 null;窗口标签、百分比、触顶状态、重置时间
和本地用量由 Provider/Admin 投影。前端不得从套餐缺失推断免费套餐,也不得从显示时舍入的百分比推断
触顶。滚动窗口使用相应时间范围的本地用量,独立于 Dashboard 的今日统计范围。
OpenAI 与 xAI 的本地费用估算按实际发送给上游的请求模型(upstreamModel)查价,结合响应中的实际
用量计算;客户端请求 A、路由后发送 B 时按 B 计价,响应返回 C 不改变计价模型。实际发送模型缺少
定价时不估算,也不借用响应模型的价格。Provider 明确上报的已计费金额仍优先于本地估算。
OpenAI Responses 用量记录的 serviceTier 与本地费用估算统一采用 Provider 最终发给上游的请求
service_tier,不使用响应档位覆盖或回退。例如发送 priority、响应回显 default 时,仍显示
Fast 并按 Priority 价格估算。未发送档位时,serviceTier 保持缺失,展示与估算按标准档处理。
计费展示把 priority/fast 映射为 Fast,flex 映射为 Flex,缺失或 default/standard
映射为 Standard;其他非空值以首字母大写展示。各模型、档位及长上下文区间使用明确登记的价格;缺少对应
价格时不估算(包括 auto 和未知档位),不以固定倍数兜底。展示的倍率由所选档位与标准档位费用之比
计算,托管工具调用费不随 Token 档位倍增。
Provider metadata 分别保留 requestedServiceTier 与 upstreamServiceTier 供诊断;发送给客户端的
原始 response.service_tier 不变。用量中的 Fast 仅表示发送档位,不能证明上游实际加速,本地费用
估算也不能代替官方账单。档位与费用在请求记录生成时确定;查询不会回填历史档位或重算已存储费用。
本地计价规则覆盖尚在服务的型号。已关闭型号或缺少计价规则时,不生成新的本地估价;已存储的历史费用 保留原值,但缺少规则时无法补充费用拆分。
11. 版本、更新与重启
| 方法 | 路由 | 主要 query/body | 说明 |
|---|---|---|---|
GET |
/api/admin/system/version |
无 | 当前构建、部署模式和可用更新 |
GET |
/api/admin/system/update/detail |
`refresh=true | false` |
GET |
/api/admin/system/update/events |
无 | SSE 更新事件流 |
POST |
/api/admin/system/update |
可选 { targetVersion } |
开始在线更新 |
GET |
/api/admin/system/update/status |
无 | 查询当前更新或回滚状态 |
POST |
/api/admin/system/rollback |
无 | 回滚到保留的上一版本 |
POST |
/api/admin/system/restart |
无 | 请求进程重启 |
在线更新仅在当前部署模式、Release 资产和进程重启能力都满足要求时可用,且只在同一 major 版本内
提供:跨大版本目标会以 40901 冲突拒绝,需按发布说明重新部署。
实例升级和仓库发版见 部署文档。