( blocks: ContentBlockParam[], maxChars: number, )
| 92 | } |
| 93 | |
| 94 | async function truncateContentBlocks( |
| 95 | blocks: ContentBlockParam[], |
| 96 | maxChars: number, |
| 97 | ): Promise<ContentBlockParam[]> { |
| 98 | const result: ContentBlockParam[] = [] |
| 99 | let currentChars = 0 |
| 100 | |
| 101 | for (const block of blocks) { |
| 102 | if (isTextBlock(block)) { |
| 103 | const remainingChars = maxChars - currentChars |
| 104 | if (remainingChars <= 0) break |
| 105 | |
| 106 | if (block.text.length <= remainingChars) { |
| 107 | result.push(block) |
| 108 | currentChars += block.text.length |
| 109 | } else { |
| 110 | result.push({ type: 'text', text: block.text.slice(0, remainingChars) }) |
| 111 | break |
| 112 | } |
| 113 | } else if (isImageBlock(block)) { |
| 114 | // Include images but count their estimated size |
| 115 | const imageChars = IMAGE_TOKEN_ESTIMATE * 4 |
| 116 | if (currentChars + imageChars <= maxChars) { |
| 117 | result.push(block) |
| 118 | currentChars += imageChars |
| 119 | } else { |
| 120 | // Image exceeds budget - try to compress it to fit remaining space |
| 121 | const remainingChars = maxChars - currentChars |
| 122 | if (remainingChars > 0) { |
| 123 | // Convert remaining chars to bytes for compression |
| 124 | // base64 uses ~4/3 the original size, so we calculate max bytes |
| 125 | const remainingBytes = Math.floor(remainingChars * 0.75) |
| 126 | try { |
| 127 | const compressedBlock = await compressImageBlock( |
| 128 | block, |
| 129 | remainingBytes, |
| 130 | ) |
| 131 | result.push(compressedBlock) |
| 132 | // Update currentChars based on compressed image size |
| 133 | if (compressedBlock.source.type === 'base64') { |
| 134 | currentChars += compressedBlock.source.data.length |
| 135 | } else { |
| 136 | currentChars += imageChars |
| 137 | } |
| 138 | } catch { |
| 139 | // If compression fails, skip the image |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | } else { |
| 144 | result.push(block) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | return result |
| 149 | } |
| 150 | |
| 151 | export async function mcpContentNeedsTruncation( |
no test coverage detected