Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve commit message generation with AI prompts #596

Merged
merged 1 commit into from
Oct 24, 2024
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
64 changes: 38 additions & 26 deletions src/Commands/GenerateCommitMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ namespace SourceGit.Commands
/// </summary>
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)
Expand Down Expand Up @@ -70,15 +97,12 @@ private string GenerateChangeSummary(Models.Change change)
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;

Expand All @@ -87,24 +111,12 @@ private string GenerateChangeSummary(Models.Change change)

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 rsp = Models.OpenAI.Chat(prompt.ToString(), $"Here are the summaries changes: {summary}", _cancelToken);
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);

if (rsp != null && rsp.Choices.Count > 0)
return rsp.Choices[0].Message.Content;

Expand Down
16 changes: 14 additions & 2 deletions src/Models/OpenAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ public static string Model
set;
}

public static string SubjectPrompt
{
get;
set;
}

public static string SummaryPrompt
{
get;
set;
}

public static bool IsValid
{
get => !string.IsNullOrEmpty(Server) && !string.IsNullOrEmpty(Model);
Expand All @@ -113,14 +125,14 @@ public static OpenAIChatResponse Chat(string prompt, string question, Cancellati
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);
}
Expand Down
27 changes: 26 additions & 1 deletion src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,31 @@
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">Server</x:String>
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API Key</x:String>
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">Model</x:String>
<x:String x:Key="Text.Preference.AI.SummaryPrompt" xml:space="preserve">Summary Prompt</x:String>
<x:String x:Key="Text.Preference.AI.SubjectPrompt" xml:space="preserve">Subject Prompt</x:String>
<x:String x:Key="Text.Preference.AI.SummaryPromptHint" xml:space="preserve">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.
</x:String>
<x:String x:Key="Text.Preference.AI.SubjectPromptHint" xml:space="preserve">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.
</x:String>
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">APPEARANCE</x:String>
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">Default Font Size</x:String>
Expand Down Expand Up @@ -621,7 +646,7 @@
<x:String x:Key="Text.Welcome.OpenAllInNode" xml:space="preserve">Open All Repositories</x:String>
<x:String x:Key="Text.Welcome.OpenOrInit" xml:space="preserve">Open Repository</x:String>
<x:String x:Key="Text.Welcome.OpenTerminal" xml:space="preserve">Open Terminal</x:String>
<x:String x:Key="Text.Welcome.ScanDefaultCloneDir" xml:space="preserve">Rescan Repositories in Default Clone Dir</x:String>
<x:String x:Key="Text.Welcome.ScanDefaultCloneDir" xml:space="preserve">Rescan Repositories in Default Clone Dir</x:String>
<x:String x:Key="Text.Welcome.Search" xml:space="preserve">Search Repositories...</x:String>
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sort</x:String>
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">Changes</x:String>
Expand Down
2 changes: 1 addition & 1 deletion src/SourceGit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<PackageReference Include="Avalonia.Diagnostics" Version="11.1.4" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.1.0" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc3.3" />
<PackageReference Include="TextMateSharp" Version="1.0.63" />
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.63" />
Expand Down
26 changes: 26 additions & 0 deletions src/ViewModels/Preference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,32 @@ public string OpenAIModel
}
}

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;
Expand Down
46 changes: 37 additions & 9 deletions src/Views/Preference.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.General}"/>
</TabItem.Header>
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="Auto,*">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.General.Locale}"
HorizontalAlignment="Right"
Expand Down Expand Up @@ -236,7 +236,7 @@
Margin="0,0,16,0"/>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
<TextBlock Margin="0,0,8,0"
Text="{Binding #ThisControl.GitVersion}"
Text="{Binding #ThisControl.GitVersion}"
IsVisible="{Binding #ThisControl.GitVersion, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>

<Border Background="Transparent"
Expand Down Expand Up @@ -368,7 +368,7 @@
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.Shell.Type}"
HorizontalAlignment="Right"
Expand Down Expand Up @@ -405,7 +405,7 @@
</Button>
</TextBox.InnerRightContent>
</TextBox>
</Grid>
</Grid>

<StackPanel Orientation="Horizontal" Margin="0,24,0,0">
<Path Width="12" Height="12" Data="{StaticResource Icons.Diff}"/>
Expand All @@ -417,7 +417,7 @@
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.DiffMerge.Type}"
HorizontalAlignment="Right"
Expand Down Expand Up @@ -465,12 +465,12 @@
<TextBlock Classes="bold" Margin="4,0,0,0" Text="{DynamicResource Text.Preference.AI}"/>
</StackPanel>
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
<Grid Margin="8,0,0,0" RowDefinitions="32,32,32">
<Grid Margin="8,0,0,0" RowDefinitions="32,32,32,128,128">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<TextBlock Grid.Row="0" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Server}"
HorizontalAlignment="Right"
Expand All @@ -497,10 +497,38 @@
Height="28"
CornerRadius="3"
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>

<TextBlock Grid.Row="3" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.SubjectPrompt}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>

<TextBox Grid.Row="3" Grid.Column="1"
Height="120"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding OpenAISubjectPrompt, Mode=TwoWay}"
AcceptsReturn="true"
Watermark="{DynamicResource Text.Preference.AI.SubjectPromptHint}"
TextWrapping="Wrap"/>

<TextBlock Grid.Row="4" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.SummaryPrompt}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>

<TextBox Grid.Row="4" Grid.Column="1"
Height="120"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding OpenAISummaryPrompt, Mode=TwoWay}"
AcceptsReturn="true"
Watermark="{DynamicResource Text.Preference.AI.SummaryPromptHint}"
TextWrapping="Wrap"/>
</Grid>
</StackPanel>
</StackPanel>
</TabItem>
</TabControl>
</Border>
</Border>
</Grid>
</v:ChromelessWindow>