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