refactor: extract tokenizer to @openchatlab/node-runtime

Move countTokens/countMessagesTokens from Electron to shared package.
Electron side becomes a thin re-export wrapper.
This commit is contained in:
digua committed 2026-05-14 21:27:39 +08:00
1 parent 6352a3f191
commit b195750f34
6 files changed
+60 -45

No files matched your search

+4 -44
View File
@@ -1,47 +1,7 @@
/**
* Token 计数模块
* 使用 js-tiktoken 的 cl100k_base 编码进行近似 token 计数。
* 该编码是 GPT-4/Claude 系列的近似值,对国内模型有一定误差,
* 因此阈值计算时预留了余量。
* Token 计数模块 — 薄包装
*
* 实际实现已提取到 @openchatlab/node-runtime。
*/
import { encodingForModel } from 'js-tiktoken'
let encoder: ReturnType<typeof encodingForModel> | null = null
function getEncoder() {
if (!encoder) {
encoder = encodingForModel('gpt-4o')
}
return encoder
}
/**
* 计算单段文本的 token 数
*/
export function countTokens(text: string): number {
if (!text) return 0
return getEncoder().encode(text).length
}
/**
* 计算消息列表的总 token 数(含 systemPrompt)
* 每条消息额外计 4 tokens 的格式开销(role + 分隔符)
*/
export function countMessagesTokens(messages: Array<{ role: string; content: string }>, systemPrompt?: string): number {
const enc = getEncoder()
let total = 0
if (systemPrompt) {
total += enc.encode(systemPrompt).length + 4
}
for (const msg of messages) {
total += enc.encode(msg.content).length + 4
}
// 回复引导 token
total += 3
return total
}
export { countTokens, countMessagesTokens } from '@openchatlab/node-runtime'
+2 -1
View File
@@ -7,6 +7,7 @@
"types": "./src/index.ts",
"dependencies": {
"@openchatlab/core": "workspace:*",
"gray-matter": "^4.0.3"
"gray-matter": "^4.0.3",
"js-tiktoken": "^1.0.21"
}
}
+3
View File
@@ -16,3 +16,6 @@ export type {
TokenUsageData,
ConversationManagerLogger,
} from './conversations'
// Tokenizer
export { countTokens, countMessagesTokens } from './tokenizer'
+47
View File
@@ -0,0 +1,47 @@
/**
* Token 计数模块
* 使用 js-tiktoken 的 cl100k_base 编码进行近似 token 计数。
* 该编码是 GPT-4/Claude 系列的近似值,对国内模型有一定误差,
* 因此阈值计算时预留了余量。
*/
import { encodingForModel } from 'js-tiktoken'
let encoder: ReturnType<typeof encodingForModel> | null = null
function getEncoder() {
if (!encoder) {
encoder = encodingForModel('gpt-4o')
}
return encoder
}
/**
* 计算单段文本的 token 数
*/
export function countTokens(text: string): number {
if (!text) return 0
return getEncoder().encode(text).length
}
/**
* 计算消息列表的总 token 数(含 systemPrompt)
* 每条消息额外计 4 tokens 的格式开销(role + 分隔符)
*/
export function countMessagesTokens(messages: Array<{ role: string; content: string }>, systemPrompt?: string): number {
const enc = getEncoder()
let total = 0
if (systemPrompt) {
total += enc.encode(systemPrompt).length + 4
}
for (const msg of messages) {
total += enc.encode(msg.content).length + 4
}
// 回复引导 token
total += 3
return total
}
+1
View File
@@ -34,6 +34,7 @@ export {
export type { AssistantConfig, AssistantSummary, SkillDef, SkillSummary } from './ai'
export { parseAssistantFile, serializeAssistant, parseSkillFile, extractSkillId } from './ai'
export { AIConversationManager } from './ai'
export { countTokens, countMessagesTokens } from './ai'
export type {
AIConversation,
AIMessage,
+3
View File
@@ -161,6 +161,9 @@ importers:
gray-matter:
specifier: ^4.0.3
version: 4.0.3
js-tiktoken:
specifier: ^1.0.21
version: 1.0.21
packages/parser:
dependencies: