Path: blob/main/extensions/copilot/src/extension/prompt/node/gitCommitMessageGenerator.ts
13399 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 type { CancellationToken } from 'vscode';6import { IAuthenticationService } from '../../../platform/authentication/common/authentication';7import { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';8import { IConversationOptions } from '../../../platform/chat/common/conversationOptions';9import { IInteractionService } from '../../../platform/chat/common/interactionService';10import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';11import { Diff } from '../../../platform/git/common/gitDiffService';12import { INotificationService } from '../../../platform/notification/common/notificationService';13import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';14import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';15import { PromptRenderer } from '../../prompts/node/base/promptRenderer';16import { GitCommitMessagePrompt } from '../../prompts/node/git/gitCommitMessagePrompt';17import { RecentCommitMessages } from '../common/repository';1819type ResponseFormat = 'noTextCodeBlock' | 'oneTextCodeBlock' | 'multipleTextCodeBlocks';2021export class GitCommitMessageGenerator {22constructor(23@IConversationOptions private readonly conversationOptions: IConversationOptions,24@IEndpointProvider private readonly endpointProvider: IEndpointProvider,25@IInstantiationService private readonly instantiationService: IInstantiationService,26@ITelemetryService private readonly telemetryService: ITelemetryService,27@INotificationService private readonly notificationService: INotificationService,28@IInteractionService private readonly interactionService: IInteractionService,29@IAuthenticationService private readonly authService: IAuthenticationService,30) { }3132async generateGitCommitMessage(repositoryName: string, branchName: string, changes: Diff[], recentCommitMessages: RecentCommitMessages, attemptCount: number, token: CancellationToken): Promise<string | undefined> {33const startTime = Date.now();3435const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');36const promptRenderer = PromptRenderer.create(this.instantiationService, endpoint, GitCommitMessagePrompt, { repositoryName, branchName, changes, recentCommitMessages });37const prompt = await promptRenderer.render(undefined, undefined);3839const temperature = Math.min(40this.conversationOptions.temperature * (1 + attemptCount),412 /* MAX temperature - https://platform.openai.com/docs/api-reference/chat/create#chat/create-temperature */42);4344const requestStartTime = Date.now();45this.interactionService.startInteraction();46const fetchResult = await endpoint47.makeChatRequest(48'gitCommitMessageGenerator',49prompt.messages,50undefined,51token,52ChatLocation.Other,53undefined,54{ temperature },55true56);5758/* __GDPR__59"git.generateCommitMessage" : {60"owner": "lszomoru",61"comment": "Metadata about the git commit message generation",62"model": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The model that is used in the endpoint." },63"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the current request turn." },64"responseType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The result type of the response." },65"attemptCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many times the user has retried." },66"diffFileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The number of files in the commit." },67"diffLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The length of the diffs in the commit." },68"timeToRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to start the request." },69"timeToComplete": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to complete the request." }70}71*/72this.telemetryService.sendMSFTTelemetryEvent('git.generateCommitMessage', {73model: endpoint.model,74requestId: fetchResult.requestId,75responseType: fetchResult.type76}, {77attemptCount: attemptCount + 1,78diffFileCount: changes.length,79diffLength: changes.map(c => c.diff).join('').length,80timeToRequest: requestStartTime - startTime,81timeToComplete: Date.now() - startTime82});8384if (fetchResult.type === ChatFetchResponseType.QuotaExceeded || (fetchResult.type === ChatFetchResponseType.RateLimited && this.authService.copilotToken?.isNoAuthUser)) {85await this.notificationService.showQuotaExceededDialog({ isNoAuthUser: this.authService.copilotToken?.isNoAuthUser ?? false });86return undefined;87}8889if (fetchResult.type !== ChatFetchResponseType.Success) {90return undefined;91}9293const [responseFormat, commitMessage] = this.processGeneratedCommitMessage(fetchResult.value);94if (responseFormat !== 'oneTextCodeBlock') {95/* __GDPR__96"git.generateCommitMessageIncorrectResponseFormat" : {97"owner": "lszomoru",98"comment": "Metadata about the git commit message generation when the response is not in the expected format",99"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the current request turn." },100"responseFormat": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The type of the response format." }101}102*/103this.telemetryService.sendMSFTTelemetryEvent('git.generateCommitMessageIncorrectResponseFormat', { requestId: fetchResult.requestId, responseFormat });104}105106return commitMessage;107}108109private processGeneratedCommitMessage(raw: string): [ResponseFormat, string] {110const textCodeBlockRegex = /^```text\s*([\s\S]+?)\s*```$/m;111const textCodeBlockMatch = textCodeBlockRegex.exec(raw);112113if (textCodeBlockMatch === null) {114return ['noTextCodeBlock', raw];115}116if (textCodeBlockMatch.length !== 2) {117return ['multipleTextCodeBlocks', raw];118}119120return ['oneTextCodeBlock', textCodeBlockMatch[1]];121}122}123124125