Path: blob/main/extensions/copilot/src/platform/otel/common/test/genAiMetrics.spec.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { describe, expect, it, vi } from 'vitest';6import { Event } from '../../../../util/vs/base/common/event';7import { GenAiAttr, GenAiOperationName, GenAiProviderName, GenAiTokenType, StdAttr } from '../genAiAttributes';8import { GenAiMetrics } from '../genAiMetrics';9import { resolveOTelConfig } from '../otelConfig';10import type { IOTelService } from '../otelService';1112function createMockOTelService(): IOTelService & { recordMetric: ReturnType<typeof vi.fn>; incrementCounter: ReturnType<typeof vi.fn> } {13const config = resolveOTelConfig({ env: {}, extensionVersion: '1.0.0', sessionId: 'test' });14return {15_serviceBrand: undefined!,16config,17startSpan: vi.fn(),18startActiveSpan: vi.fn(),19getActiveTraceContext: vi.fn(),20storeTraceContext: vi.fn(),21getStoredTraceContext: vi.fn(),22runWithTraceContext: vi.fn((_ctx: any, fn: any) => fn()),23recordMetric: vi.fn(),24incrementCounter: vi.fn(),25emitLogRecord: vi.fn(),26flush: vi.fn(),27shutdown: vi.fn(),28injectCompletedSpan: vi.fn(),29onDidCompleteSpan: Event.None,30onDidEmitSpanEvent: Event.None,31};32}3334describe('GenAiMetrics', () => {35it('recordOperationDuration calls recordMetric with correct attributes', () => {36const otel = createMockOTelService();3738GenAiMetrics.recordOperationDuration(otel, 1.5, {39operationName: GenAiOperationName.CHAT,40providerName: GenAiProviderName.OPENAI,41requestModel: 'gpt-4o',42responseModel: 'gpt-4o-2024-05-13',43serverAddress: 'api.copilot.com',44errorType: 'timeout',45});4647expect(otel.recordMetric).toHaveBeenCalledWith('gen_ai.client.operation.duration', 1.5, {48[GenAiAttr.OPERATION_NAME]: 'chat',49[GenAiAttr.PROVIDER_NAME]: 'openai',50[GenAiAttr.REQUEST_MODEL]: 'gpt-4o',51[GenAiAttr.RESPONSE_MODEL]: 'gpt-4o-2024-05-13',52[StdAttr.SERVER_ADDRESS]: 'api.copilot.com',53[StdAttr.ERROR_TYPE]: 'timeout',54});55});5657it('recordTokenUsage calls recordMetric with token type', () => {58const otel = createMockOTelService();5960GenAiMetrics.recordTokenUsage(otel, 1000, 'input', {61operationName: GenAiOperationName.CHAT,62providerName: GenAiProviderName.OPENAI,63requestModel: 'gpt-4o',64});6566expect(otel.recordMetric).toHaveBeenCalledWith('gen_ai.client.token.usage', 1000, {67[GenAiAttr.OPERATION_NAME]: 'chat',68[GenAiAttr.PROVIDER_NAME]: 'openai',69[GenAiAttr.TOKEN_TYPE]: GenAiTokenType.INPUT,70[GenAiAttr.REQUEST_MODEL]: 'gpt-4o',71});72});7374it('recordToolCallCount increments counter', () => {75const otel = createMockOTelService();7677GenAiMetrics.recordToolCallCount(otel, 'readFile', true);7879expect(otel.incrementCounter).toHaveBeenCalledWith('copilot_chat.tool.call.count', 1, {80[GenAiAttr.TOOL_NAME]: 'readFile',81success: true,82});83});8485it('recordToolCallDuration records histogram', () => {86const otel = createMockOTelService();8788GenAiMetrics.recordToolCallDuration(otel, 'runCommand', 500);8990expect(otel.recordMetric).toHaveBeenCalledWith('copilot_chat.tool.call.duration', 500, {91[GenAiAttr.TOOL_NAME]: 'runCommand',92});93});9495it('recordAgentDuration records histogram', () => {96const otel = createMockOTelService();9798GenAiMetrics.recordAgentDuration(otel, 'copilot', 15.2);99100expect(otel.recordMetric).toHaveBeenCalledWith('copilot_chat.agent.invocation.duration', 15.2, {101[GenAiAttr.AGENT_NAME]: 'copilot',102});103});104105it('incrementSessionCount increments counter', () => {106const otel = createMockOTelService();107108GenAiMetrics.incrementSessionCount(otel);109110expect(otel.incrementCounter).toHaveBeenCalledWith('copilot_chat.session.count');111});112113it('omits optional attributes when not provided', () => {114const otel = createMockOTelService();115116GenAiMetrics.recordOperationDuration(otel, 0.5, {117operationName: GenAiOperationName.CHAT,118providerName: GenAiProviderName.OPENAI,119requestModel: 'gpt-4o',120});121122const attrs = otel.recordMetric.mock.calls[0][2];123expect(attrs).not.toHaveProperty(GenAiAttr.RESPONSE_MODEL);124expect(attrs).not.toHaveProperty(StdAttr.SERVER_ADDRESS);125expect(attrs).not.toHaveProperty(StdAttr.ERROR_TYPE);126});127});128129130