usage 对象,报告该请求消耗的令牌。Sunra 按您所调用 endpoint 的语义报告用量:Messages 响应遵循 Anthropic 契约,Chat Completions 或 Responses 响应遵循 OpenAI 契约。
这些结构不同,而且是刻意为之。读取 Chat Completions 响应的 OpenAI SDK 会假定 OpenAI 语义,读取 Messages 响应的 Anthropic SDK 会假定 Anthropic 语义。每个 endpoint 都遵守自身规范所承诺的内容,而不是被强行统一成同一种结构。
Messages
在/v1/messages 上,三个输入桶是互斥的。没有任何令牌被重复计数,因此提示总量就是它们之和:
integer
仅为全新输入令牌。不含两个缓存桶。
integer
本次请求写入缓存的令牌数。
integer
从缓存提供给本次请求的令牌数。
integer
模型生成的令牌数。
integer
三个输入桶加上
output_tokens。在非 streaming 响应中存在。null 的桶按零计。
实例演算
同一段 19,000 令牌的前缀向claude-opus-4-8 发送两次。第一次请求写入缓存,第二次请求读取缓存。
冷请求(写缓存)
暖请求(读缓存)
input_tokens 都保持为 8,因为每次真正新增的输入就是这 8 个令牌。那段 19,349 令牌的前缀从创建桶转移到了读取桶。两次请求的总令牌数同为 19,359,但成本并不相同:缓存写入和缓存读取各按自己的单令牌价格计价,这正是它们被分成独立桶上报的原因。
Chat Completions 和 Responses
这些 endpoint 原样上报 OpenAI 语义。提示计数是整个提示,缓存令牌是其中的一个子集,另行单独上报。/v1/chat/completions
prompt_tokens 和 cached_tokens 相加会重复计数。要在这些 endpoint 上得到全新输入,需要做减法:
/v1/responses 的 input_tokens 和 input_tokens_details.cached_tokens。
sunra_usage_semantics 标记
经 Sunra 归一化处理的响应会在 usage 内带有一个标记:
usage 对象内;在 streaming 的响应中,它出现在携带输入桶的那个事件 message_start 上。当它以该值出现时,本页的各项保证成立:三个输入桶互斥,且 total_tokens(在存在时)等于它们之和加上 output_tokens。
它的缺失同样是一项承诺。 Sunra 只对自己实测过的响应结构做归一化。其余响应都会原封不动地从上游服务商转发出来,不带标记;不带标记的响应,就是 Sunra 不为其各个桶背书的响应。
请对该标记做断言,而不要通过查看数字来猜测某个响应遵循哪种约定。这个字段存在的意义正是取代结构嗅探——诸如”这些桶相加超过了提示计数,所以它们一定是互斥的”这类启发式判断,可以被不止一种约定同时满足,迟早会把某个响应读错。
/v1/messages 的响应才会带这个标记。Chat Completions 和 Responses 遵循 OpenAI 规范,不带标记。
该值带有版本。对这些字段含义的破坏性变更会以新的值发布,因此对 anthropic.exclusive.v1 做等值检查,不会在无声无息中开始读取另一套契约。
Streaming
在 streaming 的 Messages 请求中,用量分两个事件到达。message_start 携带输入侧和标记。末尾的 message_delta 只携带 output_tokens。请对 message_start 中的标记做断言,然后按通常的方式把两个事件合并——用后一个事件的 usage 覆盖前一个——即可得到上文记录的取值。
streaming 响应中会省略 total_tokens。没有哪个单独的事件同时知道输入侧和输出侧,因此在流进行过程中算出的任何总数都会是错的。请在流结束后自行把各个桶相加。
从旧行为迁移
Sunra 此前在/v1/messages 上原样转发上游的 usage 对象。部分服务商前面的翻译层会把缓存创建令牌折叠进 input_tokens,因此按 Anthropic 契约把三个桶相加的调用方,会把缓存创建令牌计算两次。
- 如果您把三个桶相加,正如 Anthropic 契约所描述的那样,那么您现在是正确的。您这边无需改动。
- 如果您曾为旧行为做过补偿——自行从
input_tokens中减去cache_creation_input_tokens,或以其他方式逆向还原这种折叠——请停止。该修正现在减掉的是本已被排除的令牌,会低估您的输入。 - 如果您读取
total_tokens,它仍然报告完整计数:全新输入、两个缓存桶以及输出。