Skip to content

Commit 00a06ed

Browse files
feat: annotate tool outputs with token counts
1 parent cc14f82 commit 00a06ed

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

lib/hooks.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { PluginConfig } from "./config"
44
import { syncToolCache } from "./state/tool-cache"
55
import { deduplicate, supersedeWrites, purgeErrors } from "./strategies"
66
import { prune, insertCompressToolContext } from "./messages"
7-
import { buildToolIdList, isIgnoredUserMessage } from "./messages/utils"
7+
import { buildToolIdList, isIgnoredUserMessage, annotateContext } from "./messages/utils"
88
import { checkSession } from "./state"
99
import { renderSystemPrompt } from "./prompts"
1010
import { handleStatsCommand } from "./commands/stats"
@@ -115,8 +115,10 @@ export function createChatMessageTransformHandler(
115115
purgeErrors(state, logger, config, output.messages)
116116

117117
prune(state, logger, config, output.messages)
118+
118119
insertCompressToolContext(state, config, logger, output.messages)
119120

121+
annotateContext(output.messages)
120122
applyPendingManualTriggerPrompt(state, output.messages, logger)
121123

122124
if (state.sessionId) {

lib/messages/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { isMessageCompacted } from "../shared-utils"
33
import { Logger } from "../logger"
44
import type { SessionState, WithParts } from "../state"
55
import type { UserMessage } from "@opencode-ai/sdk/v2"
6+
import { countToolTokens } from "../strategies/utils"
67

78
export const COMPRESS_SUMMARY_PREFIX = "[Compressed conversation block]\n\n"
89

@@ -267,3 +268,26 @@ export const isIgnoredUserMessage = (message: WithParts): boolean => {
267268
export const findMessageIndex = (messages: WithParts[], messageId: string): number => {
268269
return messages.findIndex((msg) => msg.info.id === messageId)
269270
}
271+
272+
function formatTokenCount(n: number): string {
273+
if (n >= 1000) return `${(n / 1000).toFixed(1)}k tokens`
274+
return `${n} tokens`
275+
}
276+
277+
export function annotateContext(messages: WithParts[]): void {
278+
for (const msg of messages) {
279+
const parts = Array.isArray(msg.parts) ? msg.parts : []
280+
for (const part of parts) {
281+
if (part.type !== "tool") continue
282+
const tokens = countToolTokens(part)
283+
if (tokens <= 0) continue
284+
const tag = `[${formatTokenCount(tokens)}]`
285+
if (part.state?.status === "completed" && typeof part.state.output === "string") {
286+
part.state.output = `${tag}\n${part.state.output}`
287+
}
288+
if (part.state?.status === "error" && typeof part.state.error === "string") {
289+
part.state.error = `${tag}\n${part.state.error}`
290+
}
291+
}
292+
}
293+
}

0 commit comments

Comments
 (0)