Skip to content

Commit 0e72da2

Browse files
committed
fix: correct truncation token display accounting
1 parent 3bd1a80 commit 0e72da2

2 files changed

Lines changed: 68 additions & 6 deletions

File tree

src/core/context-management/__tests__/context-management.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ describe("Context Management", () => {
5656
TelemetryService.createInstance([])
5757
}
5858
})
59+
60+
const countEffectiveHistoryTokens = async (messages: ApiMessage[], systemPrompt: string) => {
61+
const effectiveMessages = condenseModule.getEffectiveApiHistory(messages)
62+
let total = await estimateTokenCount([{ type: "text", text: systemPrompt }], mockApiHandler)
63+
64+
for (const message of effectiveMessages) {
65+
if (Array.isArray(message.content)) {
66+
total += await estimateTokenCount(message.content, mockApiHandler)
67+
} else if (typeof message.content === "string") {
68+
total += await estimateTokenCount([{ type: "text", text: message.content }], mockApiHandler)
69+
}
70+
}
71+
72+
return total
73+
}
74+
5975
/**
6076
* Tests for the truncateConversation function
6177
*/
@@ -1700,5 +1716,46 @@ describe("Context Management", () => {
17001716
// With system prompt included, we expect roughly 50% of the messages remaining
17011717
expect(result.newContextTokensAfterTruncation).toBeGreaterThan(0)
17021718
})
1719+
1720+
it("should count only the effective API history after truncation when prior condenses exist", async () => {
1721+
const modelInfo = createModelInfo(100000, 30000)
1722+
const totalTokens = 70001
1723+
const condenseId = "prior-condense"
1724+
const hiddenContent = "hidden historical content ".repeat(4000)
1725+
1726+
const messages: ApiMessage[] = [
1727+
{ role: "user", content: hiddenContent, condenseParent: condenseId },
1728+
{ role: "assistant", content: hiddenContent, condenseParent: condenseId },
1729+
{ role: "user", content: hiddenContent, condenseParent: condenseId },
1730+
{ role: "user", content: "Earlier summary", isSummary: true, condenseId },
1731+
{ role: "assistant", content: "Recent visible reply" },
1732+
{ role: "user", content: "Recent visible follow-up" },
1733+
{ role: "assistant", content: "Most recent visible reply" },
1734+
{ role: "user", content: "" },
1735+
]
1736+
1737+
const systemPrompt = "System prompt for truncation recount"
1738+
1739+
const result = await manageContext({
1740+
messages,
1741+
totalTokens,
1742+
contextWindow: modelInfo.contextWindow,
1743+
maxTokens: modelInfo.maxTokens,
1744+
apiHandler: mockApiHandler,
1745+
autoCondenseContext: false,
1746+
autoCondenseContextPercent: 100,
1747+
systemPrompt,
1748+
taskId,
1749+
profileThresholds: {},
1750+
currentProfileId: "default",
1751+
})
1752+
1753+
expect(result.truncationId).toBeDefined()
1754+
expect(result.newContextTokensAfterTruncation).toBeDefined()
1755+
1756+
const expectedEffectiveTokens = await countEffectiveHistoryTokens(result.messages, systemPrompt)
1757+
expect(result.newContextTokensAfterTruncation).toBe(expectedEffectiveTokens)
1758+
expect(result.newContextTokensAfterTruncation).toBeLessThan(result.prevContextTokens)
1759+
})
17031760
})
17041761
})

src/core/context-management/index.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import crypto from "crypto"
44
import { TelemetryService } from "@roo-code/telemetry"
55

66
import { ApiHandler, ApiHandlerCreateMessageMetadata } from "../../api"
7-
import { MAX_CONDENSE_THRESHOLD, MIN_CONDENSE_THRESHOLD, summarizeConversation, SummarizeResponse } from "../condense"
7+
import {
8+
MAX_CONDENSE_THRESHOLD,
9+
MIN_CONDENSE_THRESHOLD,
10+
getEffectiveApiHistory,
11+
summarizeConversation,
12+
SummarizeResponse,
13+
} from "../condense"
814
import { ApiMessage } from "../task-persistence/apiMessages"
915
import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "@roo-code/types"
1016
import { RooIgnoreController } from "../ignore/RooIgnoreController"
@@ -334,11 +340,10 @@ export async function manageContext({
334340
if (prevContextTokens > allowedTokens) {
335341
const truncationResult = truncateConversation(messages, 0.5, taskId)
336342

337-
// Calculate new context tokens after truncation by counting non-truncated messages
338-
// Messages with truncationParent are hidden, so we count only those without it
339-
const effectiveMessages = truncationResult.messages.filter(
340-
(msg) => !msg.truncationParent && !msg.isTruncationMarker,
341-
)
343+
// Calculate new context tokens after truncation from the effective API history.
344+
// This keeps the post-truncation recount aligned with the same filtered history
345+
// we actually send to the provider, including prior condense/truncation layers.
346+
const effectiveMessages = getEffectiveApiHistory(truncationResult.messages)
342347

343348
// Include system prompt tokens so this value matches what we send to the API.
344349
// Note: `prevContextTokens` is computed locally here (totalTokens + lastMessageTokens).

0 commit comments

Comments
 (0)