( historyItem: ChatHistoryItem, model: ModelConfig, )
| 104 | * @returns The estimated token count |
| 105 | */ |
| 106 | export function countChatHistoryItemTokens( |
| 107 | historyItem: ChatHistoryItem, |
| 108 | model: ModelConfig, |
| 109 | ): number { |
| 110 | try { |
| 111 | let tokenCount = 0; |
| 112 | |
| 113 | const message = historyItem.message; |
| 114 | |
| 115 | // Count tokens in content |
| 116 | tokenCount += countContentTokens(message.content, model); |
| 117 | |
| 118 | // Add tokens for role (roughly 1-2 tokens) |
| 119 | tokenCount += 2; |
| 120 | |
| 121 | // Add tokens for tool calls if present |
| 122 | // Skip if toolCallStates exists to avoid double-counting (toolCallStates includes the tool calls) |
| 123 | if ( |
| 124 | "toolCalls" in message && |
| 125 | message.toolCalls && |
| 126 | !historyItem.toolCallStates |
| 127 | ) { |
| 128 | for (const toolCall of message.toolCalls) { |
| 129 | tokenCount += countToolCallFunctionTokens(toolCall.function); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | // Add tokens for tool call results |
| 134 | if (message.role === "tool" && "content" in message) { |
| 135 | tokenCount += 5; // Tool message structure overhead |
| 136 | } |
| 137 | |
| 138 | // Add tokens for context items |
| 139 | for (const contextItem of historyItem.contextItems) { |
| 140 | tokenCount += encode(contextItem.content).length; |
| 141 | tokenCount += encode(contextItem.name).length; |
| 142 | tokenCount += 5; // Context item structure overhead |
| 143 | } |
| 144 | |
| 145 | // Add tokens for tool call states (tool results/outputs) |
| 146 | if (historyItem.toolCallStates) { |
| 147 | for (const toolState of historyItem.toolCallStates) { |
| 148 | // Count tokens in tool call function (name + arguments) |
| 149 | tokenCount += countToolCallFunctionTokens(toolState.toolCall?.function); |
| 150 | // Count tokens in tool outputs (can be very large - thousands of tokens) |
| 151 | tokenCount += countToolOutputTokens(toolState.output); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | return tokenCount; |
| 156 | } catch (error) { |
| 157 | logger.error("Error counting tokens for history item", error); |
| 158 | // Return a rough estimate based on character count |
| 159 | const message = historyItem.message; |
| 160 | const content = |
| 161 | typeof message.content === "string" |
| 162 | ? message.content |
| 163 | : JSON.stringify(message.content); |
no test coverage detected