Skip to content

Commit 7f2b284

Browse files
committed
Improves AI message generation
1 parent 6b54385 commit 7f2b284

File tree

11 files changed

+357
-247
lines changed

11 files changed

+357
-247
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3667,21 +3667,21 @@
36673667
},
36683668
"gitlens.experimental.generateCommitMessagePrompt": {
36693669
"type": "string",
3670-
"default": "Now, please generate a commit message. Ensure that it includes a precise and informative subject line that succinctly summarizes the crux of the changes in under 50 characters. If necessary, follow with an explanatory body providing insight into the nature of the changes, the reasoning behind them, and any significant consequences or considerations arising from them. Conclude with any relevant issue references at the end of the message.",
3670+
"default": "Now, based on the provided code diff and any additional context, create a concise but meaningful commit message following the instructions above.",
36713671
"markdownDescription": "Specifies the prompt to use to tell the AI provider how to structure or format the generated commit message",
36723672
"scope": "window",
36733673
"order": 2
36743674
},
36753675
"gitlens.experimental.generateCloudPatchMessagePrompt": {
36763676
"type": "string",
3677-
"default": "Now, please generate a title and optional description. Ensure that it includes a precise and informative subject line that succinctly summarizes the crux of the changes in under 50 characters. If necessary, follow with an explanatory body providing insight into the nature of the changes, the reasoning behind them, and any significant consequences or considerations arising from them. Conclude with any relevant issue references at the end of the message.",
3677+
"default": "Now, based on the provided code diff and any additional context, create a concise but meaningful title and description following the instructions above.",
36783678
"markdownDescription": "Specifies the prompt to use to tell the AI provider how to structure or format the generated title and description",
36793679
"scope": "window",
36803680
"order": 3
36813681
},
36823682
"gitlens.experimental.generateCodeSuggestMessagePrompt": {
36833683
"type": "string",
3684-
"default": "Now, please generate a title and optional description. Ensure that it includes a precise and informative subject line that succinctly summarizes the crux of the changes in under 50 characters. If necessary, follow with an explanatory body providing insight into the nature of the changes, the reasoning behind them, and any significant consequences or considerations arising from them. Conclude with any relevant issue references at the end of the message.",
3684+
"default": "Now, based on the provided code diff and any additional context, create a concise but meaningful code review title and description following the instructions above.",
36853685
"markdownDescription": "Specifies the prompt to use to tell the AI provider how to structure or format the generated title and description",
36863686
"scope": "window",
36873687
"order": 3

src/ai/aiProviderService.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,22 @@ export class AIProviderService implements Disposable {
200200
changes: string[],
201201
sourceContext: { source: Sources },
202202
options?: { cancellation?: CancellationToken; context?: string; progress?: ProgressOptions },
203-
): Promise<string | undefined>;
203+
): Promise<{ summary: string; body: string } | undefined>;
204204
async generateCommitMessage(
205205
repoPath: Uri,
206206
sourceContext: { source: Sources },
207207
options?: { cancellation?: CancellationToken; context?: string; progress?: ProgressOptions },
208-
): Promise<string | undefined>;
208+
): Promise<{ summary: string; body: string } | undefined>;
209209
async generateCommitMessage(
210210
repository: Repository,
211211
sourceContext: { source: Sources },
212212
options?: { cancellation?: CancellationToken; context?: string; progress?: ProgressOptions },
213-
): Promise<string | undefined>;
213+
): Promise<{ summary: string; body: string } | undefined>;
214214
async generateCommitMessage(
215215
changesOrRepoOrPath: string[] | Repository | Uri,
216216
sourceContext: { source: Sources },
217217
options?: { cancellation?: CancellationToken; context?: string; progress?: ProgressOptions },
218-
): Promise<string | undefined> {
218+
): Promise<{ summary: string; body: string } | undefined> {
219219
const changes: string | undefined = await this.getChanges(changesOrRepoOrPath);
220220
if (changes == null) return undefined;
221221

@@ -264,7 +264,8 @@ export class AIProviderService implements Disposable {
264264
payload['output.length'] = result?.length;
265265
this.container.telemetry.sendEvent('ai/generate', { ...payload, duration: Date.now() - start }, source);
266266

267-
return result;
267+
if (result == null) return undefined;
268+
return parseGeneratedMessage(result);
268269
} catch (ex) {
269270
this.container.telemetry.sendEvent(
270271
'ai/generate',
@@ -291,7 +292,7 @@ export class AIProviderService implements Disposable {
291292
progress?: ProgressOptions;
292293
codeSuggestion?: boolean;
293294
},
294-
): Promise<string | undefined> {
295+
): Promise<{ summary: string; body: string } | undefined> {
295296
const changes: string | undefined = await this.getChanges(changesOrRepoOrPath);
296297
if (changes == null) return undefined;
297298

@@ -342,7 +343,8 @@ export class AIProviderService implements Disposable {
342343
payload['output.length'] = result?.length;
343344
this.container.telemetry.sendEvent('ai/generate', { ...payload, duration: Date.now() - start }, source);
344345

345-
return result;
346+
if (result == null) return undefined;
347+
return parseGeneratedMessage(result);
346348
} catch (ex) {
347349
this.container.telemetry.sendEvent(
348350
'ai/generate',
@@ -633,16 +635,26 @@ export async function getApiKey(
633635
return apiKey;
634636
}
635637

636-
export function extractDraftMessage(
637-
message: string,
638-
splitter = '\n\n',
639-
): { title: string; description: string | undefined } {
640-
const firstBreak = message.indexOf(splitter) ?? 0;
641-
const title = firstBreak > -1 ? message.substring(0, firstBreak) : message;
642-
const description = firstBreak > -1 ? message.substring(firstBreak + splitter.length) : undefined;
638+
function parseGeneratedMessage(result: string): { summary: string; body: string } {
639+
let summary = result.match(/<summary>\s?([\s\S]*?)\s?<\/summary>/)?.[1];
640+
let body = result.match(/<body>\s?([\s\S]*?)\s?<\/body>/)?.[1];
641+
642+
result = result.trim();
643+
if ((summary == null || body == null) && result) {
644+
debugger;
645+
646+
const index = result.indexOf('\n');
647+
if (index === -1) {
648+
summary = '';
649+
body = result;
650+
} else {
651+
summary = result.substring(0, index);
652+
body = result.substring(index + 1);
653+
}
654+
}
643655

644656
return {
645-
title: title,
646-
description: description,
657+
summary: summary ?? '',
658+
body: body ?? '',
647659
};
648660
}

src/ai/anthropicProvider.ts

Lines changed: 46 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ import type { TelemetryEvents } from '../constants.telemetry';
66
import type { Container } from '../container';
77
import { CancellationError } from '../errors';
88
import { sum } from '../system/iterable';
9+
import { interpolate } from '../system/string';
910
import { configuration } from '../system/vscode/configuration';
1011
import type { Storage } from '../system/vscode/storage';
1112
import type { AIModel, AIProvider } from './aiProviderService';
1213
import { getApiKey as getApiKeyCore, getMaxCharacters } from './aiProviderService';
13-
import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMessageSystemPrompt } from './prompts';
14+
import {
15+
generateCloudPatchMessageSystemPrompt,
16+
generateCloudPatchMessageUserPrompt,
17+
generateCodeSuggestMessageSystemPrompt,
18+
generateCodeSuggestMessageUserPrompt,
19+
generateCommitMessageSystemPrompt,
20+
generateCommitMessageUserPrompt,
21+
} from './prompts';
1422

1523
const provider = { id: 'anthropic', name: 'Anthropic' } as const;
1624
type LegacyModels = Extract<AnthropicModels, 'claude-instant-1' | 'claude-2'>;
@@ -81,9 +89,10 @@ export class AnthropicProvider implements AIProvider<typeof provider.id> {
8189
diff: string,
8290
reporting: TelemetryEvents['ai/generate'],
8391
promptConfig: {
92+
type: 'commit' | 'cloud-patch' | 'code-suggestion';
8493
systemPrompt: string;
85-
customPrompt: string;
86-
contextName: string;
94+
userPrompt: string;
95+
customInstructions?: string;
8796
},
8897
options?: { cancellation?: CancellationToken; context?: string },
8998
): Promise<string | undefined> {
@@ -99,15 +108,14 @@ export class AnthropicProvider implements AIProvider<typeof provider.id> {
99108
model as LegacyModel,
100109
apiKey,
101110
(max, retries) => {
102-
const code = diff.substring(0, max);
103-
let prompt = `\n\nHuman: ${promptConfig.systemPrompt}\n\nHuman: Here is the code diff to use to generate the ${promptConfig.contextName}:\n\n${code}\n`;
104-
if (options?.context) {
105-
prompt += `\nHuman: Here is additional context which should be taken into account when generating the ${promptConfig.contextName}:\n\n${options.context}\n`;
106-
}
107-
if (promptConfig.customPrompt) {
108-
prompt += `\nHuman: ${promptConfig.customPrompt}\n`;
109-
}
110-
prompt += '\nAssistant:';
111+
const prompt = `\n\nHuman: ${promptConfig.systemPrompt}\n\n${interpolate(
112+
promptConfig.userPrompt,
113+
{
114+
diff: diff.substring(0, max),
115+
context: options?.context ?? '',
116+
instructions: promptConfig.customInstructions ?? '',
117+
},
118+
)}\n\nAssistant:`;
111119

112120
reporting['retry.count'] = retries;
113121
reporting['input.length'] = (reporting['input.length'] ?? 0) + prompt.length;
@@ -123,39 +131,18 @@ export class AnthropicProvider implements AIProvider<typeof provider.id> {
123131
apiKey,
124132
promptConfig.systemPrompt,
125133
(max, retries) => {
126-
const code = diff.substring(0, max);
127134
const messages: Message[] = [
128135
{
129136
role: 'user',
130137
content: [
131138
{
132139
type: 'text',
133-
text: `Here is the code diff to use to generate the ${promptConfig.contextName}:`,
134-
},
135-
{
136-
type: 'text',
137-
text: code,
140+
text: interpolate(promptConfig.userPrompt, {
141+
diff: diff.substring(0, max),
142+
context: options?.context ?? '',
143+
instructions: promptConfig.customInstructions ?? '',
144+
}),
138145
},
139-
...(options?.context
140-
? ([
141-
{
142-
type: 'text',
143-
text: `Here is additional context which should be taken into account when generating the ${promptConfig.contextName}:`,
144-
},
145-
{
146-
type: 'text',
147-
text: options.context,
148-
},
149-
] satisfies Message['content'])
150-
: []),
151-
...(promptConfig.customPrompt
152-
? ([
153-
{
154-
type: 'text',
155-
text: promptConfig.customPrompt,
156-
},
157-
] satisfies Message['content'])
158-
: []),
159146
],
160147
},
161148
];
@@ -180,7 +167,7 @@ export class AnthropicProvider implements AIProvider<typeof provider.id> {
180167

181168
return result;
182169
} catch (ex) {
183-
throw new Error(`Unable to generate ${promptConfig.contextName}: ${ex.message}`);
170+
throw new Error(`Unable to generate ${promptConfig.type} message: ${ex.message}`);
184171
}
185172
}
186173

@@ -190,27 +177,28 @@ export class AnthropicProvider implements AIProvider<typeof provider.id> {
190177
reporting: TelemetryEvents['ai/generate'],
191178
options?: { cancellation?: CancellationToken; context?: string; codeSuggestion?: boolean },
192179
): Promise<string | undefined> {
193-
let customPrompt =
194-
options?.codeSuggestion === true
195-
? configuration.get('experimental.generateCodeSuggestionMessagePrompt')
196-
: configuration.get('experimental.generateCloudPatchMessagePrompt');
197-
if (!customPrompt.endsWith('.')) {
198-
customPrompt += '.';
180+
let codeSuggestion;
181+
if (options != null) {
182+
({ codeSuggestion, ...options } = options ?? {});
199183
}
200184

201185
return this.generateMessage(
202186
model,
203187
diff,
204188
reporting,
205-
{
206-
systemPrompt:
207-
options?.codeSuggestion === true ? codeSuggestMessageSystemPrompt : cloudPatchMessageSystemPrompt,
208-
customPrompt: customPrompt,
209-
contextName:
210-
options?.codeSuggestion === true
211-
? 'code suggestion title and description'
212-
: 'cloud patch title and description',
213-
},
189+
codeSuggestion
190+
? {
191+
type: 'code-suggestion',
192+
systemPrompt: generateCodeSuggestMessageSystemPrompt,
193+
userPrompt: generateCodeSuggestMessageUserPrompt,
194+
customInstructions: configuration.get('experimental.generateCodeSuggestionMessagePrompt'),
195+
}
196+
: {
197+
type: 'cloud-patch',
198+
systemPrompt: generateCloudPatchMessageSystemPrompt,
199+
userPrompt: generateCloudPatchMessageUserPrompt,
200+
customInstructions: configuration.get('experimental.generateCloudPatchMessagePrompt'),
201+
},
214202
options,
215203
);
216204
}
@@ -221,19 +209,15 @@ export class AnthropicProvider implements AIProvider<typeof provider.id> {
221209
reporting: TelemetryEvents['ai/generate'],
222210
options?: { cancellation?: CancellationToken; context?: string },
223211
): Promise<string | undefined> {
224-
let customPrompt = configuration.get('experimental.generateCommitMessagePrompt');
225-
if (!customPrompt.endsWith('.')) {
226-
customPrompt += '.';
227-
}
228-
229212
return this.generateMessage(
230213
model,
231214
diff,
232215
reporting,
233216
{
234-
systemPrompt: commitMessageSystemPrompt,
235-
customPrompt: customPrompt,
236-
contextName: 'commit message',
217+
type: 'commit',
218+
systemPrompt: generateCommitMessageSystemPrompt,
219+
userPrompt: generateCommitMessageUserPrompt,
220+
customInstructions: configuration.get('experimental.generateCommitMessagePrompt'),
237221
},
238222
options,
239223
);

0 commit comments

Comments
 (0)