From e668169ed9136014b87721548c587c7939e2dafc Mon Sep 17 00:00:00 2001 From: MFPires Date: Mon, 27 Jan 2025 23:02:25 -0300 Subject: [PATCH 1/4] feat: Add conversation context token counter - Add contextTokens to ApiMetrics interface - Calculate context size using last API request's tokens - Display context token count in TaskHeader below total tokens - Use exact token counts instead of character estimation This helps users track the total size of their conversation context, which is useful for managing context window limits. --- src/shared/getApiMetrics.ts | 12 ++++++++++++ webview-ui/src/components/chat/ChatView.tsx | 1 + webview-ui/src/components/chat/TaskHeader.tsx | 9 +++++++++ 3 files changed, 22 insertions(+) diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index bd7b1bb..b4caadc 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -6,6 +6,7 @@ interface ApiMetrics { totalCacheWrites?: number totalCacheReads?: number totalCost: number + contextTokens: number // Total tokens in conversation (last message's tokensIn + tokensOut) } /** @@ -32,8 +33,14 @@ export function getApiMetrics(messages: ClineMessage[]): ApiMetrics { totalCacheWrites: undefined, totalCacheReads: undefined, totalCost: 0, + contextTokens: 0, } + // Find the last api_req_started message to get the context size + const lastApiReq = [...messages] + .reverse() + .find((message) => message.type === "say" && message.say === "api_req_started" && message.text) + messages.forEach((message) => { if (message.type === "say" && message.say === "api_req_started" && message.text) { try { @@ -55,6 +62,11 @@ export function getApiMetrics(messages: ClineMessage[]): ApiMetrics { if (typeof cost === "number") { result.totalCost += cost } + + // If this is the last api request, use its tokens for context size + if (message === lastApiReq) { + result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + } } catch (error) { console.error("Error parsing JSON:", error) } diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index da15b4e..7769f63 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -915,6 +915,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie cacheWrites={apiMetrics.totalCacheWrites} cacheReads={apiMetrics.totalCacheReads} totalCost={apiMetrics.totalCost} + contextTokens={apiMetrics.contextTokens} onClose={handleTaskCloseButtonClick} /> ) : ( diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 0b3f494..646dce3 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -16,6 +16,7 @@ interface TaskHeaderProps { cacheWrites?: number cacheReads?: number totalCost: number + contextTokens: number onClose: () => void } @@ -27,6 +28,7 @@ const TaskHeader: React.FC = ({ cacheWrites, cacheReads, totalCost, + contextTokens, onClose, }) => { const { apiConfiguration } = useExtensionState() @@ -272,6 +274,13 @@ const TaskHeader: React.FC = ({ {!isCostAvailable && } +
+ Context: + + {formatLargeNumber(contextTokens || 0)} + +
+ {shouldShowPromptCacheInfo && (cacheReads !== undefined || cacheWrites !== undefined) && (
Cache: From 97fe13dcb163ae422b05bbdb691a81bb47093379 Mon Sep 17 00:00:00 2001 From: MFPires Date: Mon, 27 Jan 2025 23:12:01 -0300 Subject: [PATCH 2/4] fix: Prevent context token counter from resetting on failed API calls - Only update context tokens when both input and output tokens are non-zero - Keep previous context token count when API calls fail - Avoid resetting counter on partial or failed responses This makes the context token counter more resilient against edge cases and provides more accurate context size tracking during API failures. --- src/shared/getApiMetrics.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index b4caadc..0c8a61e 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -65,7 +65,10 @@ export function getApiMetrics(messages: ClineMessage[]): ApiMetrics { // If this is the last api request, use its tokens for context size if (message === lastApiReq) { - result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + // Only update context tokens if both input and output tokens are non-zero + if (tokensIn > 0 && tokensOut > 0) { + result.contextTokens = tokensIn + tokensOut + } } } catch (error) { console.error("Error parsing JSON:", error) From 5311e0c8abfb52fa76fc251d70b8809ebdac5904 Mon Sep 17 00:00:00 2001 From: MFPires Date: Tue, 28 Jan 2025 00:27:17 -0300 Subject: [PATCH 3/4] fix: Make context token counter more reliable - Only consider API requests with valid token information - Skip messages with invalid/missing token data - Prevent counter from resetting on action approval messages - Ensure both tokensIn and tokensOut are valid numbers This makes the context token counter more stable and accurate by only updating on valid API responses with complete token data. --- src/shared/getApiMetrics.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index 0c8a61e..882c4aa 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -36,10 +36,18 @@ export function getApiMetrics(messages: ClineMessage[]): ApiMetrics { contextTokens: 0, } - // Find the last api_req_started message to get the context size - const lastApiReq = [...messages] - .reverse() - .find((message) => message.type === "say" && message.say === "api_req_started" && message.text) + // Find the last api_req_started message that has valid token information + const lastApiReq = [...messages].reverse().find((message) => { + if (message.type === "say" && message.say === "api_req_started" && message.text) { + try { + const parsedData = JSON.parse(message.text) + return typeof parsedData.tokensIn === "number" && typeof parsedData.tokensOut === "number" + } catch { + return false + } + } + return false + }) messages.forEach((message) => { if (message.type === "say" && message.say === "api_req_started" && message.text) { From 1ba632fe89e3491714f7deee5310f8536ecc4b4a Mon Sep 17 00:00:00 2001 From: Murilo Pires <50873657+MuriloFP@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:39:40 -0300 Subject: [PATCH 4/4] Update webview-ui/src/components/chat/TaskHeader.tsx Co-authored-by: Matt Rubens --- webview-ui/src/components/chat/TaskHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 646dce3..52b3756 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -277,7 +277,7 @@ const TaskHeader: React.FC = ({
Context: - {formatLargeNumber(contextTokens || 0)} + {contextTokens ? formatLargeNumber(contextTokens) : '-'}