diff --git a/src/AI/Agent.cs b/src/AI/Agent.cs index bd51babb..2979dadb 100644 --- a/src/AI/Agent.cs +++ b/src/AI/Agent.cs @@ -17,6 +17,9 @@ namespace SourceGit.AI public async Task GenerateCommitMessageAsync(string repo, string changeList, Action onUpdate, CancellationToken cancellation) { var chatClient = _service.GetChatClient(); + if (chatClient == null) + throw new Exception("Failed to fetch available models from this service. Please check your configuration and try again."); + var options = new ChatCompletionOptions() { Tools = { ChatTools.GetDetailChangesInFile } }; var userMessageBuilder = new StringBuilder(); diff --git a/src/AI/Service.cs b/src/AI/Service.cs index 91709cfe..0bd3bf84 100644 --- a/src/AI/Service.cs +++ b/src/AI/Service.cs @@ -1,22 +1,90 @@ using System; using System.ClientModel; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using System.Threading.Tasks; using Azure.AI.OpenAI; +using CommunityToolkit.Mvvm.ComponentModel; using OpenAI; using OpenAI.Chat; namespace SourceGit.AI { - public class Service + public class Service : ObservableObject { - public string Name { get; set; } = string.Empty; - public string Server { get; set; } = string.Empty; - public string Model { get; set; } = string.Empty; - public string ApiKey { get; set; } = string.Empty; - public bool ReadApiKeyFromEnv { get; set; } = false; - public string AdditionalPrompt { get; set; } = string.Empty; + public string Name + { + get => _name; + set => SetProperty(ref _name, value); + } + + public string Server + { + get; + set; + } = string.Empty; + + public string ApiKey + { + get; + set; + } = string.Empty; + + public bool ReadApiKeyFromEnv + { + get; + set; + } = false; + + public string AdditionalPrompt + { + get; + set; + } = string.Empty; + + [JsonIgnore] + public List AvailableModels + { + get; + private set; + } = []; + + public string Model + { + get; + set; + } = string.Empty; + + public async Task> FetchAvailableModelsAsync() + { + var credential = new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey); + var client = Server.Contains("openai.azure.com/", StringComparison.Ordinal) + ? new AzureOpenAIClient(new Uri(Server), credential) + : new OpenAIClient(credential, new() { Endpoint = new Uri(Server) }); + + var allModels = client.GetOpenAIModelClient().GetModels(); + AvailableModels = new List(); + foreach (var model in allModels.Value) + AvailableModels.Add(model.Id); + + if (AvailableModels.Count > 0) + { + if (string.IsNullOrEmpty(Model) || !AvailableModels.Contains(Model)) + Model = AvailableModels[0]; + } + else + { + Model = null; + } + + return AvailableModels; + } public ChatClient GetChatClient() { + if (string.IsNullOrEmpty(Model)) + return null; + var credential = new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey); var client = Server.Contains("openai.azure.com/", StringComparison.Ordinal) ? new AzureOpenAIClient(new Uri(Server), credential) @@ -24,5 +92,7 @@ namespace SourceGit.AI return client.GetChatClient(Model); } + + private string _name = string.Empty; } } diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index b36f1fc2..8887260c 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -602,7 +602,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Einstellungen AI API-Schlüssel - Modell + Modell Name Der eingegebene Wert ist der Name der Umgebungsvariable, aus der der API-Schlüssel gelesen wird Server diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index ed115881..8e204eea 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -18,6 +18,7 @@ Create New Branch Existing Branch AI Assistant + MODEL RE-GENERATE Use AI to generate commit message Use @@ -616,7 +617,6 @@ AI Additional Prompt (Use `-` to list your requirements) API Key - Model Name Entered value is the name to load API key from ENV Server diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 0a9cca55..df8971a9 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -616,7 +616,7 @@ OPEN AI Prompt adicional (Usa `-` para listar tus requerimientos) Clave API - Modelo + Modelo Nombre El valor ingresado es el nombre de la clave API a cargar desde ENV Servidor diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 0ec2396e..d4d4245a 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -561,7 +561,7 @@ Préférences IA Clé d'API - Modèle + Modèle Nom La valeur saisie est le nom pour charger la clé API depuis l'ENV Serveur diff --git a/src/Resources/Locales/id_ID.axaml b/src/Resources/Locales/id_ID.axaml index 11c2f739..120b547a 100644 --- a/src/Resources/Locales/id_ID.axaml +++ b/src/Resources/Locales/id_ID.axaml @@ -535,7 +535,7 @@ Preferensi AI API Key - Model + Model Nama Nilai yang dimasukkan adalah nama untuk memuat API key dari ENV Server diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index 869a426c..79f9d4d6 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -598,7 +598,7 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Preferenze AI Chiave API - Modello + Modello Nome Il valore inserito è il nome per caricare la chiave API da ENV Server diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 57385e10..487b2503 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -604,7 +604,7 @@ 設定 AI API キー - モデル + モデル 名前 この値を環境変数の名前とし、そこから API キーを読み込む サーバー diff --git a/src/Resources/Locales/ko_KR.axaml b/src/Resources/Locales/ko_KR.axaml index 9e1b439d..f377d694 100644 --- a/src/Resources/Locales/ko_KR.axaml +++ b/src/Resources/Locales/ko_KR.axaml @@ -537,7 +537,7 @@ 환경설정 AI API 키 - 모델 + 모델 이름 입력된 값은 환경변수(ENV)에서 API 키를 불러올 이름입니다 서버 diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 45937501..43aed16d 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -418,7 +418,7 @@ Preferências INTELIGÊNCIA ARTIFICIAL Chave da API - Modelo + Modelo Nome Servidor APARÊNCIA diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 6128e9b8..ae97a7ef 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -616,7 +616,7 @@ ОТКРЫТЬ ИИ Дополнительная подсказка (Для перечисления ваших требований используйте `-`) Ключ API - Модель + Модель Имя: Введённое значение — это имя для загрузки API-ключа из ENV Сервер diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index e87ab7c0..b5cce51b 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -415,7 +415,7 @@ விருப்பத்தேர்வுகள் செநு பநிஇ திறவுகோல் - மாதிரி + மாதிரி பெயர் சேவையகம் தோற்றம் diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index 23966d81..59d46553 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -419,7 +419,7 @@ Налаштування AI Ключ API - Модель + Модель Назва Сервер ВИГЛЯД diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index d52f3b59..2021d2a2 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -620,7 +620,7 @@ AI 附加提示词 (请使用 `-` 列出您的要求) API密钥 - 模型 + 模型 配置名称 从环境变量(填写环境变量名)中读取API密钥 服务地址 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 907dfeae..44bc1fb7 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -620,7 +620,7 @@ AI 附加提示詞 (請使用 '-' 列出您的要求) API 金鑰 - 模型 + 模型 名稱 從環境變數中 (輸入環境變數名稱) 讀取 API 金鑰 伺服器 diff --git a/src/ViewModels/AIAssistant.cs b/src/ViewModels/AIAssistant.cs index 032572d0..e138f09a 100644 --- a/src/ViewModels/AIAssistant.cs +++ b/src/ViewModels/AIAssistant.cs @@ -10,6 +10,17 @@ namespace SourceGit.ViewModels { public class AIAssistant : ObservableObject { + public List AvailableModels + { + get => _service.AvailableModels; + } + + public string CurrentModel + { + get => _service.Model; + set => _service.Model = value; + } + public bool IsGenerating { get => _isGenerating; diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index b71563db..265390d8 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading.Tasks; using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; @@ -616,6 +617,21 @@ namespace SourceGit.ViewModels RemoveInvalidRepositoriesRecursive(RepositoryNodes); } + public async Task UpdateAvailableAIModelsAsync() + { + foreach (var service in OpenAIServices) + { + try + { + await service.FetchAvailableModelsAsync(); + } + catch + { + // Ignore errors. + } + } + } + public void Save() { if (_isLoading || _isReadonly) diff --git a/src/Views/AIAssistant.axaml b/src/Views/AIAssistant.axaml index 2b1dcaad..d7111439 100644 --- a/src/Views/AIAssistant.axaml +++ b/src/Views/AIAssistant.axaml @@ -46,18 +46,33 @@ Content="{Binding Text}"/> - - - -