TokenGateway统一网关 · 接入文档
base_url = v0.14.0

01这是什么

一个内部统一的 Token API 入口。你的产品只对接它一个地址、一把我们签发的密钥、一套不随上游变化的模型名; 背后换供应商、换模型版本、加兜底渠道,你的代码不动。

第 1 层产品自己的计费你的最终用户的套餐、余额、扣费。你自己实现,依据本网关回传的用量。
第 2 层 ← 这里TokenGateway租户身份、逻辑模型名、用量账本与归因、预算与限流、跨模型兜底。
第 3 层new-api上游适配、渠道与 key 池、同模型换渠道、健康检查。

网关不认识「最终用户」这个业务概念,只记录你传来的标识。用户属于你的产品,不属于网关。

0230 秒接入

三件事:地址换成网关、密钥换成网关签发的、model 换成给你的逻辑名。请求体格式不变, OpenAI 和 Anthropic 两种写法都收Authorization: BearerX-Api-Key 两种鉴权头也都收。

curl http://127.0.0.1:8080/v1/chat/completions \
  -H "Authorization: Bearer tg-prod-你的密钥" \
  -H "Content-Type: application/json" \
  -H "X-TG-End-User: u_10231" \
  -H "X-TG-Session: s_88fa1" \
  -H "X-TG-Feature: chat" \
  -d '{"model":"你的逻辑模型名","messages":[{"role":"user","content":"hi"}]}'

不知道自己的逻辑模型名和限额?不用问人 —— 下一节把 key 贴进去,网关会当场告诉你。

03自检(用你的 key)

密钥只留在这个浏览器标签页里(sessionStorage),只发给当前这个网关地址,不落任何日志、不进账本。

04归因三件套

三个可选的请求头。强烈建议带上,否则报表里全部归入「未标注」, 而「这个月哪个用户花了多少」是你自己给用户计费时唯一的依据 —— 网关只能记录你告诉它的东西。

放什么用来回答
X-TG-End-User你的用户标识(可以是哈希,网关不需要看懂)这个用户这个月花了多少
X-TG-Session会话 / 对话 id一次对话烧了多少、哪一轮变贵了
X-TG-Feature你产品内部的功能名,如 code-review哪个功能在烧钱

已经在用 OpenAI 的 user 字段或 Anthropic 的 metadata.user_id 的, 网关会自动取用,不必额外加头。三个值都会被截断到合理长度。

别把真名真邮箱发进来。网关的账本是内部成本账,不是用户资料库; 发一个你自己能反查的稳定标识就够了,反查留在你自己那一层。

05SDK 写法

不需要任何网关专用的 SDK。官方 SDK 改两个参数就行 —— 下面的代码会在你上一节自检成功后自动填上你的 key 和模型名

from openai import OpenAI

client = OpenAI(
    api_key="tg-prod-你的密钥",
    base_url="http://127.0.0.1:8080/v1",
    default_headers={"X-TG-Feature": "chat"},   # 整个客户端共用的功能名
)

r = client.chat.completions.with_raw_response.create(
    model="你的逻辑模型名",
    messages=[{"role": "user", "content": "hi"}],
    user="u_10231",                              # 网关自动当成 X-TG-End-User
    extra_headers={"X-TG-Session": "s_88fa1"},   # 单次请求的会话 id
)
print(r.headers.get("X-TG-Request-Id"))          # 对账用,出问题报这个
print(r.headers.get("X-TG-Cost-USD"))            # 非流式才有
print(r.parse().choices[0].message.content)

06响应头与错误码

响应头

何时出现含义
X-TG-Request-Id总是网关侧的请求标识。存下来 —— 对账、查成本、报障都靠它
X-TG-Upstream-Model转发成功时实际使用的上游模型。兜底发生时会和你请求的不是同一个
X-TG-Cost-USD仅非流式本次请求的成本
X-TG-Stub桩渠道这条响应的用量是编的,别拿去计费

错误

错误体同时满足两家 SDK 的解析习惯,所以你原来的错误处理不用改:

{"type":"error","error":{"type":"rate_limit_error","message":"..."},"request_id":"tg-..."}
状态码含义你该怎么做
401密钥无效检查配置,不要重试
403模型未对该密钥开放找平台开通,不要重试
402租户预算用尽告警,不要重试
404这条路径网关不转发检查 URL,不要重试
413请求体超限裁剪上下文
429触发速率或并发限制退避后重试
502上游不可用且网关的重试已用尽退避后重试
503网关正在重启,你的请求被安全地拒了Retry-After 重试

HTTP 200 不等于模型回答了你。账本里 stop_reasonrefusal 表示模型拒答 —— 这也是 200。只看状态码的产品会把它当成正常回答。

07用量与对账

四条只读接口,用你自己的产品 key 就能查,不需要管理密钥。结果严格限定在你自己的租户和环境内 —— 查别人的 request id 和查一个不存在的 id,返回完全一样的 404。

接口用途
GET /v1/models可用逻辑模型、价格、本 key 的限额与预算剩余(就是自检那一节读的)
GET /v1/usage/{request_id}查一次请求的用量与成本
GET /v1/usage?limit=N最近的记录,默认 50、上限 500。来自内存环形缓冲,进程重启会清空
GET /v1/usage/summary按时间范围汇总。读账本文件(含轮转归档,留 30 天),月末结账靠它
# 九月每个用户花了多少 —— 你给自己的用户计费就用这条
curl -s "http://127.0.0.1:8080/v1/usage/summary?from=2026-09-01&to=2026-09-30&group_by=end_user" \
  -H "Authorization: Bearer tg-prod-你的密钥"

group_by 可取 end_user / feature / session / model / day / hour;还能用 end_user=feature=model= 过滤。默认最近 24 小时,最长 62 天。

对账时必须知道的四件事

  • 裸日期作 to 含当天整天from=2026-09-01&to=2026-09-30 覆盖整个九月。 写成 RFC 3339 则精确到那个时刻、不含该时刻。日期按服务器本地时区解释,响应里 time_zone 会标明。
  • request_id 去重。账本是幂等可重放的。
  • cost_usd 不含桩记录。桩单列在 stub_requests / stub_cost_usd,永远不混进成本。
  • 零成本不一定是免费usage_missing 是上游没报用量;usage_suspect 是正文明明有内容却被报成 0 输出 token。 后者的输入成本计入 cost_usd,同时以 suspect_cost_usd 摆在旁边 —— 你要是不认这部分钱,原样减掉即可。

为什么把可疑的钱摆出来而不是悄悄扣掉:悄悄扣掉的话,你的账和我们的账对不上,而且没有任何线索说明差在哪。

08几条规矩

逻辑模型名:<租户>-<你自己的档位名>

zurv-chat        ← 你的代码里写这个
     ↓             这行映射随时可改,只影响你一家
gpt-5.3-codex-spark  ← 厂商 / 版本 / 端点,你不该知道也不该依赖

档位词汇由你自己定,不强求和别家统一。两个产品各自指向同一个上游模型,那是两行配置, 重复是故意的 —— 否则你改一行会把别人一起换掉,而他没要求过。

  • 别把上游真实模型名硬编码进代码。上游停服、下架、涨价时要改的是网关一行配置,不是你重新发一次版。
  • 别把网关 key 放进客户端。它代表整个租户的额度,不是某个用户的凭据。
  • 预算是近似的。成本只有请求结束才知道,已经在飞的请求可能把租户带过线 —— 预算拦的是下一个请求,不是当前这个。
  • 模型换了会提前通知你,带到秒的时间戳。反过来,你看到账单形态突变先来问,别自己猜。
  • 报障请带 X-TG-Request-Id。没有它,我们只能在几万行账本里猜你是哪一条。

09申请一把 key

密钥由网关侧签发,签发和轮换都不需要重启网关,所以要一把 key 不会打扰到任何人的线上流量。

把这几项发给 TokenGateway 席(运维台在这台机器上):

产品 / 租户 id:  zurv            (小写、短横线,会成为你模型名的前缀)
环境:            prod / staging   (分开发,额度和账目也分开)
预计速率:        600 rpm、并发 64
月预算上限:      500 USD          (到顶返回 402,不会静默超支)
需要什么模型:    一个便宜快的 + 一个强的,各自用途一句话
调用方式:        OpenAI 格式 / Anthropic 格式,流式与否

档位名(-chat-lite 之类)你自己定,因为「用途」是你的用途, 网关看不见你产品内部,不该替你分类。

找不到人怎么办

你能打开这个页面,说明你已经在这台机器上,或者开着一条到它的隧道。 那么挡在你和一把 key 之间的其实只有管理密钥这一件东西,不是某个人在不在线。

  • 手上有管理密钥:不用等任何人 —— 打开 /admin/ → 租户 → 新建租户/环境, 当场签发,不重启进程,密钥完整值只在签发那一次的响应里出现。轮换、停用同一个地方。
  • 没有管理密钥:把上面那段信息发给持有它的人。签发是一分钟的事, 难的是决定给你多少额度 —— 所以把预算和用途写清楚,比催人有用。

拿到 key 之后回到第 3 节贴进去,当场就能验证它通不通。