refactor: OpenAI integration

* supports configure multiple services
* supports select service when generate commit message by OpenAI

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2024-10-28 11:00:11 +08:00
parent 8280287362
commit 1044915be1
No known key found for this signature in database
13 changed files with 283 additions and 229 deletions

View file

@ -17,12 +17,14 @@ namespace SourceGit.Views
InitializeComponent();
}
public AIAssistant(string repo, List<Models.Change> changes, Action<string> onDone)
public AIAssistant(Models.OpenAIService service, string repo, List<Models.Change> changes, Action<string> onDone)
{
_service = service;
_repo = repo;
_changes = changes;
_onDone = onDone;
_cancel = new CancellationTokenSource();
InitializeComponent();
}
@ -35,7 +37,7 @@ namespace SourceGit.Views
Task.Run(() =>
{
var message = new Commands.GenerateCommitMessage(_repo, _changes, _cancel.Token, SetDescription).Result();
var message = new Commands.GenerateCommitMessage(_service, _repo, _changes, _cancel.Token, SetDescription).Result();
if (_cancel.IsCancellationRequested)
return;
@ -63,6 +65,7 @@ namespace SourceGit.Views
Dispatcher.UIThread.Invoke(() => ProgressMessage.Text = message);
}
private Models.OpenAIService _service;
private string _repo;
private List<Models.Change> _changes;
private Action<string> _onDone;

View file

@ -412,7 +412,7 @@
<TextBlock Classes="bold" Margin="4,0,0,0" Text="{DynamicResource Text.Preference.DiffMerge}"/>
</StackPanel>
<Rectangle Margin="0,8" Fill="{DynamicResource Brush.Border2}" Height=".6" HorizontalAlignment="Stretch"/>
<Grid Margin="8,0,0,0" RowDefinitions="32,Auto">
<Grid Margin="8,0,0,8" RowDefinitions="32,Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="IntegrationLabel"/>
<ColumnDefinition Width="*"/>
@ -459,82 +459,117 @@
</TextBox.InnerRightContent>
</TextBox>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0,24,0,0">
<Path Width="12" Height="12" Data="{StaticResource Icons.AIAssist}"/>
<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,32,Auto,Auto">
<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"
Margin="0,0,16,0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIServer, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.Model}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding OpenAIModel, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.ApiKey}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<TextBox Grid.Row="2" Grid.Column="1"
Height="28"
CornerRadius="3"
PasswordChar="*"
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
<ToggleButton Grid.Row="3" Grid.Column="1" Classes="group_expander" x:Name="OpenAIAdvancedOptions" HorizontalAlignment="Right">
<TextBlock Margin="0" Text="{DynamicResource Text.Preference.Advanced}"/>
</ToggleButton>
<TextBlock Grid.Row="4" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.AnalyzeDiffPrompt}"
HorizontalAlignment="Right"
Margin="0,0,16,0"
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
<TextBox Grid.Row="4" Grid.Column="1"
Height="120"
Margin="0,2"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding OpenAIAnalyzeDiffPrompt, Mode=TwoWay}"
AcceptsReturn="true"
TextWrapping="Wrap"
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
<TextBlock Grid.Row="5" Grid.Column="0"
Text="{DynamicResource Text.Preference.AI.GenerateSubjectPrompt}"
HorizontalAlignment="Right"
Margin="0,0,16,0"
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
<TextBox Grid.Row="5" Grid.Column="1"
Height="120"
Margin="0,2"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding OpenAIGenerateSubjectPrompt, Mode=TwoWay}"
AcceptsReturn="true"
TextWrapping="Wrap"
IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
</Grid>
</StackPanel>
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.AI}"/>
</TabItem.Header>
<Grid ColumnDefinitions="200,*" Margin="0,8,0,16" MinHeight="400">
<Border Grid.Column="0"
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
Background="{DynamicResource Brush.Contents}">
<Grid RowDefinitions="*,1,Auto">
<ListBox Grid.Row="0"
Background="Transparent"
ItemsSource="{Binding OpenAIServices}"
SelectedItem="{Binding #ThisControl.SelectedOpenAIService, Mode=TwoWay}"
SelectionMode="Single">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="MinHeight" Value="0"/>
<Setter Property="Height" Value="26"/>
<Setter Property="Padding" Value="4,2"/>
</Style>
</ListBox.Styles>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="m:OpenAIService">
<Grid ColumnDefinitions="Auto,*">
<Path Grid.Column="0" Width="14" Height="14" Data="{StaticResource Icons.AIAssist}"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="8,0" TextTrimming="CharacterEllipsis"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Rectangle Grid.Row="1" Height="1" Fill="{DynamicResource Brush.Border2}" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"/>
<StackPanel Grid.Row="2" Orientation="Horizontal" Background="{DynamicResource Brush.ToolBar}">
<Button Classes="icon_button" Click="OnAddOpenAIService">
<Path Width="14" Height="14" Data="{StaticResource Icons.Plus}"/>
</Button>
<Rectangle Width="1" Fill="{DynamicResource Brush.Border2}" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
<Button Classes="icon_button" Click="OnRemoveSelectedOpenAIService">
<Path Width="14" Height="14" Data="{StaticResource Icons.Window.Minimize}"/>
</Button>
<Rectangle Width="1" Fill="{DynamicResource Brush.Border2}" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
</StackPanel>
</Grid>
</Border>
<ContentControl Grid.Column="1" Margin="16,0,0,0">
<ContentControl.Content>
<Binding Path="#ThisControl.SelectedOpenAIService">
<Binding.TargetNullValue>
<Path Width="64" Height="64"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Fill="{DynamicResource Brush.FG2}"
Data="{StaticResource Icons.Empty}"/>
</Binding.TargetNullValue>
</Binding>
</ContentControl.Content>
<ContentControl.DataTemplates>
<DataTemplate DataType="m:OpenAIService">
<StackPanel Orientation="Vertical" MaxWidth="680">
<TextBlock Text="{DynamicResource Text.Preference.AI.Name}"/>
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Name, Mode=TwoWay}"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.Server}"/>
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Server, Mode=TwoWay}"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.Model}"/>
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding Model, Mode=TwoWay}"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.ApiKey}"/>
<TextBox Margin="0,4,0,0" CornerRadius="3" Height="28" Text="{Binding ApiKey, Mode=TwoWay}" PasswordChar="*"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.AnalyzeDiffPrompt}"/>
<TextBox Height="120"
Margin="0,4,0,0"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding AnalyzeDiffPrompt, Mode=TwoWay}"
AcceptsReturn="true"
TextWrapping="Wrap"/>
<TextBlock Margin="0,12,0,0" Text="{DynamicResource Text.Preference.AI.GenerateSubjectPrompt}"/>
<TextBox Height="120"
Margin="0,4,0,0"
CornerRadius="3"
VerticalContentAlignment="Top"
Text="{Binding GenerateSubjectPrompt, Mode=TwoWay}"
AcceptsReturn="true"
TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
</Grid>
</TabItem>
</TabControl>
</Border>
</Grid>

View file

@ -74,6 +74,15 @@ namespace SourceGit.Views
set;
}
public static readonly StyledProperty<Models.OpenAIService> SelectedOpenAIServiceProperty =
AvaloniaProperty.Register<Preference, Models.OpenAIService>(nameof(SelectedOpenAIService));
public Models.OpenAIService SelectedOpenAIService
{
get => GetValue(SelectedOpenAIServiceProperty);
set => SetValue(SelectedOpenAIServiceProperty, value);
}
public Preference()
{
var pref = ViewModels.Preference.Instance;
@ -312,5 +321,24 @@ namespace SourceGit.Views
e.Handled = true;
}
private void OnAddOpenAIService(object sender, RoutedEventArgs e)
{
var service = new Models.OpenAIService() { Name = "Unnamed Service" };
ViewModels.Preference.Instance.OpenAIServices.Add(service);
SelectedOpenAIService = service;
e.Handled = true;
}
private void OnRemoveSelectedOpenAIService(object sender, RoutedEventArgs e)
{
if (SelectedOpenAIService == null)
return;
ViewModels.Preference.Instance.OpenAIServices.Remove(SelectedOpenAIService);
SelectedOpenAIService = null;
e.Handled = true;
}
}
}

View file

@ -199,7 +199,7 @@
<Button Grid.Column="1"
Classes="icon_button"
Margin="4,2,0,0"
Command="{Binding GenerateCommitMessageByAI}"
Click="OnOpenOpenAIHelper"
ToolTip.Tip="{DynamicResource Text.AIAssistant.Tip}"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="0">

View file

@ -120,6 +120,17 @@ namespace SourceGit.Views
e.Handled = true;
}
private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm)
{
var menu = vm.CreateContextForOpenAI();
(sender as Button)?.OpenContextMenu(menu);
}
e.Handled = true;
}
private void OnOpenConventionalCommitHelper(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm)