这次中转修了什么:Claude Code 读图、更准的计费、少一些失败的请求
· api, billing, claude-code, images, topxai
Claude Code 读图在非 Claude 模型上不再失败;上游没出图不收费;修了 Claude 缓存的重复计费;以及几类不再失败的请求。
这几天我们把中转服务里协议转换、流式输出和计费的代码逐项过了一遍,10 月 23 日发了两个版本。修的大多是这类请求:客户端用一种协议,模型用另一种,转换时出了错,请求就失败了。也有几处改的是收费,方向都是往实际发生的用量靠。模型目录和价格表没变,客户端不用改任何东西。全部改动列在更新日志里,这篇只讲你最可能碰到的几处。
Claude Code 在 Kimi、DeepSeek、GLM、Grok 上读图
Claude Code 用的是 Anthropic 的 Messages 接口。它读一张 PNG、或者截一张屏,图片会放在 tool_result 块里返回。Kimi、DeepSeek、GLM、Grok 走的是 Chat Completions,那边的 tool 消息只能放文字,所以以前中转服务直接返回 400,会话就卡在这一步。
现在,工具结果里的文字留在 tool 消息上,图片挪到紧跟在工具结果后面的那条 user 消息里。这样几个并行的工具结果仍然挨在一起,符合 Chat Completions 的要求。GLM-5.3-Abliterated 只读文字,所以在 /v1/messages 上,每张图片、每个文档都会换成它在 Chat Completions 上本来就会收到的那句说明:[Attachment omitted: this model accepts text only.]。tool_result 里放 PDF 之类的文档,在这几个模型上仍然返回 400,文档请发给 Claude 或 gpt-6.1-sol。
长会话的转换也快了。给每个工具结果找名字时,原来的转换代码每处理一个结果都要把整段对话重读一遍,一段 400 轮的 Claude Code 会话,单是转换就要大约 1 秒。现在是 21 毫秒。
计费上的改动
先说 Claude 的缓存。Claude 把命中缓存的 token 和 input_tokens 分开报,prompt 全部命中缓存时,input_tokens 是 0。流在最终用量到达之前就结束的话,中转服务会把这个 0 当成"没报",按输入价补上一份整段 prompt 的估算,叠加在缓存读取费用之上,等于缓存那部分又收了一次。现在只有缓存 token 也是 0 时,才认为输入没报。
图片按张计费,张数现在按实际返回的内容来数。只有 revised_prompt 的条目不算一张图;同一张图拆成一条 url、一条 base64 返回,只收一张的钱。一张图都没有的结果按错误返回,不扣费,MCP 的调用费也一起退回。以前这种结果会按你请求的 n 张收费。如果供应商为这次尝试向我们收了钱,这笔由我们承担。
再说 OpenAI 的缓存时长。OpenAI 的 Responses 接口允许请求用 prompt_cache_options.ttl: "12h" 要 12 小时的提示缓存。按 OpenAI 的说明,这种缓存的写入价是未缓存输入价的 2.5 倍,默认 30 分钟写入是 1.25 倍,正好差一倍;而 TopxAI 每个模型只卖一档缓存写入价。所以中转服务不再把 ttl 转给 OpenAI,Kimi 的 1 小时选项原来就是这样处理的。你的缓存写入都是默认那种,按模型页上的价格算,缓存读取不受影响。
不再失败的请求
- 连续两条带工具调用的 assistant 消息发给 Claude 时,原来只保留后一条的调用,前一条的工具结果找不到对应的调用,Claude 就返回 400。
- Responses 的输入以模型自己的回答结尾时(有的客户端重试流式请求会这样发),原来会当作 prefill 发给 Claude。Claude 5.x 不接受 prefill,现在这段回答不会再放进请求。
- Responses 的工具输出如果是一组内容块(文字加图片),原来按 Responses 的格式原样发给 Claude,会被拒收;现在会转成文字块和图片块。
- Kimi K3 可以在对话中途用一条带
tools的 system 消息声明工具。原来这个字段会被中转服务丢掉,之后的tool_choice: "required"就因为找不到工具而失败。 - 在
/v1/messages上,消息级的output_config和safeguards现在会转给 Claude;多行anthropic-beta请求头会全部转发,不再只转第一行。 - Chat 上游把最后一个推理 token 和第一段正文放在同一个分片里时,
/v1/messages的客户端原来会丢掉这段正文。
慢连接和大事件
流式输出原来每写一次,都只给 30 秒的期限。生成的 2K 图片是一个好几 MB 的事件,网速低于每秒约 100 KB 的连接来不及收完,写入就会失败,客户端看到的是断开的流,而这次请求已经扣了费。现在每个流能用的写入时间按已经发出的数据量计算:按每秒 8 KiB 的读取速度估出所需时间,再加 30 秒,单次写入最多 30 分钟。客户端如果彻底不读了,到时间仍然会断开。
安全
只读的密钥接口 /api/usage/token/ 和 /api/log/token 现在也检查密钥的 IP 白名单,和 /v1 一样。中转服务改用 Go 1.26.10 编译,包含 1.26.6 之后标准库在 TLS、证书校验和 HTTP 上的修复。中转服务跟随重定向时(比如下载生成好的视频),如果从 https 跳到明文 http,不再带上凭证;跳到别的主机时,也不再带上供应商的密钥请求头。
你需要做什么
大多数情况下什么都不用做。设置了 IP 白名单的密钥,用量和日志接口现在也会拒绝白名单以外的地址,和 /v1 一样。给 GPT 设了 prompt_cache_options.ttl 的,写入现在都是默认那种。在 GLM-5.3-Abliterated 上,带图片的 /v1/messages 请求现在会带着那句说明正常返回,不再报 400。
相关
- 更新日志
- Claude Code 接 TopxAI:ANTHROPIC_BASE_URL 和密钥
- 请求怎么计费
- 图片生成
- API 参考:全部端点,附 OpenAPI 规范
- TopxAI 上线 Jev 1.14:能看图、能框位置,图片怎么计费
- 在 TopxAI 升级到 Claude Sonnet 5.5
- 在 TopxAI 升级到 GPT-6.1 Sol
其他语言:English