使用文档

一个端点,
所有模型。

Tare 是挡在你所有模型前面的一层 OpenAI 兼容网关 —— 我们的,和你自己带来的。一个 base URL、一把 key,每一个 token 都算得清。

运维错误码

错误码

错误体是 OpenAI 形状({"error": {"message": …, "type": …}}),SDK 读的就是这个形状; 换成别的形状,客户端日志里会退化成一句 undefined。

状态码含义处理方式
401key 缺失、格式错误或已吊销检查 header。401 只关于凭据本身
403凭据有效,但这次不许过Key 类型与接口不匹配、额度用尽、或来源 IP 不在该 Key 的白名单上 —— 看 metadata.refusal_reason
404这个模型没有配置路由改模型名、加一条路由或加一条兜底
402账号欠费(仅此一种含义)充值,恢复是自动的
429超过每分钟请求数按 Retry-After 退避,见限流与配额
503有路由,但渠道全部失败重试,不是配置问题

三种「余额不足」的区分

⚠️ 额度用尽是 403,不是 402。 402 只表示账号欠费。两个码的分工是「谁的问题」:402 是 账号对平台的欠款,403 是治理阈值(key 额度、月度预算)拦下的。

下表是唯一的权威口径:

情形HTTPerror.type / metadata.error_typemetadata.refusal_reason
这把 key 的累计额度用尽403token_limit_exceededkey_credit_exhausted
账号月度预算到顶403token_limit_exceededbudget_exhausted
账号欠费402payment_requiredaccount_balance_insufficient
调用来源不在该 key 的 IP 白名单上403permission_deniedsource_ip_not_allowed

平台型客户要把原因转述给终端用户时,判断依据是 refusal_reason,不是文案:

refusal_reason含义对终端用户的说法
key_credit_exhausted这把 key 的累计额度用完「你的额度已用完」,充值可解决
budget_exhausted账号月度预算到顶与他无关,不要引导充值
account_balance_insufficient账号欠费与他无关,不要引导充值
source_ip_not_allowed来源地址不在这把 key 的名单上与钱无关:换一台在名单上的机器重试,或请 key 的持有者把这台加进名单

中间两种若说成「你的额度用完了」,用户会为与自己无关的事付费;最后一种说成额度问题, 他会去给一个根本不缺钱的账号充值。

Code / Terminal
{"error":{"code":403,"type":"token_limit_exceeded","message":"...","metadata":{
  "error_type":"token_limit_exceeded",
  "refusal_reason":"key_credit_exhausted"}}}

两种响应信封

接口成功失败
/v1/chat/completions、/v1/keys/*、/v1/models、/v1/generationOpenAI 形状HTTP 非 2xx + {"error":{…}}
/v1/integration/*、/v1/content-settings*{"code","message","data","success"}HTTP 非 2xx,body 仍是这个信封(不是 200 + success:false)

⚠️ 经由 api.jamerly.dev 网关访问对账接口时,网关不拆上游信封,业务数据会嵌两层 (data.data);直连 tare.jamerly.ai 只有一层。判据是 data 里是否又套了一个 code/success,而不是部署位置——两种部署下这条都成立。

错误体的形状

Code / Terminal
{"error":{
  "code": 403,
  "type": "token_limit_exceeded",
  "message": "...",
  "metadata": {
    "error_type": "token_limit_exceeded",
    "refusal_reason": "key_credit_exhausted",
    "provider_code": "503",
    "provider_raw": "{\"error\":{\"code\":\"model_not_found\",\"message\":\"...\"}}"
  }}}
字段保证
error.code始终等于 HTTP 状态码,按它分支是安全的
error.type始终存在,OpenAI SDK 读这一格
metadata.error_type与 error.type 同值,有些 SDK 只读这一格
metadata.refusal_reason只在上述三种拒绝时出现
metadata.provider_code上游原始状态码,透传,可能缺失
metadata.provider_raw上游回包原文,未做任何改动。 不解析、不摘要、不过滤。仅在请求未发出时不存在该字段

[!NOTE] error.message 由 Tare 生成:说明这属于哪一类失败、上游返回了什么状态码。 Tare 不解析上游的错误体来生成该字段 —— 各家错误结构不一致,推断哪个字段承载说明 信息一旦出错不会报错,只会产生一段措辞确定但内容错误的描述。

上游返回的原文原样保留在 metadata.provider_raw 中,可直接用于排查,或原样提供给 源厂商。唯一的例外:失败源于本次请求本身时(工具 schema 有误、参数越界), message 中会一并带上上游的解释 —— 仅返回「请求无效」不足以定位问题。

[!NOTE] 顶层 type 与 metadata.error_type 是同值、刻意重复:两边生态读的不是同一格。 只给后者时,按 OpenAI SDK 写的客户端会拿到 undefined——不报错,只是把一个明确的拒绝 显示成「未知错误」。

error_type 的取值保持不变,细分是新增在 refusal_reason 上的。

404 与 503 的区分

「没配路由」改配置能解决,「上游全部失败」等待能解决。合并成一个状态码会把人引向错误的 方向,这也是兜底路由只补 404 一侧、不补 503 一侧的原因。

[!NOTE] 这里曾经是 402。402 表示「付款后可继续」,而这个错误付多少钱都不会好——客户会先充值、 再查余额,最后才发现是模型名写错了。

有路由但模态不匹配

返回 404,但消息不同:配置的上游不接受图片(或音频、视频)。解决方式是在路由上声明模态, 或改用支持该输入的模型。

文档最后更新于 2026 年 9 月 20 日 06:31(UTC+8)