fix: handle both Anthropic and OpenAI token semantics correctly

The previous fix only handled Anthropic-style APIs. This update ensures
correct context token calculation when switching between OpenAI and
Anthropic providers in the same task.

Key changes:
- Store tokensIn based on API protocol:
  * Anthropic: inputTokens (base input, no cache)
  * OpenAI: totalInputTokens (total input, includes cache)
- Store apiProtocol in the message for protocol-aware calculations
- getApiMetrics now uses tokensIn directly since it represents total
  input tokens for both protocols

This handles the provider switching scenario correctly.
This commit is contained in:
Roo Code 2025-11-13 06:11:25 +00:00
parent 8dbc1031ff
commit 9d294c83d0
2 changed files with 12 additions and 10 deletions

View file

@ -1920,17 +1920,20 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
cacheReadTokens,
)
// For tokensIn, store the base input tokens (not including cache tokens)
// This ensures context length calculation works correctly in getApiMetrics
// where it adds tokensIn + tokensOut. The cache tokens are stored separately
// in cacheWrites and cacheReads for display purposes.
// Store tokensIn based on the API protocol:
// - For Anthropic: inputTokens is base input (without cache), so use inputTokens
// - For OpenAI: inputTokens already includes cache, so use totalInputTokens
// This ensures getApiMetrics can correctly calculate context tokens by checking apiProtocol
const tokensInValue = apiProtocol === "anthropic" ? inputTokens : costResult.totalInputTokens
this.clineMessages[lastApiReqIndex].text = JSON.stringify({
...existingData,
tokensIn: inputTokens,
tokensIn: tokensInValue,
tokensOut: costResult.totalOutputTokens,
cacheWrites: cacheWriteTokens,
cacheReads: cacheReadTokens,
cost: totalCost ?? costResult.totalCost,
apiProtocol,
cancelReason,
streamingFailedMessage,
} satisfies ClineApiReqInfo)

View file

@ -80,12 +80,11 @@ export function getApiMetrics(messages: ClineMessage[]) {
if (message.type === "say" && message.say === "api_req_started" && message.text) {
try {
const parsedText: ParsedApiReqStartedTextType = JSON.parse(message.text)
const { tokensIn, tokensOut } = parsedText
const { tokensIn, tokensOut, apiProtocol } = parsedText
// tokensIn contains base input tokens (without cache tokens).
// This works correctly for both Anthropic and OpenAI protocols because:
// - Cache tokens are stored separately in cacheWrites/cacheReads
// - Context length = base input + output tokens
// Context tokens = total tokens sent to the model
// For both Anthropic and OpenAI, tokensIn now stores the total input tokens
// (including cache tokens), so we just add tokensIn + tokensOut
result.contextTokens = (tokensIn || 0) + (tokensOut || 0)
} catch (error) {
console.error("Error parsing JSON:", error)