Path: blob/main/src/vs/workbench/contrib/chat/browser/telemetry/chatModelCountTelemetry.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 { Disposable } from '../../../../../base/common/lifecycle.js';6import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';7import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../../common/contributions.js';8import { IChatService } from '../../common/chatService/chatService.js';9import { ChatAgentLocation } from '../../common/constants.js';10import { IChatWidgetService } from '../chat.js';1112type ChatModelCountEvent = {13totalModels: number;14modelsOpenInWidgets: number;15backgroundModels: number;16backgroundModels_modifiedEditsKeepAlive: number;17backgroundModels_requestInProgressKeepAlive: number;18backgroundModels_otherHolders: number;19};2021type ChatModelCountClassification = {22totalModels: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Total number of live chat models.' };23modelsOpenInWidgets: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of chat models that are open in a chat widget or editor.' };24backgroundModels: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of chat models with no open widget.' };25backgroundModels_modifiedEditsKeepAlive: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of background models held alive by the ChatModel#modifiedEditsKeepAlive reference (has pending edits).' };26backgroundModels_requestInProgressKeepAlive: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of background models held alive by the ChatModel#requestInProgressKeepAlive reference (request is running).' };27backgroundModels_otherHolders: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of background models with unrecognized holders (potential leaks).' };28};2930type ChatModelsAtStartupClassification = ChatModelCountClassification & {31owner: 'roblourens';32comment: 'Tracks chat model counts at startup.';33};3435type ChatModelCreatedEvent = ChatModelCountEvent & {36newModelLocation: string;37};3839type ChatModelCreatedClassification = ChatModelCountClassification & {40newModelLocation: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ChatAgentLocation of the newly created chat model.' };41owner: 'roblourens';42comment: 'Tracks chat model counts each time a new chat model is created, to detect accumulation of background sessions over the lifetime of a window.';43};4445/**46* Logs telemetry about how many chat models are live at two moments:47* 1. At startup, after sessions with pending edits have been revived.48* 2. Each time a new chat model is created (skipping the very first one).49*50* Both events share the same model-count snapshot logic to track background51* session accumulation.52*/53export class ChatModelCountTelemetry extends Disposable implements IWorkbenchContribution {5455static readonly ID = 'workbench.contrib.chatModelCountTelemetry';5657constructor(58@IChatService private readonly chatService: IChatService,59@IChatWidgetService private readonly chatWidgetService: IChatWidgetService,60@ITelemetryService private readonly telemetryService: ITelemetryService,61) {62super();63this.logStartupTelemetry();64this._register(this.chatService.onDidCreateModel(model => this.onDidCreateModel(model.initialLocation)));65}6667private logStartupTelemetry(): void {68this.telemetryService.publicLog2<ChatModelCountEvent, ChatModelsAtStartupClassification>('chat.modelsAtStartup', this.getSnapshot());69}7071private onDidCreateModel(newModelLocation: ChatAgentLocation): void {72const snapshot = this.getSnapshot();7374// Skip the trivial case of the very first chat model in the window75if (snapshot.totalModels <= 1) {76return;77}7879this.telemetryService.publicLog2<ChatModelCreatedEvent, ChatModelCreatedClassification>('chat.modelCreatedStats', {80...snapshot,81newModelLocation,82});83}8485private getSnapshot(): ChatModelCountEvent {86const snapshot = this.chatService.getChatModelReferenceDebugInfo();8788let modelsOpenInWidgets = 0;89let backgroundModels = 0;90let backgroundModels_modifiedEditsKeepAlive = 0;91let backgroundModels_requestInProgressKeepAlive = 0;92let backgroundModels_otherHolders = 0;9394for (const model of snapshot.models) {95if (this.chatWidgetService.getWidgetBySessionResource(model.sessionResource)) {96modelsOpenInWidgets++;97} else {98backgroundModels++;99let hasOther = false;100for (const { holder } of model.holders) {101if (holder === 'ChatModel#modifiedEditsKeepAlive') {102backgroundModels_modifiedEditsKeepAlive++;103} else if (holder === 'ChatModel#requestInProgressKeepAlive') {104backgroundModels_requestInProgressKeepAlive++;105} else {106hasOther = true;107}108}109if (hasOther) {110backgroundModels_otherHolders++;111}112}113}114115return {116totalModels: snapshot.totalModels,117modelsOpenInWidgets,118backgroundModels,119backgroundModels_modifiedEditsKeepAlive,120backgroundModels_requestInProgressKeepAlive,121backgroundModels_otherHolders,122};123}124}125126registerWorkbenchContribution2(ChatModelCountTelemetry.ID, ChatModelCountTelemetry, WorkbenchPhase.AfterRestored);127128129