diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index cea500df263..fe56097ce2a 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -60,6 +60,9 @@ export class OpenAiHandler implements ApiHandler, SingleCompletionHandler { const deepseekReasoner = modelId.includes("deepseek-reasoner") const ark = modelUrl.includes(".volces.com") + let last_prompt_tokens = 0 + let last_completion_tokens = 0 + if (this.options.openAiStreamingEnabled ?? true) { const systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = { role: "system", @@ -107,7 +110,9 @@ export class OpenAiHandler implements ApiHandler, SingleCompletionHandler { } } if (chunk.usage) { - yield this.processUsageMetrics(chunk.usage) + yield this.processUsageMetrics(chunk.usage, last_prompt_tokens, last_completion_tokens) + last_prompt_tokens = chunk.usage?.prompt_tokens || 0 + last_completion_tokens = chunk.usage?.completion_tokens || 0 } } } else { @@ -130,15 +135,27 @@ export class OpenAiHandler implements ApiHandler, SingleCompletionHandler { type: "text", text: response.choices[0]?.message.content || "", } - yield this.processUsageMetrics(response.usage) + yield this.processUsageMetrics(response.usage, last_prompt_tokens, last_completion_tokens) } } - protected processUsageMetrics(usage: any): ApiStreamUsageChunk { + protected processUsageMetrics( + usage: any, + last_prompt_tokens: number, + last_completion_tokens: number, + ): ApiStreamUsageChunk { + if (this.options.openAiUsageCumulation) { + return { + type: "usage", + inputTokens: usage?.prompt_tokens || 0, + outputTokens: usage?.completion_tokens || 0, + } + } + return { type: "usage", - inputTokens: usage?.prompt_tokens || 0, - outputTokens: usage?.completion_tokens || 0, + inputTokens: usage?.prompt_tokens - last_prompt_tokens || 0, + outputTokens: usage?.completion_tokens - last_completion_tokens || 0, } } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 031e7960f9d..b5f308592ef 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -84,6 +84,7 @@ type GlobalStateKey = | "openAiModelId" | "openAiCustomModelInfo" | "openAiUseAzure" + | "openAiUsageCumulation" | "ollamaModelId" | "ollamaBaseUrl" | "lmStudioModelId" @@ -1615,6 +1616,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { openAiModelId, openAiCustomModelInfo, openAiUseAzure, + openAiUsageCumulation, ollamaModelId, ollamaBaseUrl, lmStudioModelId, @@ -1660,6 +1662,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("openAiModelId", openAiModelId) await this.updateGlobalState("openAiCustomModelInfo", openAiCustomModelInfo) await this.updateGlobalState("openAiUseAzure", openAiUseAzure) + await this.updateGlobalState("openAiUsageCumulation", openAiUsageCumulation) await this.updateGlobalState("ollamaModelId", ollamaModelId) await this.updateGlobalState("ollamaBaseUrl", ollamaBaseUrl) await this.updateGlobalState("lmStudioModelId", lmStudioModelId) @@ -2481,6 +2484,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { openAiModelId, openAiCustomModelInfo, openAiUseAzure, + openAiUsageCumulation, ollamaModelId, ollamaBaseUrl, lmStudioModelId, @@ -2561,6 +2565,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("openAiModelId") as Promise, this.getGlobalState("openAiCustomModelInfo") as Promise, this.getGlobalState("openAiUseAzure") as Promise, + this.getGlobalState("openAiUsageCumulation") as Promise, this.getGlobalState("ollamaModelId") as Promise, this.getGlobalState("ollamaBaseUrl") as Promise, this.getGlobalState("lmStudioModelId") as Promise, @@ -2658,6 +2663,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { openAiModelId, openAiCustomModelInfo, openAiUseAzure, + openAiUsageCumulation, ollamaModelId, ollamaBaseUrl, lmStudioModelId, diff --git a/src/shared/api.ts b/src/shared/api.ts index 7e926b09cfa..e95ecb28928 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -45,6 +45,7 @@ export interface ApiHandlerOptions { openAiModelId?: string openAiCustomModelInfo?: ModelInfo openAiUseAzure?: boolean + openAiUsageCumulation?: boolean ollamaModelId?: string ollamaBaseUrl?: string lmStudioModelId?: string diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 0d8bb43bb5b..bc02ea6a3fb 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -628,6 +628,15 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage, fromWelcomeView }: A Enable streaming + { + handleInputChange("openAiUsageCumulation")({ + target: { value: checked }, + }) + }}> + Enable token usage cumulation + {