Help us shape the future of Go! Take the 2026 Go Developer Survey

tokenkit

package module
v0.0.0-...-7d8b912 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

README

TokenKit

TokenKit 是从 new-api 中拆分出的独立 Go 模块,用来在不调用上游 API 的情况下,计算或估算大模型请求的 token 数。

它只做计数,不负责解析请求、下载图片或计费。调用方传入文本,或者图片的尺寸和 detail 设置,TokenKit 返回 token 数。可以脱离 new-api 主模块,嵌入其他 Go 网关、代理或客户端。

能力

  • GPT 和 o 系列模型用 tiktoken 精确计数,GPT-4 和 GPT-3.5 走 cl100k_base,其余走 o200k_base
  • Claude、Gemini 和其他模型按字符类别加权估算,Claude Opus 4.7 前后的两套 tokenizer 分开处理
  • 按各家公开的尺寸规则计算图片 token,包括 OpenAI 的 detail 级别和 Gemini 3 的 media resolution
  • 不依赖 new-api 主模块、Gin、数据库或全局设置

准确度

文本权重是用真实 agent 会话校准的:从 Claude Code 和 Codex 的会话里取了 54 段,包括工具定义、工具调用和工具输出,和上游返回的 usage 逐条对比。

模型 测试模型 prompt 偏差 / 平均误差 输出文本 偏差 / 平均误差
Claude(4.7 之前) claude-haiku-4-5 +0.5% / 3.3% −0.6% / 3.8%
Claude(4.7 及以后) claude-sonnet-5 +0.3% / 3.5% −1.1% / 4.3%
GPT(估算) gpt-5.6-luna +0.9% / 2.9% −1.8% / 5.3%
Gemini gemini-3.7-flash +0.7% / 4.2% −1.3% / 4.7%

GPT 用 Count 时和 tiktoken 基本一致。有两处已知差异:

  • 只含缩进空格的空行会多算 1 个 token,来自 tiktoken-go v0.8.1 的分词正则,1.2 MB 的 agent 文本上一共多 3 个。
  • 超过 1024 字节、中间没有分隔的连续片段(例如没有标点的长段汉字、长串空格)会被切开再计数,误差不到 0.5%。不切的话,BPE 的耗时随片段长度平方增长:10 万个连续汉字要 20 秒左右,切开后约 0.1 秒。

估算适合 agent 流量:代码、JSON、命令输出和中英文混排。几百 token 的短文本误差会大一些。

图片规则和上游实测一致:OpenAI 的 patch 和 tile 计算、Claude 两个分辨率档位、Gemini 2.x 的切块。例外和注意事项见图片。

安装

TokenKit 要求 Go 1.26 或更高版本。

go get github.com/QuantumNous/new-api/tokenkit@latest

快速开始

package main

import (
	"fmt"

	"github.com/QuantumNous/new-api/tokenkit"
)

func main() {
	prompt := "读取 main.go 并解释 init 函数"

	// GPT 用 tiktoken 精确计数,其他模型估算
	fmt.Println(tokenkit.Count("gpt-5.6-sol", prompt))
	fmt.Println(tokenkit.Count("claude-opus-5-5", prompt))

	// 只要估算,不跑 tokenizer,比如流式输出中途断开时补算 completion
	fmt.Println(tokenkit.Estimate("gpt-5.6-sol", prompt))

	// 一张 1920×1080 的截图
	shot := tokenkit.Image{Width: 1920, Height: 1080}
	fmt.Println(tokenkit.ImageTokens("claude-opus-5-5", shot))  // 2694
	fmt.Println(tokenkit.ImageTokens("claude-haiku-4-5", shot)) // 1564
	shot.Detail = "low"
	fmt.Println(tokenkit.ImageTokens("gpt-5.6-sol", shot)) // 173
}

模型分组

ModelFamily 按模型名分组,不区分大小写,允许带厂商前缀和日期后缀,例如 us.anthropic.claude-opus-4-7-v1:0、claude-sonnet-4-5-20250929。

分组 匹配 文本
FamilyOpenAI 名称含 gpt-、o1、o3、o4、chatgpt Count 精确,Estimate 估算
FamilyClaude Claude 4.7 之前,例如 Haiku 4.5、Sonnet 4.6 估算
FamilyClaude47 Claude 4.7 及以后,以及读不出版本号的名称,例如 Opus 4.7、Sonnet 5、Fable、Mythos 估算
FamilyGemini 名称含 gemini 估算
FamilyUnknown 其他模型 估算,权重未校准

Claude 4.7 及以后的 tokenizer 对同样的文本多出约 30% 的 token,所以两组 Claude 用不同的权重。

图片

ImageTokens(model, Image) 返回一张图片占用的 prompt token。Image.Width 和 Image.Height 是像素尺寸,为 0 时按 1024×1024 计算。ImageNeedsSize 判断某个模型和 detail 设置下是否需要尺寸;返回 false 时可以不读取图片。

模型 计算方式
gpt-5.2 及以后、gpt-6 32px patch × 1.2,按模型和 detail 缩放;不传 detail 时 gpt-5.5 及以后按 original 计算
gpt-5、gpt-5.1、gpt-4o、gpt-4.1、o1、o3 512px tile,加 base tokens
Claude 4.7 之前 ⌈宽/28⌉ × ⌈高/28⌉,最长边 1568,最多 1568 个 patch,另加 4
Claude 4.7 及以后 同上,最长边 2576,最多 4784 个 patch,另加 3
Gemini 3 与尺寸无关,按 media resolution:280 / 560 / 1120 / 2240,默认 1120
Gemini 2.x 两边都不超过 384 时 258;更大的图切成方块,每块 258,最多 12 块,另加 1 块缩略图
其他模型 每张 520

Image.Detail 填 OpenAI 的 detail(low、high、auto、original),或 Gemini 的 media resolution(MEDIA_RESOLUTION_LOW 等)。两类值只在对应的模型上生效,例如 Gemini 会忽略 low。

注意事项:

  • 有两处按上游实际计费,而不是按官方文档。gpt-5.5 和 gpt-5.6 的 high 先缩到 2048×2048 以内,最短边再缩到 768,文档写的是 2500 patch 预算。gpt-6 的 high 先缩到 2048×2048 以内,再按 2500 patch 预算缩放,文档没有写 2048 这一步。
  • Gemini 3 用的是文档值。实测按图片宽高比低 2% 到 9%。
  • Gemini 2.x 的 4K 图会多算:3840×2160 实际 2322,这里给 3354。
  • 图片尺寸来自请求方,可能被伪造。超过 65535 像素的边会先缩小,单张最多按 30000 个 patch 计算,结果始终有上限。

开发

TokenKit 必须始终保持独立可构建,不依赖 new-api 主模块或 RelayKit。修改模块后,在 tokenkit 目录运行:

GOWORK=off go test ./...
GOWORK=off go build ./...

image_test.go 里的期望值是上游实测值。改动图片规则时,用同样的方法重新测量:同一个请求带图和不带图各发一次,两次 input token 的差就是这张图的消耗。

版本与兼容性

TokenKit 当前使用 v0.x 版本,公开 API 可能在小版本中调整。估算权重和图片规则会随上游模型更新重新校准,同样的输入在不同版本之间可能得到不同的结果。

许可证

TokenKit 是 new-api 项目的一部分,遵循项目根目录中的 GNU Affero General Public License v3.0。

Documentation

Overview

Package tokenkit estimates LLM token counts for text and images without calling an upstream API.

Text is counted exactly with tiktoken for OpenAI GPT and o-series models and estimated with per-tokenizer weights for Claude, Gemini, and other models. Image costs follow each provider's published sizing rules. The weights were calibrated against upstream usage on real agent traffic (Claude Code and Codex sessions).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Count

func Count(model, text string) int

Count returns the token count of text for model. GPT and o-series models are counted with their tiktoken encoding; other models use Estimate.

func Estimate

func Estimate(model, text string) int

Estimate returns the estimated token count of text for model, using the weights of the model's family. It never runs a tokenizer; use Count for an exact count where one is available.

func EstimateFamily

func EstimateFamily(family Family, text string) int

EstimateFamily returns the estimated token count of text for a tokenizer family. Unrecognized families use the FamilyUnknown weights.

func ImageNeedsSize

func ImageNeedsSize(model, detail string) bool

ImageNeedsSize reports whether ImageTokens depends on the image dimensions for model and detail. When it returns false, callers can skip reading the image.

func ImageTokens

func ImageTokens(model string, img Image) int

ImageTokens returns the prompt tokens one image costs on model.

Types

type Family

type Family string

Family is a tokenizer family. Models in one family tokenize text alike.

const (
	FamilyOpenAI   Family = "openai"    // GPT and o-series models
	FamilyClaude   Family = "claude"    // Claude models before Claude Opus 4.7
	FamilyClaude47 Family = "claude4.7" // Claude Opus 4.7 and later, including Claude 5, Fable, and Mythos
	FamilyGemini   Family = "gemini"    // Gemini models
	FamilyUnknown  Family = "unknown"   // everything else
)

func ModelFamily

func ModelFamily(model string) Family

ModelFamily classifies a model name, case-insensitively. Vendor prefixes and date suffixes are allowed (for example us.anthropic.claude-opus-4-7-v1:0).

type Image

type Image struct {
	// Width and Height are the pixel dimensions. Zero or negative means
	// unknown; where size matters, a 1024×1024 image is assumed.
	Width, Height int
	// Detail is the OpenAI detail level (low, high, auto, original) or the
	// Gemini media resolution (MEDIA_RESOLUTION_LOW, _MEDIUM, _HIGH,
	// _ULTRA_HIGH). Empty means the provider default. Each family ignores
	// values meant for the other.
	Detail string
}

Image describes one input image.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL