Skip to content

Commit 7def2ce

Browse files
committed
fix: token usage statistics
1 parent 508216a commit 7def2ce

File tree

5 files changed

+88
-4
lines changed

5 files changed

+88
-4
lines changed

src/components/chat/messageHandler.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ interface OpenAIResponse {
1414
content?: string;
1515
};
1616
}>;
17+
usage?: {
18+
total_tokens: number;
19+
prompt_tokens: number;
20+
completion_tokens: number;
21+
};
1722
error?: {
1823
message: string;
1924
};
@@ -29,6 +34,27 @@ interface OpenAIRequest {
2934
max_tokens?: number;
3035
}
3136

37+
interface TokenUsage {
38+
totalTokens: number;
39+
requestCount: number;
40+
}
41+
42+
async function updateTokenUsage(newTokens: number): Promise<void> {
43+
const result = await new Promise<{ tokenUsage?: TokenUsage }>((resolve) => {
44+
chrome.storage.local.get(['tokenUsage'], resolve);
45+
});
46+
47+
const currentUsage = result.tokenUsage || { totalTokens: 0, requestCount: 0 };
48+
const updatedUsage = {
49+
totalTokens: currentUsage.totalTokens + newTokens,
50+
requestCount: currentUsage.requestCount + 1
51+
};
52+
53+
await new Promise<void>((resolve) => {
54+
chrome.storage.local.set({ tokenUsage: updatedUsage }, resolve);
55+
});
56+
}
57+
3258
export async function handleQuestion(question: string, context: string, model?: ModelType): Promise<void> {
3359
try {
3460
// Check if the question is a shortcut
@@ -50,6 +76,32 @@ export async function handleQuestion(question: string, context: string, model?:
5076
const selectedModel = model || settings.model;
5177
await addMessage('assistant', '', selectedModel);
5278

79+
// First make a non-streaming request to get token usage
80+
const nonStreamingResponse = await fetch(`${settings.apiUrl}/chat/completions`, {
81+
method: 'POST',
82+
headers: {
83+
'Content-Type': 'application/json',
84+
'Authorization': `Bearer ${settings.apiKey}`
85+
},
86+
body: JSON.stringify({
87+
model: selectedModel,
88+
messages: [
89+
{ role: 'system', content: systemMessage },
90+
{ role: 'user', content: finalQuestion }
91+
],
92+
stream: false
93+
} as OpenAIRequest)
94+
});
95+
96+
if (!nonStreamingResponse.ok) {
97+
const data = await nonStreamingResponse.json();
98+
throw new Error(data.error?.message || 'Failed to get response from OpenAI');
99+
}
100+
101+
const nonStreamingData = await nonStreamingResponse.json();
102+
const totalTokens = nonStreamingData.usage?.total_tokens || 0;
103+
104+
// Now make the streaming request for the actual response
53105
const response = await fetch(`${settings.apiUrl}/chat/completions`, {
54106
method: 'POST',
55107
headers: {
@@ -100,15 +152,17 @@ export async function handleQuestion(question: string, context: string, model?:
100152
}
101153
}
102154
}
155+
156+
// Update token usage after successful completion
157+
await updateTokenUsage(totalTokens);
103158
} catch (error) {
104159
console.error('Error reading stream:', error);
105160
throw error;
106161
} finally {
107162
reader.releaseLock();
108163
}
109-
110164
} catch (error) {
111-
console.error('Error handling question:', error);
112-
await addMessage('assistant', `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`, model || (await getSettings()).model);
165+
console.error('Error in handleQuestion:', error);
166+
throw error;
113167
}
114168
}

src/popup.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ <h3>Custom Prompts</h3>
5353
<p>Total Tokens: <span id="total-tokens">0</span></p>
5454
<p>Total Requests: <span id="request-count">0</span></p>
5555
<p>Average Tokens/Request: <span id="avg-tokens">0</span></p>
56+
<button id="clear-usage" class="ai-button-base ai-danger-button">Clear Usage Stats</button>
5657
</div>
5758
</div>
5859
<script src="popup.js"></script>

src/popup.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// <reference types="chrome"/>
22

33
import { MODELS, MODEL_DISPLAY_NAMES, DEFAULT_MODEL, ModelType } from './config';
4-
import { Settings as GlobalSettings, getSettings, updateSettings } from './settings';
4+
import { Settings as GlobalSettings, getSettings, updateSettings, clearTokenUsage } from './settings';
55

66
interface TokenUsage {
77
totalTokens: number;
@@ -12,6 +12,15 @@ interface CustomPrompts {
1212
[key: string]: string;
1313
}
1414

15+
// Load token usage from storage
16+
async function loadTokenUsage(): Promise<TokenUsage> {
17+
return new Promise((resolve) => {
18+
chrome.storage.local.get(['tokenUsage'], (result) => {
19+
resolve(result.tokenUsage || { totalTokens: 0, requestCount: 0 });
20+
});
21+
});
22+
}
23+
1524
function initializeSettings(): void {
1625
// Initialize model selector
1726
const modelSelector = document.getElementById('model-selector') as HTMLSelectElement;
@@ -55,6 +64,9 @@ function initializeSettings(): void {
5564
showIconToggle.checked = settings.showIcon;
5665
}
5766
});
67+
68+
// Load token usage
69+
loadTokenUsage().then(updateUsageStats);
5870
}
5971

6072
function loadCustomPrompts(prompts: CustomPrompts): void {
@@ -169,6 +181,15 @@ function saveSettings(): void {
169181
document.addEventListener('DOMContentLoaded', () => {
170182
initializeSettings();
171183

184+
// Add event listener for clear usage button
185+
const clearUsageButton = document.getElementById('clear-usage');
186+
if (clearUsageButton) {
187+
clearUsageButton.addEventListener('click', async () => {
188+
await clearTokenUsage();
189+
updateUsageStats({ totalTokens: 0, requestCount: 0 });
190+
});
191+
}
192+
172193
// Load custom prompts
173194
chrome.storage.sync.get(['customPrompts'], (result: { customPrompts?: Record<string, string> }) => {
174195
const customPrompts = result.customPrompts || {};

src/settings.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ label {
188188
/* Save button */
189189
.save-button {
190190
width: 100%;
191+
height: 32px;
191192
margin-top: 12px;
193+
border-radius: 6px;
192194
}
193195

194196
/* Token usage section */

src/settings.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ export async function clearApiKey(): Promise<void> {
7474
return updateSettings(settings);
7575
}
7676

77+
export async function clearTokenUsage(): Promise<void> {
78+
return new Promise((resolve) => {
79+
chrome.storage.local.set({ tokenUsage: { totalTokens: 0, requestCount: 0 } }, resolve);
80+
});
81+
}
82+
7783
function updateTokenDisplay(usage: TokenUsage): void {
7884
const totalTokensEl = document.getElementById('total-tokens');
7985
const requestCountEl = document.getElementById('request-count');

0 commit comments

Comments
 (0)