Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,17 @@ export class GitHubPullRequestTitleAndDescriptionGenerator implements TitleAndDe
return patches;
}

async provideTitleAndDescription(context: { commitMessages: string[]; patches: string[] | { patch: string; fileUri: string; previousFileUri?: string }[]; issues?: { reference: string; content: string }[] }, token: CancellationToken): Promise<{ title: string; description?: string } | undefined> {
async provideTitleAndDescription(context: { commitMessages: string[]; patches: string[] | { patch: string; fileUri: string; previousFileUri?: string }[]; issues?: { reference: string; content: string }[]; template?: string }, token: CancellationToken): Promise<{ title: string; description?: string } | undefined> {
const commitMessages: string[] = context.commitMessages;
const allPatches: { patch: string; fileUri?: string; previousFileUri?: string }[] = isStringArray(context.patches) ? context.patches.map(patch => ({ patch })) : context.patches;
const patches = await this.excludePatches(allPatches);
const issues: { reference: string; content: string }[] | undefined = context.issues;
const template: string | undefined = context.template;

const endpoint = await this.endpointProvider.getChatEndpoint('gpt-4o-mini');
const charLimit = Math.floor((endpoint.modelMaxPromptTokens * 4) / 3);

const prompt = await this.createPRTitleAndDescriptionPrompt(commitMessages, patches, issues, charLimit);
const prompt = await this.createPRTitleAndDescriptionPrompt(commitMessages, patches, issues, template, charLimit);
const fetchResult = await endpoint
.makeChatRequest(
'githubPullRequestTitleAndDescriptionGenerator',
Expand All @@ -105,10 +106,10 @@ export class GitHubPullRequestTitleAndDescriptionGenerator implements TitleAndDe
return undefined;
}

return GitHubPullRequestTitleAndDescriptionGenerator.parseFetchResult(fetchResult.value);
return GitHubPullRequestTitleAndDescriptionGenerator.parseFetchResult(fetchResult.value, !!template);
}

public static parseFetchResult(value: string, retry: boolean = true): { title: string; description?: string } | undefined {
public static parseFetchResult(value: string, hasTemplate: boolean = false, retry: boolean = true): { title: string; description?: string } | undefined {
value = value.trim();
let workingValue = value;
let delimiter = '+++';
Expand All @@ -130,8 +131,13 @@ export class GitHubPullRequestTitleAndDescriptionGenerator implements TitleAndDe
// If there's only one line, split on newlines as the model has left out some +++ delimiters
splitOnLines = splitOnPlus[0].split('\n');
} else if (splitOnPlus.length > 1) {
const descriptionLines = splitOnPlus.slice(1).map(line => line.split('\n')).flat().filter(s => s.trim().length > 0);
splitOnLines = [splitOnPlus[0], ...descriptionLines];
if (hasTemplate) {
// When using a template, keep description whitespace as-is.
splitOnLines = splitOnPlus;
} else {
const descriptionLines = splitOnPlus.slice(1).map(line => line.split('\n')).flat().filter(s => s.trim().length > 0);
splitOnLines = [splitOnPlus[0], ...descriptionLines];
}
} else {
return undefined;
}
Expand All @@ -141,7 +147,7 @@ export class GitHubPullRequestTitleAndDescriptionGenerator implements TitleAndDe
if (splitOnLines.length === 1) {
title = splitOnLines[0].trim();
if (retry && value.includes('\n') && (value.split(delimiter).length === 3)) {
return this.parseFetchResult(value + delimiter, false);
return this.parseFetchResult(value + delimiter, hasTemplate, false);
}
} else if (splitOnLines.length > 1) {
title = splitOnLines[0].trim();
Expand All @@ -159,14 +165,14 @@ export class GitHubPullRequestTitleAndDescriptionGenerator implements TitleAndDe
if (title) {
title = title.replace(/Title\:\s/, '').trim();
title = title.replace(/^\"(?<title>.+)\"$/, (_match, title) => title);
if (description) {
if (description && !hasTemplate) {
description = description.replace(/Description\:\s/, '').trim();
}
return { title, description };
}
}

private async createPRTitleAndDescriptionPrompt(commitMessages: string[], patches: string[], issues: { reference: string; content: string }[] | undefined, charLimit: number): Promise<RenderPromptResult> {
private async createPRTitleAndDescriptionPrompt(commitMessages: string[], patches: string[], issues: { reference: string; content: string }[] | undefined, template: string | undefined, charLimit: number): Promise<RenderPromptResult> {
// Reserve 20% of the character limit for the safety rules and instructions
const availableChars = charLimit - Math.floor(charLimit * 0.2);

Expand All @@ -184,7 +190,7 @@ export class GitHubPullRequestTitleAndDescriptionGenerator implements TitleAndDe
}

const endpoint = await this.endpointProvider.getChatEndpoint('gpt-4o-mini');
const promptRenderer = PromptRenderer.create(this.instantiationService, endpoint, GitHubPullRequestPrompt, { commitMessages, issues, patches });
const promptRenderer = PromptRenderer.create(this.instantiationService, endpoint, GitHubPullRequestPrompt, { commitMessages, issues, patches, template });
return promptRenderer.render(undefined, undefined);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface GitHubPullRequestPromptProps extends BasePromptElementProps {
commitMessages: string[];
patches: string[];
issues: { reference: string; content: string }[] | undefined;
template: string | undefined;
}

interface GitHubPullRequestIdentityProps extends BasePromptElementProps {
Expand Down Expand Up @@ -76,6 +77,7 @@ class GitHubPullRequestSystemRules extends PromptElement<GitHubPullRequestIdenti
To compose the description, read through each commit and patch and tersly describe the intent of the changes, not the changes themselves. Do not list commits, files or patches. Do not make up an issue reference if the pull request isn't fixing an issue.<br />
If the pull request is fixing an issue, consider how the commits relate to the issue and include that in the description.<br />
Avoid saying "this PR" or similar. Avoid passive voice.<br />
If a template is specified, the description must match the template, filling in any required fields.<br />
The title and description of a pull request should be markdown and start with +++ and end with +++.<br />
<GitHubPullRequestSystemExamples issues={this.props.issues} />
</>
Expand All @@ -86,6 +88,7 @@ class GitHubPullRequestSystemRules extends PromptElement<GitHubPullRequestIdenti
interface GitHubPullRequestUserMessageProps extends BasePromptElementProps {
commitMessages: string[];
patches: string[];
template: string | undefined;
}

class GitHubPullRequestUserMessage extends PromptElement<GitHubPullRequestUserMessageProps> {
Expand All @@ -98,6 +101,14 @@ class GitHubPullRequestUserMessage extends PromptElement<GitHubPullRequestUserMe
{formattedCommitMessages}<br />
Below is a list of git patches that contain the file changes for all the files that will be included in the pull request:<br />
{formattedPatches}<br />
{this.props.template && (
<>
The pull request description should match the following template:<br />
```<br />
{this.props.template}<br />
```<br />
</>
)}
Based on the git patches and on the git commit messages above, the title and description of the pull request should be:<br />
</>
);
Expand All @@ -113,7 +124,7 @@ export class GitHubPullRequestPrompt extends PromptElement<GitHubPullRequestProm
<SafetyRules />
</SystemMessage>
<UserMessage>
<GitHubPullRequestUserMessage commitMessages={this.props.commitMessages} patches={this.props.patches} />
<GitHubPullRequestUserMessage commitMessages={this.props.commitMessages} patches={this.props.patches} template={this.props.template} />
<Tag priority={750} name='custom-instructions'>
<CustomInstructions
chatVariables={undefined}
Expand Down
2 changes: 1 addition & 1 deletion test/outcome/pr-title-and-description-context.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"name": "PR Title and Description [context] - Multiple commits without issue information",
"requests": [
"8428a33bed887a6b29356a7e240e42a90fcea91744ec3477395eb5d23dbed752"
"c661b19df36a5e6b216162071bf3a758898a0365e5d29c86deb4c01d5927a4ca"
]
}
]
Git LFS file not shown