From 2f68aed8173b5057f37d9c94d6552db950d49d3d Mon Sep 17 00:00:00 2001 From: Douglas Cunha Date: Wed, 23 Oct 2024 22:31:05 -0300 Subject: [PATCH] feat: improve commit message generation with AI prompts (#596) - Refactor the commit message generation process to utilize default prompts and enhance clarity while eliminating redundancy. - Added new properties for subject and summary prompts, while improving cancellation support in async task handling. - feat: add AI prompts for commit message generation. - Updated the formatting of the package reference for consistency in the project file. - Add properties for managing OpenAI subject and summary prompts in the Preference view model. - Refactor layout and add new input fields for AI subject and summary prompts in the preferences view. --- src/Commands/GenerateCommitMessage.cs | 62 ++++++++++++++++----------- src/Models/OpenAI.cs | 16 ++++++- src/Resources/Locales/en_US.axaml | 27 +++++++++++- src/SourceGit.csproj | 2 +- src/ViewModels/Preference.cs | 26 +++++++++++ src/Views/Preference.axaml | 46 ++++++++++++++++---- 6 files changed, 141 insertions(+), 38 deletions(-) diff --git a/src/Commands/GenerateCommitMessage.cs b/src/Commands/GenerateCommitMessage.cs index e71fb0b9..9cfb8a1c 100644 --- a/src/Commands/GenerateCommitMessage.cs +++ b/src/Commands/GenerateCommitMessage.cs @@ -10,6 +10,33 @@ namespace SourceGit.Commands /// public class GenerateCommitMessage { + private const string DEFAULT_SUMMARY_PROMPT = """ + You are an expert developer specialist in creating commits. + Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules: + - Do not use any code snippets, imports, file routes or bullets points. + - Do not mention the route of file that has been change. + - Simply describe the MAIN GOAL of the changes. + - Output directly the summary in plain text. + """; + + private const string DEFAULT_SUBJECT_PROMPT = """ + You are an expert developer specialist in creating commits messages. + Your only goal is to retrieve a single commit message. + Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules: + - Assign the commit {type} according to the next conditions: + feat: Only when adding a new feature. + fix: When fixing a bug. + docs: When updating documentation. + style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic. + test: When adding or updating tests. + chore: When making changes to the build process or auxiliary tools and libraries. + revert: When undoing a previous commit. + refactor: When restructuring code without changing its external behavior, or is any of the other refactor types. + - Do not add any issues numeration, explain your output nor introduce your answer. + - Output directly only one commit message in plain text with the next format: {type}: {commit_message}. + - Be as concise as possible, keep the message under 50 characters. + """; + public class GetDiffContent : Command { public GetDiffContent(string repo, Models.DiffOption opt) @@ -70,15 +97,12 @@ namespace SourceGit.Commands var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd(); var diff = rs.IsSuccess ? rs.StdOut : "unknown change"; - var prompt = new StringBuilder(); - prompt.AppendLine("You are an expert developer specialist in creating commits."); - prompt.AppendLine("Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:"); - prompt.AppendLine("- Do not use any code snippets, imports, file routes or bullets points."); - prompt.AppendLine("- Do not mention the route of file that has been change."); - prompt.AppendLine("- Simply describe the MAIN GOAL of the changes."); - prompt.AppendLine("- Output directly the summary in plain text.`"); + var prompt = string.IsNullOrWhiteSpace(Models.OpenAI.SummaryPrompt) + ? DEFAULT_SUMMARY_PROMPT + : Models.OpenAI.SummaryPrompt; + + var rsp = Models.OpenAI.Chat(prompt, $"Here is the `git diff` output: {diff}", _cancelToken); - var rsp = Models.OpenAI.Chat(prompt.ToString(), $"Here is the `git diff` output: {diff}", _cancelToken); if (rsp != null && rsp.Choices.Count > 0) return rsp.Choices[0].Message.Content; @@ -87,24 +111,12 @@ namespace SourceGit.Commands private string GenerateSubject(string summary) { - var prompt = new StringBuilder(); - prompt.AppendLine("You are an expert developer specialist in creating commits messages."); - prompt.AppendLine("Your only goal is to retrieve a single commit message."); - prompt.AppendLine("Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:"); - prompt.AppendLine("- Assign the commit {type} according to the next conditions:"); - prompt.AppendLine(" feat: Only when adding a new feature."); - prompt.AppendLine(" fix: When fixing a bug."); - prompt.AppendLine(" docs: When updating documentation."); - prompt.AppendLine(" style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic."); - prompt.AppendLine(" test: When adding or updating tests. "); - prompt.AppendLine(" chore: When making changes to the build process or auxiliary tools and libraries. "); - prompt.AppendLine(" revert: When undoing a previous commit."); - prompt.AppendLine(" refactor: When restructuring code without changing its external behavior, or is any of the other refactor types."); - prompt.AppendLine("- Do not add any issues numeration, explain your output nor introduce your answer."); - prompt.AppendLine("- Output directly only one commit message in plain text with the next format: {type}: {commit_message}."); - prompt.AppendLine("- Be as concise as possible, keep the message under 50 characters."); + var prompt = string.IsNullOrWhiteSpace(Models.OpenAI.SubjectPrompt) + ? DEFAULT_SUBJECT_PROMPT + : Models.OpenAI.SubjectPrompt; + + var rsp = Models.OpenAI.Chat(prompt, $"Here are the summaries changes: {summary}", _cancelToken); - var rsp = Models.OpenAI.Chat(prompt.ToString(), $"Here are the summaries changes: {summary}", _cancelToken); if (rsp != null && rsp.Choices.Count > 0) return rsp.Choices[0].Message.Content; diff --git a/src/Models/OpenAI.cs b/src/Models/OpenAI.cs index 5ab0c7ee..16feabd7 100644 --- a/src/Models/OpenAI.cs +++ b/src/Models/OpenAI.cs @@ -94,6 +94,18 @@ namespace SourceGit.Models set; } + public static string SubjectPrompt + { + get; + set; + } + + public static string SummaryPrompt + { + get; + set; + } + public static bool IsValid { get => !string.IsNullOrEmpty(Server) && !string.IsNullOrEmpty(Model); @@ -113,14 +125,14 @@ namespace SourceGit.Models try { var task = client.PostAsync(Server, req, cancellation); - task.Wait(); + task.Wait(cancellation); var rsp = task.Result; if (!rsp.IsSuccessStatusCode) throw new Exception($"AI service returns error code {rsp.StatusCode}"); var reader = rsp.Content.ReadAsStringAsync(cancellation); - reader.Wait(); + reader.Wait(cancellation); return JsonSerializer.Deserialize(reader.Result, JsonCodeGen.Default.OpenAIChatResponse); } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 919c071b..f5a2f989 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -403,6 +403,31 @@ Server API Key Model + Summary Prompt + Subject Prompt + You are an expert developer specialist in creating commits. +Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules: +- Do not use any code snippets, imports, file routes or bullets points. +- Do not mention the route of file that has been change. +- Simply describe the MAIN GOAL of the changes. +- Output directly the summary in plain text. + + You are an expert developer specialist in creating commits messages. +Your only goal is to retrieve a single commit message. +Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules: +- Assign the commit {type} according to the next conditions: + feat: Only when adding a new feature. + fix: When fixing a bug. + docs: When updating documentation. + style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic. + test: When adding or updating tests. + chore: When making changes to the build process or auxiliary tools and libraries. + revert: When undoing a previous commit. + refactor: When restructuring code without changing its external behavior, or is any of the other refactor types. +- Do not add any issues numeration, explain your output nor introduce your answer. +- Output directly only one commit message in plain text with the next format: {type}: {commit_message}. +- Be as concise as possible, keep the message under 50 characters. + APPEARANCE Default Font Default Font Size @@ -621,7 +646,7 @@ Open All Repositories Open Repository Open Terminal - Rescan Repositories in Default Clone Dir + Rescan Repositories in Default Clone Dir Search Repositories... Sort Changes diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 12e08130..fdfb75d5 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -45,7 +45,7 @@ - + diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 045a6461..c315a912 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -315,6 +315,32 @@ namespace SourceGit.ViewModels } } + public string OpenAISubjectPrompt + { + get => Models.OpenAI.SubjectPrompt; + set + { + if (value != Models.OpenAI.SubjectPrompt) + { + Models.OpenAI.SubjectPrompt = value; + OnPropertyChanged(); + } + } + } + + public string OpenAISummaryPrompt + { + get => Models.OpenAI.SummaryPrompt; + set + { + if (value != Models.OpenAI.SummaryPrompt) + { + Models.OpenAI.SummaryPrompt = value; + OnPropertyChanged(); + } + } + } + public uint StatisticsSampleColor { get => _statisticsSampleColor; diff --git a/src/Views/Preference.axaml b/src/Views/Preference.axaml index 0b01202b..82436e1f 100644 --- a/src/Views/Preference.axaml +++ b/src/Views/Preference.axaml @@ -52,7 +52,7 @@ - + - + - + @@ -417,7 +417,7 @@ - + - + - + + + + + + + + + - + - +