Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompt/node/gitCommitMessageGenerator.ts
13399 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type { CancellationToken } from 'vscode';
7
import { IAuthenticationService } from '../../../platform/authentication/common/authentication';
8
import { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';
9
import { IConversationOptions } from '../../../platform/chat/common/conversationOptions';
10
import { IInteractionService } from '../../../platform/chat/common/interactionService';
11
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
12
import { Diff } from '../../../platform/git/common/gitDiffService';
13
import { INotificationService } from '../../../platform/notification/common/notificationService';
14
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';
15
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
16
import { PromptRenderer } from '../../prompts/node/base/promptRenderer';
17
import { GitCommitMessagePrompt } from '../../prompts/node/git/gitCommitMessagePrompt';
18
import { RecentCommitMessages } from '../common/repository';
19
20
type ResponseFormat = 'noTextCodeBlock' | 'oneTextCodeBlock' | 'multipleTextCodeBlocks';
21
22
export class GitCommitMessageGenerator {
23
constructor(
24
@IConversationOptions private readonly conversationOptions: IConversationOptions,
25
@IEndpointProvider private readonly endpointProvider: IEndpointProvider,
26
@IInstantiationService private readonly instantiationService: IInstantiationService,
27
@ITelemetryService private readonly telemetryService: ITelemetryService,
28
@INotificationService private readonly notificationService: INotificationService,
29
@IInteractionService private readonly interactionService: IInteractionService,
30
@IAuthenticationService private readonly authService: IAuthenticationService,
31
) { }
32
33
async generateGitCommitMessage(repositoryName: string, branchName: string, changes: Diff[], recentCommitMessages: RecentCommitMessages, attemptCount: number, token: CancellationToken): Promise<string | undefined> {
34
const startTime = Date.now();
35
36
const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');
37
const promptRenderer = PromptRenderer.create(this.instantiationService, endpoint, GitCommitMessagePrompt, { repositoryName, branchName, changes, recentCommitMessages });
38
const prompt = await promptRenderer.render(undefined, undefined);
39
40
const temperature = Math.min(
41
this.conversationOptions.temperature * (1 + attemptCount),
42
2 /* MAX temperature - https://platform.openai.com/docs/api-reference/chat/create#chat/create-temperature */
43
);
44
45
const requestStartTime = Date.now();
46
this.interactionService.startInteraction();
47
const fetchResult = await endpoint
48
.makeChatRequest(
49
'gitCommitMessageGenerator',
50
prompt.messages,
51
undefined,
52
token,
53
ChatLocation.Other,
54
undefined,
55
{ temperature },
56
true
57
);
58
59
/* __GDPR__
60
"git.generateCommitMessage" : {
61
"owner": "lszomoru",
62
"comment": "Metadata about the git commit message generation",
63
"model": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The model that is used in the endpoint." },
64
"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the current request turn." },
65
"responseType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The result type of the response." },
66
"attemptCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many times the user has retried." },
67
"diffFileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The number of files in the commit." },
68
"diffLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The length of the diffs in the commit." },
69
"timeToRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to start the request." },
70
"timeToComplete": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to complete the request." }
71
}
72
*/
73
this.telemetryService.sendMSFTTelemetryEvent('git.generateCommitMessage', {
74
model: endpoint.model,
75
requestId: fetchResult.requestId,
76
responseType: fetchResult.type
77
}, {
78
attemptCount: attemptCount + 1,
79
diffFileCount: changes.length,
80
diffLength: changes.map(c => c.diff).join('').length,
81
timeToRequest: requestStartTime - startTime,
82
timeToComplete: Date.now() - startTime
83
});
84
85
if (fetchResult.type === ChatFetchResponseType.QuotaExceeded || (fetchResult.type === ChatFetchResponseType.RateLimited && this.authService.copilotToken?.isNoAuthUser)) {
86
await this.notificationService.showQuotaExceededDialog({ isNoAuthUser: this.authService.copilotToken?.isNoAuthUser ?? false });
87
return undefined;
88
}
89
90
if (fetchResult.type !== ChatFetchResponseType.Success) {
91
return undefined;
92
}
93
94
const [responseFormat, commitMessage] = this.processGeneratedCommitMessage(fetchResult.value);
95
if (responseFormat !== 'oneTextCodeBlock') {
96
/* __GDPR__
97
"git.generateCommitMessageIncorrectResponseFormat" : {
98
"owner": "lszomoru",
99
"comment": "Metadata about the git commit message generation when the response is not in the expected format",
100
"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the current request turn." },
101
"responseFormat": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The type of the response format." }
102
}
103
*/
104
this.telemetryService.sendMSFTTelemetryEvent('git.generateCommitMessageIncorrectResponseFormat', { requestId: fetchResult.requestId, responseFormat });
105
}
106
107
return commitMessage;
108
}
109
110
private processGeneratedCommitMessage(raw: string): [ResponseFormat, string] {
111
const textCodeBlockRegex = /^```text\s*([\s\S]+?)\s*```$/m;
112
const textCodeBlockMatch = textCodeBlockRegex.exec(raw);
113
114
if (textCodeBlockMatch === null) {
115
return ['noTextCodeBlock', raw];
116
}
117
if (textCodeBlockMatch.length !== 2) {
118
return ['multipleTextCodeBlocks', raw];
119
}
120
121
return ['oneTextCodeBlock', textCodeBlockMatch[1]];
122
}
123
}
124
125