Skip to content

Commit

Permalink
feat: ET-1429: Single Line Commit Messages (#4)
Browse files Browse the repository at this point in the history
* refactor: ET-1429: update prompts.ts to clarify commit message instructions

* feat: ET-1429: add support for generating a single commit message by combining multiple commit messages into a single commit message title and description

* chore: ET-1429: remove extra new line character when joining commit messages

* feat: ET-1429: fix issueID variable being redeclared and not being properly checked for existence before confirming Issue ID

---------

Co-authored-by: Matthew Salter <[email protected]>
  • Loading branch information
mattsalt123 and Matthew Salter authored Nov 1, 2023
1 parent 2ab378a commit 9a1e4c3
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 17 deletions.
75 changes: 71 additions & 4 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from './commands/config';
import { GenerateCommitMessageErrorEnum } from './generateCommitMessageFromGitDiff';
import { tokenCount } from './utils/tokenCount';
import { IDENTITY } from './prompts';

const config = getConfig();

Expand Down Expand Up @@ -51,16 +52,17 @@ class OpenAi {
constructor() {
switch (API_TYPE) {
case AI_TYPE.AZURE:
this.openAiApiConfiguration.baseOptions = {
this.openAiApiConfiguration.baseOptions = {
headers: {
"api-key": API_KEY,
'api-key': API_KEY
},
params: {
'api-version': API_VERSION,
'api-version': API_VERSION
}
};
if (BASE_PATH) {
this.openAiApiConfiguration.basePath = BASE_PATH + 'openai/deployments/' + DEPLOYMENT;
this.openAiApiConfiguration.basePath =
BASE_PATH + 'openai/deployments/' + DEPLOYMENT;
}
break;
case AI_TYPE.OPENAI:
Expand All @@ -73,6 +75,71 @@ class OpenAi {
this.openAI = new OpenAIApi(this.openAiApiConfiguration);
}

public generateSingleCommitMessage = async (
messages: string
): Promise<string | undefined> => {
const params = {
model: MODEL,
messages,
temperature: 0,
top_p: 0.1,
max_tokens: MAX_TOKENS || 500
};
try {
const completionReponse = await this.openAI.createChatCompletion({
...params,
messages: [
{
role: 'system',
content: ` ${IDENTITY} Your mission is to summarise multiple commit messages into a single commit message.`
},
{
role: 'user',
content: `Summarise the following commit messages into a single commit message title ensuring the title format stays the same.
${
config?.OCO_EMOJI
? 'Use GitMoji convention to preface the summarised commit.'
: 'Do not preface the commit with anything.'
}
${
config?.OCO_DESCRIPTION
? 'Summarise the descriptions from the multiple messages into a single short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.'
: 'The summarised commit message should just be one line with a commit message title and no description.'
}
${
config?.OCO_ISSUE_ENABLED
? `You must also keep the Issue ID in the summarised commit message title.`
: 'Don\'t include an Issue ID in the summarised commit message title.'
}
The summarised commit message title needs to be less than 72 characters. Where there are multiple types (eg. fix, feat), this should be combined into the single most relevant type.
You should only output ONE commit message:\n${messages}`
}
]
});
const oneLineMessage = completionReponse.data.choices[0].message;
return oneLineMessage?.content;
} catch (error) {
outro(`${chalk.red('✖')} ${JSON.stringify(params)}`);

const err = error as Error;
outro(`${chalk.red('✖')} ${err?.message || err}`);

if (
axios.isAxiosError<{ error?: { message: string } }>(error) &&
error.response?.status === 401
) {
const openAiError = error.response.data.error;

if (openAiError?.message) outro(openAiError.message);
outro(
'For help look into README https://github.com/di-sukharev/opencommit#setup'
);
}

throw err;
}
};

public generateCommitMessage = async (
messages: Array<ChatCompletionRequestMessage>
): Promise<string | undefined> => {
Expand Down
6 changes: 3 additions & 3 deletions src/commands/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { getConfig } from './config';

const config = getConfig();

let issueID = '';

const getGitRemotes = async () => {
const { stdout } = await execa('git', ['remote']);
return stdout.split('\n').filter((remote) => Boolean(remote.trim()));
Expand Down Expand Up @@ -182,9 +184,7 @@ export async function commit(
process.exit(1);
}

let issueID = '';

if (config?.OCO_ISSUE_ENABLED) {
if (config?.OCO_ISSUE_ENABLED && !issueID) {
const issueIDSpinner = spinner();

issueIDSpinner.start('Confirming Issue ID');
Expand Down
13 changes: 10 additions & 3 deletions src/generateCommitMessageFromGitDiff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export const generateCommitMessageByDiff = async (
if (tokenCount(diff) >= MAX_REQUEST_TOKENS) {
const commitMessagePromises = await getCommitMsgsPromisesFromFileDiffs(
diff,
issueID,
MAX_REQUEST_TOKENS
MAX_REQUEST_TOKENS,
issueID
);

const commitMessages = [];
Expand All @@ -65,7 +65,14 @@ export const generateCommitMessageByDiff = async (
await delay(2000);
}

return commitMessages.join('\n\n');
const commitMessagesNewLines = commitMessages.join('\n');

const combinedCommitMessage = await api.generateSingleCommitMessage(commitMessagesNewLines);

if (!combinedCommitMessage)
throw new Error(GenerateCommitMessageErrorEnum.emptyMessage);

return combinedCommitMessage;
}

const messages = await generateCommitMessageChatCompletionPrompt(diff, issueID);
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"localLanguage": "english",
"commitFix": "fix: AB-1234: change port variable case from lowercase port to uppercase PORT to improve semantics",
"commitFeat": "feat: AB-1234: add support for process.env.PORT environment variable to be able to run app on a configurable port",
"commitFeat": "feat: AB-1234: add support for process.env.PORT environment variable to be able to run app on a configurable port and change port variable name to uppercase PORT",
"commitDescription": "The port variable is now named PORT, which improves consistency with the naming conventions as PORT is a constant. Support for an environment variable allows the application to be more flexible as it can now run on any available port specified via the process.env.PORT environment variable."
}
13 changes: 7 additions & 6 deletions src/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,25 @@ export const IDENTITY =

const INIT_MAIN_PROMPT = (language: string, issueID: string): ChatCompletionRequestMessage => ({
role: ChatCompletionRequestMessageRoleEnum.System,
content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages as per the conventional commit convention and explain WHAT were the changes and mainly WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you are to convert it into a commit message.
content: `${IDENTITY} Your mission is to create a clean and comprehensive commit message as per the conventional commit convention and explain WHAT were the changes and mainly WHY the changes were done.
I'll send you an output of 'git diff --staged' command, and you are to convert it into a commit message.
Only produce a single commit message for all files combined.
${
config?.OCO_EMOJI
? 'Use GitMoji convention to preface the commit.'
: 'Do not preface the commit with anything.'
}
${
config?.OCO_DESCRIPTION
? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.'
: "Don't add any descriptions to the commit, only commit message."
? 'Add a short description of WHY the changes are done after the single commit message title. Don\'t start it with "This commit", just describe the changes.'
: "Your response should just be one line with a commit message title and no description."
}
${
config?.OCO_ISSUE_ENABLED
? `You must also include the Issue ID: ${issueID} in the commit message title.`
: 'Don\'t include an Issue ID in the commit message title.'
}
Use the present tense. Lines must not be longer than 74 characters. Use ${language} for the commit message.`
Use the present tense. Lines must not be longer than 72 characters. Use ${language} for the commit message.`
});

export const INIT_DIFF_PROMPT: ChatCompletionRequestMessage = {
Expand Down Expand Up @@ -71,8 +73,7 @@ const INIT_CONSISTENCY_PROMPT = (
translation: ConsistencyPrompt
): ChatCompletionRequestMessage => ({
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: `${config?.OCO_EMOJI ? '🐛 ' : ''}${translation.commitFix}
${config?.OCO_EMOJI ? '✨ ' : ''}${translation.commitFeat}
content: `${config?.OCO_EMOJI ? '🐛 ' : ''}${translation.commitFeat}
${config?.OCO_DESCRIPTION ? translation.commitDescription : ''}`
});

Expand Down

0 comments on commit 9a1e4c3

Please sign in to comment.