( block: string | Anthropic.ContentBlock | Anthropic.ContentBlockParam, )
| 389 | } |
| 390 | |
| 391 | function roughTokenCountEstimationForBlock( |
| 392 | block: string | Anthropic.ContentBlock | Anthropic.ContentBlockParam, |
| 393 | ): number { |
| 394 | if (typeof block === 'string') { |
| 395 | return roughTokenCountEstimation(block) |
| 396 | } |
| 397 | if (block.type === 'text') { |
| 398 | return roughTokenCountEstimation(block.text) |
| 399 | } |
| 400 | if (block.type === 'image' || block.type === 'document') { |
| 401 | // https://platform.claude.com/docs/en/build-with-claude/vision#calculate-image-costs |
| 402 | // tokens = (width px * height px)/750 |
| 403 | // Images are resized to max 2000x2000 (5333 tokens). Use a conservative |
| 404 | // estimate that matches microCompact's IMAGE_MAX_TOKEN_SIZE to avoid |
| 405 | // underestimating and triggering auto-compact too late. |
| 406 | // |
| 407 | // document: base64 PDF in source.data. Must NOT reach the |
| 408 | // jsonStringify catch-all — a 1MB PDF is ~1.33M base64 chars → |
| 409 | // ~325k estimated tokens, vs the ~2000 the API actually charges. |
| 410 | // Same constant as microCompact's calculateToolResultTokens. |
| 411 | return 2000 |
| 412 | } |
| 413 | if (block.type === 'tool_result') { |
| 414 | return roughTokenCountEstimationForContent(block.content as any) |
| 415 | } |
| 416 | if (block.type === 'tool_use') { |
| 417 | // input is the JSON the model generated — arbitrarily large (bash |
| 418 | // commands, Edit diffs, file contents). Stringify once for the |
| 419 | // char count; the API re-serializes anyway so this is what it sees. |
| 420 | return roughTokenCountEstimation( |
| 421 | block.name + jsonStringify(block.input ?? {}), |
| 422 | ) |
| 423 | } |
| 424 | if (block.type === 'thinking') { |
| 425 | return roughTokenCountEstimation(block.thinking) |
| 426 | } |
| 427 | if (block.type === 'redacted_thinking') { |
| 428 | return roughTokenCountEstimation(block.data) |
| 429 | } |
| 430 | // server_tool_use, web_search_tool_result, mcp_tool_use, etc. — |
| 431 | // text-like payloads (tool inputs, search results, no base64). |
| 432 | // Stringify-length tracks the serialized form the API sees; the |
| 433 | // key/bracket overhead is single-digit percent on real blocks. |
| 434 | return roughTokenCountEstimation(jsonStringify(block)) |
| 435 | } |
| 436 | |
| 437 | async function countTokensWithBedrock({ |
| 438 | model, |
no test coverage detected