Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
import type { ModelMessage } from 'ai';
|
|
import type { TokenEstimatorKind } from './types';
|
|
|
|
const CHARS_PER_TOKEN_FALLBACK = 4;
|
|
|
|
function estimateChars(value: unknown): number {
|
|
if (value == null) return 0;
|
|
if (typeof value === 'string') return value.length;
|
|
if (Array.isArray(value)) {
|
|
return value.reduce((sum, item) => sum + estimateChars(item), 0);
|
|
}
|
|
if (typeof value === 'object') {
|
|
try {
|
|
return JSON.stringify(value).length;
|
|
} catch {
|
|
return String(value).length;
|
|
}
|
|
}
|
|
return String(value).length;
|
|
}
|
|
|
|
export function resolveEstimatorKind(providerId?: string | null): TokenEstimatorKind {
|
|
const id = (providerId ?? '').toLowerCase();
|
|
if (id.includes('openai') || id.includes('gpt')) return 'openai-heuristic';
|
|
if (id.includes('anthropic') || id.includes('claude')) return 'anthropic-heuristic';
|
|
if (id.includes('google') || id.includes('gemini')) return 'google-heuristic';
|
|
return 'chars-div-4';
|
|
}
|
|
|
|
function applyHeuristicMultiplier(chars: number, kind: TokenEstimatorKind): number {
|
|
switch (kind) {
|
|
case 'openai-heuristic':
|
|
return Math.ceil(chars / 3.5);
|
|
case 'anthropic-heuristic':
|
|
return Math.ceil(chars / 3.2);
|
|
case 'google-heuristic':
|
|
return Math.ceil(chars / 3.8);
|
|
default:
|
|
return Math.ceil(chars / CHARS_PER_TOKEN_FALLBACK);
|
|
}
|
|
}
|
|
|
|
export function estimateTextTokens(text: string, providerId?: string | null): number {
|
|
const kind = resolveEstimatorKind(providerId);
|
|
return applyHeuristicMultiplier(text.length, kind);
|
|
}
|
|
|
|
export function estimateUnknownTokens(value: unknown, providerId?: string | null): number {
|
|
const kind = resolveEstimatorKind(providerId);
|
|
return applyHeuristicMultiplier(estimateChars(value), kind);
|
|
}
|
|
|
|
export interface EstimateModelMessagesTokensInput {
|
|
messages: ModelMessage[];
|
|
providerId?: string | null;
|
|
}
|
|
|
|
export interface EstimateModelMessagesTokensResult {
|
|
tokens: number;
|
|
estimatorKind: TokenEstimatorKind;
|
|
}
|
|
|
|
export function estimateModelMessagesTokensWithKind(
|
|
input: EstimateModelMessagesTokensInput,
|
|
): EstimateModelMessagesTokensResult {
|
|
const estimatorKind = resolveEstimatorKind(input.providerId);
|
|
const chars = input.messages.reduce((total, message) => {
|
|
return total + estimateChars(message.role) + estimateChars(message.content);
|
|
}, 0);
|
|
return {
|
|
tokens: applyHeuristicMultiplier(chars, estimatorKind),
|
|
estimatorKind,
|
|
};
|
|
}
|
|
|
|
export function estimateModelMessagesTokens(
|
|
messages: ModelMessage[],
|
|
providerId?: string | null,
|
|
): number {
|
|
return estimateModelMessagesTokensWithKind({ messages, providerId }).tokens;
|
|
}
|