MCPcopy Create free account
hub / github.com/Noumena-Network/code / countTokensViaHaikuFallback

Function countTokensViaHaikuFallback

src/services/tokenEstimation.ts:257–332  ·  view source on GitHub ↗
(
  messages: Anthropic.Beta.Messages.BetaMessageParam[],
  tools: Anthropic.Beta.Messages.BetaToolUnion[],
)

Source from the content-addressed store, hash-verified

255 * otherwise uses the configured SmallFastModel.
256 */
257export async function countTokensViaHaikuFallback(
258 messages: Anthropic.Beta.Messages.BetaMessageParam[],
259 tools: Anthropic.Beta.Messages.BetaToolUnion[],
260): Promise<number | null> {
261 // Check if messages contain thinking blocks
262 const containsThinking = hasThinkingBlocks(messages)
263
264 // If we're on Vertex and using global region, always use Sonnet since Haiku is not available there.
265 const isVertexGlobalEndpoint =
266 isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) &&
267 getVertexRegionForModel(getSmallFastModel()) === 'global'
268 // If we're on Bedrock with thinking blocks, use Sonnet since Haiku 3.5 doesn't support thinking
269 const isBedrockWithThinking =
270 isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) && containsThinking
271 // If we're on Vertex with thinking blocks, use Sonnet since Haiku 3.5 doesn't support thinking
272 const isVertexWithThinking =
273 isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) && containsThinking
274 // On Noumena-managed first-party, route token counting through the main
275 // loop model so the reported usage matches the tokenizer of the serving
276 // model. GLM, Kimi, and DeepSeek V4 Flash each have provider-specific
277 // tokenizers; using a different model family for counting would silently
278 // mis-estimate input tokens, breaking auto-compact thresholds.
279 const isManagedFirstParty = getAPIProvider() === 'firstParty'
280 const model = isManagedFirstParty
281 ? getMainLoopModel()
282 : isVertexGlobalEndpoint || isBedrockWithThinking || isVertexWithThinking
283 ? getDefaultFlashModel()
284 : getSmallFastModel()
285 const inferenceClient = await getInferenceClient({
286 maxRetries: 1,
287 model,
288 source: 'count_tokens',
289 })
290
291 // Strip tool search-specific fields (caller, tool_reference) before sending
292 // These fields are only valid with the tool search beta header
293 const normalizedMessages = stripToolSearchFieldsFromMessages(messages)
294
295 const messagesToSend: MessageParam[] =
296 normalizedMessages.length > 0
297 ? (normalizedMessages as MessageParam[])
298 : [{ role: 'user', content: 'count' }]
299
300 const betas = getModelBetas(model)
301 // Filter betas for Vertex - some betas (like web-search) cause 400 errors
302 // on certain Vertex endpoints. See issue #10789.
303 const filteredBetas =
304 getAPIProvider() === 'vertex'
305 ? betas.filter(b => VERTEX_COUNT_TOKENS_ALLOWED_BETAS.has(b))
306 : betas
307
308 // biome-ignore lint/plugin: token counting needs specialized parameters (thinking, betas) that sideQuery doesn't support
309 const response = await inferenceClient.createMessage({
310 model: normalizeModelStringForAPI(model),
311 max_tokens: containsThinking ? TOKEN_COUNT_MAX_TOKENS : 1,
312 messages: messagesToSend,
313 tools: tools.length > 0 ? tools : undefined,
314 ...(filteredBetas.length > 0 && { betas: filteredBetas }),

Callers 1

countTokensWithFallbackFunction · 0.85

Calls 14

isEnvTruthyFunction · 0.90
hasThinkingBlocksFunction · 0.85
getVertexRegionForModelFunction · 0.85
getSmallFastModelFunction · 0.85
getAPIProviderFunction · 0.85
getMainLoopModelFunction · 0.85
getDefaultFlashModelFunction · 0.85
getInferenceClientFunction · 0.85
getAPIMetadataFunction · 0.85
getExtraBodyParamsFunction · 0.85

Tested by

no test coverage detected