mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-22 07:41:23 -07:00
Feat/a2a expose usage metadata (#27288)
This commit is contained in:
committed by
GitHub
parent
ba04e99bea
commit
d1fa323cfb
@@ -294,6 +294,66 @@ describe('Task', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture usageMetadata on Finished event and include it in final status update', async () => {
|
||||||
|
const mockConfig = createMockConfig();
|
||||||
|
const mockEventBus: ExecutionEventBus = {
|
||||||
|
publish: vi.fn(),
|
||||||
|
on: vi.fn(),
|
||||||
|
off: vi.fn(),
|
||||||
|
once: vi.fn(),
|
||||||
|
removeAllListeners: vi.fn(),
|
||||||
|
finished: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error - Calling private constructor for test purposes.
|
||||||
|
const task = new Task(
|
||||||
|
'task-id',
|
||||||
|
'context-id',
|
||||||
|
mockConfig as Config,
|
||||||
|
mockEventBus,
|
||||||
|
);
|
||||||
|
|
||||||
|
const finishedEvent = {
|
||||||
|
type: GeminiEventType.Finished,
|
||||||
|
value: {
|
||||||
|
reason: 'STOP',
|
||||||
|
usageMetadata: {
|
||||||
|
promptTokenCount: 100,
|
||||||
|
candidatesTokenCount: 50,
|
||||||
|
totalTokenCount: 150,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await task.acceptAgentMessage(finishedEvent);
|
||||||
|
expect(task.usageMetadata).toEqual({
|
||||||
|
promptTokenCount: 100,
|
||||||
|
candidatesTokenCount: 50,
|
||||||
|
totalTokenCount: 150,
|
||||||
|
});
|
||||||
|
|
||||||
|
task.setTaskStateAndPublishUpdate(
|
||||||
|
'input-required',
|
||||||
|
{ kind: CoderAgentEvent.StateChangeEvent },
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true, // final
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockEventBus.publish).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
final: true,
|
||||||
|
metadata: expect.objectContaining({
|
||||||
|
usageMetadata: {
|
||||||
|
promptTokenCount: 100,
|
||||||
|
candidatesTokenCount: 50,
|
||||||
|
totalTokenCount: 150,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should update modelInfo and reflect it in metadata and status updates', async () => {
|
it('should update modelInfo and reflect it in metadata and status updates', async () => {
|
||||||
const mockConfig = createMockConfig();
|
const mockConfig = createMockConfig();
|
||||||
const mockEventBus: ExecutionEventBus = {
|
const mockEventBus: ExecutionEventBus = {
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ export class Task {
|
|||||||
currentAgentMessageId = uuidv4();
|
currentAgentMessageId = uuidv4();
|
||||||
promptCount = 0;
|
promptCount = 0;
|
||||||
autoExecute: boolean;
|
autoExecute: boolean;
|
||||||
|
usageMetadata?: {
|
||||||
|
promptTokenCount?: number;
|
||||||
|
candidatesTokenCount?: number;
|
||||||
|
totalTokenCount?: number;
|
||||||
|
cachedContentTokenCount?: number;
|
||||||
|
};
|
||||||
private get isYoloMatch(): boolean {
|
private get isYoloMatch(): boolean {
|
||||||
return (
|
return (
|
||||||
this.autoExecute || this.config.getApprovalMode() === ApprovalMode.YOLO
|
this.autoExecute || this.config.getApprovalMode() === ApprovalMode.YOLO
|
||||||
@@ -274,6 +280,7 @@ export class Task {
|
|||||||
userTier?: UserTierId;
|
userTier?: UserTierId;
|
||||||
error?: string;
|
error?: string;
|
||||||
traceId?: string;
|
traceId?: string;
|
||||||
|
usageMetadata?: Task['usageMetadata'];
|
||||||
} = {
|
} = {
|
||||||
coderAgent: coderAgentMessage,
|
coderAgent: coderAgentMessage,
|
||||||
model: this.modelInfo || this.config.getModel(),
|
model: this.modelInfo || this.config.getModel(),
|
||||||
@@ -288,6 +295,10 @@ export class Task {
|
|||||||
metadata.traceId = traceId;
|
metadata.traceId = traceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (final && this.usageMetadata) {
|
||||||
|
metadata.usageMetadata = this.usageMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: 'status-update',
|
kind: 'status-update',
|
||||||
taskId: this.id,
|
taskId: this.id,
|
||||||
@@ -857,8 +868,18 @@ export class Task {
|
|||||||
break;
|
break;
|
||||||
case GeminiEventType.Finished:
|
case GeminiEventType.Finished:
|
||||||
logger.info(`[Task ${this.id}] Agent finished its turn.`);
|
logger.info(`[Task ${this.id}] Agent finished its turn.`);
|
||||||
|
// Capture the usage metadata when the stream finishes
|
||||||
|
if (
|
||||||
|
event.value &&
|
||||||
|
typeof event.value === 'object' &&
|
||||||
|
'usageMetadata' in event.value
|
||||||
|
) {
|
||||||
|
this.usageMetadata = event.value
|
||||||
|
.usageMetadata as typeof this.usageMetadata;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GeminiEventType.ModelInfo:
|
case GeminiEventType.ModelInfo:
|
||||||
|
this.usageMetadata = undefined;
|
||||||
this.modelInfo = event.value;
|
this.modelInfo = event.value;
|
||||||
break;
|
break;
|
||||||
case GeminiEventType.Retry:
|
case GeminiEventType.Retry:
|
||||||
|
|||||||
Reference in New Issue
Block a user