(body: AnthropicRequest)
| 111 | } |
| 112 | |
| 113 | export function estimateInputTokens(body: AnthropicRequest): number { |
| 114 | let total = 0; |
| 115 | |
| 116 | if (body.system) { |
| 117 | const sysStr = typeof body.system === 'string' ? body.system : JSON.stringify(body.system); |
| 118 | total += estimateTokens(sysStr); |
| 119 | } |
| 120 | |
| 121 | for (const msg of body.messages ?? []) { |
| 122 | const msgStr = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content); |
| 123 | total += estimateTokens(msgStr); |
| 124 | } |
| 125 | |
| 126 | // Tool schemas are heavily compressed by compactSchema in converter.ts. |
| 127 | // However, they still consume Cursor's context budget. |
| 128 | // If not counted, Claude CLI will dangerously underestimate context size. |
| 129 | if (body.tools && body.tools.length > 0) { |
| 130 | total += body.tools.length * 70; // ~200 chars/tool → ~70 tokens after compression |
| 131 | total += 350; // Tool use guidelines and behavior instructions |
| 132 | } |
| 133 | |
| 134 | // ★ 上下文压力膨胀(Context Pressure Inflation) |
| 135 | // Claude Code 假设模型的 context window 是 200K tokens,在 ~80%(160K)时触发自动压缩。 |
| 136 | // 但 Cursor API 的实际 context window 只有 ~150K tokens。 |
| 137 | // 这意味着 Claude Code 到 160K 才压缩,而 Cursor 在 ~120K 输入时就已开始挤压输出空间。 |
| 138 | // 解决方案:虚增 input_tokens,让 Claude Code 以为上下文更满,提前触发压缩。 |
| 139 | // 膨胀系数 1.35 = 200K / 150K(Cursor 实际窗口与 Claude Code 假设窗口的比值) |
| 140 | // 效果:当 Cursor 实际输入达到 ~118K 时,报告给 Claude Code 的是 ~160K,触发压缩。 |
| 141 | const contextPressure = getConfig().contextPressure ?? 1.0; // 默认关闭(1.0=不膨胀),推荐 1.35 |
| 142 | if (contextPressure > 1.0) { |
| 143 | total = Math.ceil(total * contextPressure); |
| 144 | } |
| 145 | |
| 146 | return Math.max(1, total); |
| 147 | } |
| 148 | |
| 149 | export function countTokens(req: Request, res: Response): void { |
| 150 | const body = req.body as AnthropicRequest; |
no test coverage detected