Skip to content

Commit ecc5ee9

Browse files
committed
Start mocking out token use in response
1 parent 9a51979 commit ecc5ee9

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-7
lines changed

src/extension/intents/node/toolCallingLoop.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,8 @@ export abstract class ToolCallingLoop<TOptions extends IToolCallingLoopOptions =
517517
toolCalls,
518518
toolInputRetry,
519519
statefulMarker,
520-
thinking: thinkingItem
520+
thinking: thinkingItem,
521+
usage: fetchResult.usage
521522
}),
522523
chatResult,
523524
hadIgnoredFiles: buildPromptResult.hasIgnoredFiles,

src/extension/prompt/common/intents.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import type * as vscode from 'vscode';
77
import { NotebookDocumentSnapshot } from '../../../platform/editing/common/notebookDocumentSnapshot';
88
import { TextDocumentSnapshot } from '../../../platform/editing/common/textDocumentSnapshot';
9+
import { APIUsage } from '../../../platform/networking/common/openai';
910
import { ThinkingData } from '../../../platform/thinking/common/thinking';
1011
import { ResourceMap } from '../../../util/vs/base/common/map';
1112
import { generateUuid } from '../../../util/vs/base/common/uuid';
@@ -30,6 +31,7 @@ export interface IToolCallRound {
3031
toolCalls: IToolCall[];
3132
thinking?: ThinkingData;
3233
statefulMarker?: string;
34+
usage?: APIUsage;
3335
}
3436

3537
export interface InternalToolReference extends vscode.ChatLanguageModelToolReference {

src/extension/prompt/common/toolCallRound.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55
import { FetchSuccess } from '../../../platform/chat/common/commonTypes';
6+
import { APIUsage } from '../../../platform/networking/common/openai';
67
import { isEncryptedThinkingDelta, ThinkingData, ThinkingDelta } from '../../../platform/thinking/common/thinking';
78
import { generateUuid } from '../../../util/vs/base/common/uuid';
89
import { IToolCall, IToolCallRound } from './intents';
@@ -27,7 +28,8 @@ export class ToolCallRound implements IToolCallRound {
2728
params.toolInputRetry,
2829
params.id,
2930
params.statefulMarker,
30-
params.thinking
31+
params.thinking,
32+
params.usage
3133
);
3234
round.summary = params.summary;
3335
return round;
@@ -39,14 +41,17 @@ export class ToolCallRound implements IToolCallRound {
3941
* @param toolInputRetry The number of times this round has been retried due to tool input validation failures
4042
* @param id A stable identifier for this round
4143
* @param statefulMarker Optional stateful marker used with the responses API
44+
* @param thinking Optional thinking data from the model
45+
* @param usage Optional API usage data for this round
4246
*/
4347
constructor(
4448
public readonly response: string,
4549
public readonly toolCalls: IToolCall[] = [],
4650
public readonly toolInputRetry: number = 0,
4751
public readonly id: string = ToolCallRound.generateID(),
4852
public readonly statefulMarker?: string,
49-
public readonly thinking?: ThinkingData
53+
public readonly thinking?: ThinkingData,
54+
public readonly usage?: APIUsage
5055
) { }
5156

5257
private static generateID(): string {

src/extension/prompt/node/chatParticipantRequestHandler.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { CanceledMessage, ChatLocation } from '../../../platform/chat/common/com
1212
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
1313
import { IIgnoreService } from '../../../platform/ignore/common/ignoreService';
1414
import { ILogService } from '../../../platform/log/common/logService';
15-
import { FilterReason } from '../../../platform/networking/common/openai';
15+
import { APIUsage, FilterReason } from '../../../platform/networking/common/openai';
1616
import { ITabsAndEditorsService } from '../../../platform/tabs/common/tabsAndEditorsService';
1717
import { getWorkspaceFileDisplayPath, IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
1818
import { ChatResponseStreamImpl } from '../../../util/common/chatResponseStreamImpl';
@@ -36,7 +36,7 @@ import { UnknownIntent } from '../../intents/node/unknownIntent';
3636
import { ContributedToolName } from '../../tools/common/toolNames';
3737
import { ChatVariablesCollection } from '../common/chatVariablesCollection';
3838
import { Conversation, getGlobalContextCacheKey, GlobalContextMessageMetadata, ICopilotChatResult, ICopilotChatResultIn, normalizeSummariesOnRounds, RenderedUserMessageMetadata, Turn, TurnStatus } from '../common/conversation';
39-
import { InternalToolReference } from '../common/intents';
39+
import { InternalToolReference, IToolCallRound } from '../common/intents';
4040
import { ChatTelemetryBuilder } from './chatParticipantTelemetry';
4141
import { DefaultIntentRequestHandler } from './defaultIntentRequestHandler';
4242
import { IDocumentContext } from './documentContext';
@@ -254,9 +254,14 @@ export class ChatParticipantRequestHandler {
254254

255255
result = await chatResult;
256256
const endpoint = await this._endpointProvider.getChatEndpoint(this.request);
257+
const resultMetadata = (result as ICopilotChatResultIn).metadata;
258+
const totalUsage = getTotalUsage(resultMetadata?.toolCallRounds);
259+
const usageStr = totalUsage
260+
? ` • ↑${totalUsage.prompt_tokens}${totalUsage.completion_tokens} tokens`
261+
: '';
257262
result.details = this._authService.copilotToken?.isNoAuthUser ?
258-
`${endpoint.name}` :
259-
`${endpoint.name}${endpoint.multiplier ?? 0}x`;
263+
`${endpoint.name}${usageStr}` :
264+
`${endpoint.name}${endpoint.multiplier ?? 0}x${usageStr}`;
260265
}
261266

262267
this._conversationStore.addConversation(this.turn.id, this.conversation);
@@ -463,3 +468,36 @@ function anchorPartToMarkdown(workspaceService: IWorkspaceService, anchor: ChatR
463468

464469
return `[${text}](${path} ${anchor.title ? `"${anchor.title}"` : ''})`;
465470
}
471+
472+
/**
473+
* Calculates the total API usage by summing usage from all tool call rounds.
474+
*/
475+
function getTotalUsage(toolCallRounds: readonly IToolCallRound[] | undefined): APIUsage | undefined {
476+
if (!toolCallRounds?.length) {
477+
return undefined;
478+
}
479+
480+
const initial: APIUsage = {
481+
completion_tokens: 0,
482+
prompt_tokens: 0,
483+
total_tokens: 0,
484+
prompt_tokens_details: { cached_tokens: 0 }
485+
};
486+
487+
const hasAnyUsage = toolCallRounds.some(round => round.usage);
488+
if (!hasAnyUsage) {
489+
return undefined;
490+
}
491+
492+
return toolCallRounds.reduce((acc, round): APIUsage => {
493+
const usage = round.usage || initial;
494+
return {
495+
completion_tokens: acc.completion_tokens + usage.completion_tokens,
496+
prompt_tokens: acc.prompt_tokens + usage.prompt_tokens,
497+
total_tokens: acc.total_tokens + usage.total_tokens,
498+
prompt_tokens_details: {
499+
cached_tokens: (acc.prompt_tokens_details?.cached_tokens ?? 0) + (usage.prompt_tokens_details?.cached_tokens ?? 0),
500+
}
501+
};
502+
}, initial);
503+
}

0 commit comments

Comments
 (0)