Skip to content

Commit 2f68aed

Browse files
authored
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.
1 parent 547c28a commit 2f68aed

File tree

6 files changed

+142
-39
lines changed

6 files changed

+142
-39
lines changed

src/Commands/GenerateCommitMessage.cs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,33 @@ namespace SourceGit.Commands
1010
/// </summary>
1111
public class GenerateCommitMessage
1212
{
13+
private const string DEFAULT_SUMMARY_PROMPT = """
14+
You are an expert developer specialist in creating commits.
15+
Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:
16+
- Do not use any code snippets, imports, file routes or bullets points.
17+
- Do not mention the route of file that has been change.
18+
- Simply describe the MAIN GOAL of the changes.
19+
- Output directly the summary in plain text.
20+
""";
21+
22+
private const string DEFAULT_SUBJECT_PROMPT = """
23+
You are an expert developer specialist in creating commits messages.
24+
Your only goal is to retrieve a single commit message.
25+
Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:
26+
- Assign the commit {type} according to the next conditions:
27+
feat: Only when adding a new feature.
28+
fix: When fixing a bug.
29+
docs: When updating documentation.
30+
style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic.
31+
test: When adding or updating tests.
32+
chore: When making changes to the build process or auxiliary tools and libraries.
33+
revert: When undoing a previous commit.
34+
refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.
35+
- Do not add any issues numeration, explain your output nor introduce your answer.
36+
- Output directly only one commit message in plain text with the next format: {type}: {commit_message}.
37+
- Be as concise as possible, keep the message under 50 characters.
38+
""";
39+
1340
public class GetDiffContent : Command
1441
{
1542
public GetDiffContent(string repo, Models.DiffOption opt)
@@ -70,15 +97,12 @@ private string GenerateChangeSummary(Models.Change change)
7097
var rs = new GetDiffContent(_repo, new Models.DiffOption(change, false)).ReadToEnd();
7198
var diff = rs.IsSuccess ? rs.StdOut : "unknown change";
7299

73-
var prompt = new StringBuilder();
74-
prompt.AppendLine("You are an expert developer specialist in creating commits.");
75-
prompt.AppendLine("Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:");
76-
prompt.AppendLine("- Do not use any code snippets, imports, file routes or bullets points.");
77-
prompt.AppendLine("- Do not mention the route of file that has been change.");
78-
prompt.AppendLine("- Simply describe the MAIN GOAL of the changes.");
79-
prompt.AppendLine("- Output directly the summary in plain text.`");
100+
var prompt = string.IsNullOrWhiteSpace(Models.OpenAI.SummaryPrompt)
101+
? DEFAULT_SUMMARY_PROMPT
102+
: Models.OpenAI.SummaryPrompt;
103+
104+
var rsp = Models.OpenAI.Chat(prompt, $"Here is the `git diff` output: {diff}", _cancelToken);
80105

81-
var rsp = Models.OpenAI.Chat(prompt.ToString(), $"Here is the `git diff` output: {diff}", _cancelToken);
82106
if (rsp != null && rsp.Choices.Count > 0)
83107
return rsp.Choices[0].Message.Content;
84108

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

88112
private string GenerateSubject(string summary)
89113
{
90-
var prompt = new StringBuilder();
91-
prompt.AppendLine("You are an expert developer specialist in creating commits messages.");
92-
prompt.AppendLine("Your only goal is to retrieve a single commit message.");
93-
prompt.AppendLine("Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:");
94-
prompt.AppendLine("- Assign the commit {type} according to the next conditions:");
95-
prompt.AppendLine(" feat: Only when adding a new feature.");
96-
prompt.AppendLine(" fix: When fixing a bug.");
97-
prompt.AppendLine(" docs: When updating documentation.");
98-
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.");
99-
prompt.AppendLine(" test: When adding or updating tests. ");
100-
prompt.AppendLine(" chore: When making changes to the build process or auxiliary tools and libraries. ");
101-
prompt.AppendLine(" revert: When undoing a previous commit.");
102-
prompt.AppendLine(" refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.");
103-
prompt.AppendLine("- Do not add any issues numeration, explain your output nor introduce your answer.");
104-
prompt.AppendLine("- Output directly only one commit message in plain text with the next format: {type}: {commit_message}.");
105-
prompt.AppendLine("- Be as concise as possible, keep the message under 50 characters.");
106-
107-
var rsp = Models.OpenAI.Chat(prompt.ToString(), $"Here are the summaries changes: {summary}", _cancelToken);
114+
var prompt = string.IsNullOrWhiteSpace(Models.OpenAI.SubjectPrompt)
115+
? DEFAULT_SUBJECT_PROMPT
116+
: Models.OpenAI.SubjectPrompt;
117+
118+
var rsp = Models.OpenAI.Chat(prompt, $"Here are the summaries changes: {summary}", _cancelToken);
119+
108120
if (rsp != null && rsp.Choices.Count > 0)
109121
return rsp.Choices[0].Message.Content;
110122

src/Models/OpenAI.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ public static string Model
9494
set;
9595
}
9696

97+
public static string SubjectPrompt
98+
{
99+
get;
100+
set;
101+
}
102+
103+
public static string SummaryPrompt
104+
{
105+
get;
106+
set;
107+
}
108+
97109
public static bool IsValid
98110
{
99111
get => !string.IsNullOrEmpty(Server) && !string.IsNullOrEmpty(Model);
@@ -113,14 +125,14 @@ public static OpenAIChatResponse Chat(string prompt, string question, Cancellati
113125
try
114126
{
115127
var task = client.PostAsync(Server, req, cancellation);
116-
task.Wait();
128+
task.Wait(cancellation);
117129

118130
var rsp = task.Result;
119131
if (!rsp.IsSuccessStatusCode)
120132
throw new Exception($"AI service returns error code {rsp.StatusCode}");
121133

122134
var reader = rsp.Content.ReadAsStringAsync(cancellation);
123-
reader.Wait();
135+
reader.Wait(cancellation);
124136

125137
return JsonSerializer.Deserialize(reader.Result, JsonCodeGen.Default.OpenAIChatResponse);
126138
}

src/Resources/Locales/en_US.axaml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,31 @@
403403
<x:String x:Key="Text.Preference.AI.Server" xml:space="preserve">Server</x:String>
404404
<x:String x:Key="Text.Preference.AI.ApiKey" xml:space="preserve">API Key</x:String>
405405
<x:String x:Key="Text.Preference.AI.Model" xml:space="preserve">Model</x:String>
406+
<x:String x:Key="Text.Preference.AI.SummaryPrompt" xml:space="preserve">Summary Prompt</x:String>
407+
<x:String x:Key="Text.Preference.AI.SubjectPrompt" xml:space="preserve">Subject Prompt</x:String>
408+
<x:String x:Key="Text.Preference.AI.SummaryPromptHint" xml:space="preserve">You are an expert developer specialist in creating commits.
409+
Provide a super concise one sentence overall changes summary of the user `git diff` output following strictly the next rules:
410+
- Do not use any code snippets, imports, file routes or bullets points.
411+
- Do not mention the route of file that has been change.
412+
- Simply describe the MAIN GOAL of the changes.
413+
- Output directly the summary in plain text.
414+
</x:String>
415+
<x:String x:Key="Text.Preference.AI.SubjectPromptHint" xml:space="preserve">You are an expert developer specialist in creating commits messages.
416+
Your only goal is to retrieve a single commit message.
417+
Based on the provided user changes, combine them in ONE SINGLE commit message retrieving the global idea, following strictly the next rules:
418+
- Assign the commit {type} according to the next conditions:
419+
feat: Only when adding a new feature.
420+
fix: When fixing a bug.
421+
docs: When updating documentation.
422+
style: When changing elements styles or design and/or making changes to the code style (formatting, missing semicolons, etc.) without changing the code logic.
423+
test: When adding or updating tests.
424+
chore: When making changes to the build process or auxiliary tools and libraries.
425+
revert: When undoing a previous commit.
426+
refactor: When restructuring code without changing its external behavior, or is any of the other refactor types.
427+
- Do not add any issues numeration, explain your output nor introduce your answer.
428+
- Output directly only one commit message in plain text with the next format: {type}: {commit_message}.
429+
- Be as concise as possible, keep the message under 50 characters.
430+
</x:String>
406431
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">APPEARANCE</x:String>
407432
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
408433
<x:String x:Key="Text.Preference.Appearance.DefaultFontSize" xml:space="preserve">Default Font Size</x:String>
@@ -621,7 +646,7 @@
621646
<x:String x:Key="Text.Welcome.OpenAllInNode" xml:space="preserve">Open All Repositories</x:String>
622647
<x:String x:Key="Text.Welcome.OpenOrInit" xml:space="preserve">Open Repository</x:String>
623648
<x:String x:Key="Text.Welcome.OpenTerminal" xml:space="preserve">Open Terminal</x:String>
624-
<x:String x:Key="Text.Welcome.ScanDefaultCloneDir" xml:space="preserve">Rescan Repositories in Default Clone Dir</x:String>
649+
<x:String x:Key="Text.Welcome.ScanDefaultCloneDir" xml:space="preserve">Rescan Repositories in Default Clone Dir</x:String>
625650
<x:String x:Key="Text.Welcome.Search" xml:space="preserve">Search Repositories...</x:String>
626651
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sort</x:String>
627652
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">Changes</x:String>

src/SourceGit.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<PackageReference Include="Avalonia.Diagnostics" Version="11.1.4" Condition="'$(Configuration)' == 'Debug'" />
4646
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.1.0" />
4747
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
48-
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
48+
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
4949
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc3.3" />
5050
<PackageReference Include="TextMateSharp" Version="1.0.63" />
5151
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.63" />

src/ViewModels/Preference.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,32 @@ public string OpenAIModel
315315
}
316316
}
317317

318+
public string OpenAISubjectPrompt
319+
{
320+
get => Models.OpenAI.SubjectPrompt;
321+
set
322+
{
323+
if (value != Models.OpenAI.SubjectPrompt)
324+
{
325+
Models.OpenAI.SubjectPrompt = value;
326+
OnPropertyChanged();
327+
}
328+
}
329+
}
330+
331+
public string OpenAISummaryPrompt
332+
{
333+
get => Models.OpenAI.SummaryPrompt;
334+
set
335+
{
336+
if (value != Models.OpenAI.SummaryPrompt)
337+
{
338+
Models.OpenAI.SummaryPrompt = value;
339+
OnPropertyChanged();
340+
}
341+
}
342+
}
343+
318344
public uint StatisticsSampleColor
319345
{
320346
get => _statisticsSampleColor;

src/Views/Preference.axaml

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
<TabItem.Header>
5353
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.General}"/>
5454
</TabItem.Header>
55-
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="Auto,*">
55+
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32" ColumnDefinitions="Auto,*">
5656
<TextBlock Grid.Row="0" Grid.Column="0"
5757
Text="{DynamicResource Text.Preference.General.Locale}"
5858
HorizontalAlignment="Right"
@@ -236,7 +236,7 @@
236236
Margin="0,0,16,0"/>
237237
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
238238
<TextBlock Margin="0,0,8,0"
239-
Text="{Binding #ThisControl.GitVersion}"
239+
Text="{Binding #ThisControl.GitVersion}"
240240
IsVisible="{Binding #ThisControl.GitVersion, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
241241

242242
<Border Background="Transparent"
@@ -368,7 +368,7 @@
368368
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
369369
<ColumnDefinition Width="*"/>
370370
</Grid.ColumnDefinitions>
371-
371+
372372
<TextBlock Grid.Row="0" Grid.Column="0"
373373
Text="{DynamicResource Text.Preference.Shell.Type}"
374374
HorizontalAlignment="Right"
@@ -405,7 +405,7 @@
405405
</Button>
406406
</TextBox.InnerRightContent>
407407
</TextBox>
408-
</Grid>
408+
</Grid>
409409

410410
<StackPanel Orientation="Horizontal" Margin="0,24,0,0">
411411
<Path Width="12" Height="12" Data="{StaticResource Icons.Diff}"/>
@@ -417,7 +417,7 @@
417417
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
418418
<ColumnDefinition Width="*"/>
419419
</Grid.ColumnDefinitions>
420-
420+
421421
<TextBlock Grid.Row="0" Grid.Column="0"
422422
Text="{DynamicResource Text.Preference.DiffMerge.Type}"
423423
HorizontalAlignment="Right"
@@ -465,12 +465,12 @@
465465
<TextBlock Classes="bold" Margin="4,0,0,0" Text="{DynamicResource Text.Preference.AI}"/>
466466
</StackPanel>
467467
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
468-
<Grid Margin="8,0,0,0" RowDefinitions="32,32,32">
468+
<Grid Margin="8,0,0,0" RowDefinitions="32,32,32,128,128">
469469
<Grid.ColumnDefinitions>
470470
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
471471
<ColumnDefinition Width="*"/>
472472
</Grid.ColumnDefinitions>
473-
473+
474474
<TextBlock Grid.Row="0" Grid.Column="0"
475475
Text="{DynamicResource Text.Preference.AI.Server}"
476476
HorizontalAlignment="Right"
@@ -497,10 +497,38 @@
497497
Height="28"
498498
CornerRadius="3"
499499
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
500+
501+
<TextBlock Grid.Row="3" Grid.Column="0"
502+
Text="{DynamicResource Text.Preference.AI.SubjectPrompt}"
503+
HorizontalAlignment="Right"
504+
Margin="0,0,16,0"/>
505+
506+
<TextBox Grid.Row="3" Grid.Column="1"
507+
Height="120"
508+
CornerRadius="3"
509+
VerticalContentAlignment="Top"
510+
Text="{Binding OpenAISubjectPrompt, Mode=TwoWay}"
511+
AcceptsReturn="true"
512+
Watermark="{DynamicResource Text.Preference.AI.SubjectPromptHint}"
513+
TextWrapping="Wrap"/>
514+
515+
<TextBlock Grid.Row="4" Grid.Column="0"
516+
Text="{DynamicResource Text.Preference.AI.SummaryPrompt}"
517+
HorizontalAlignment="Right"
518+
Margin="0,0,16,0"/>
519+
520+
<TextBox Grid.Row="4" Grid.Column="1"
521+
Height="120"
522+
CornerRadius="3"
523+
VerticalContentAlignment="Top"
524+
Text="{Binding OpenAISummaryPrompt, Mode=TwoWay}"
525+
AcceptsReturn="true"
526+
Watermark="{DynamicResource Text.Preference.AI.SummaryPromptHint}"
527+
TextWrapping="Wrap"/>
500528
</Grid>
501-
</StackPanel>
529+
</StackPanel>
502530
</TabItem>
503531
</TabControl>
504-
</Border>
532+
</Border>
505533
</Grid>
506534
</v:ChromelessWindow>

0 commit comments

Comments
 (0)