| 1 | using System.Collections.ObjectModel; |
| 2 | using Avalonia.Threading; |
| 3 | using CommunityToolkit.Mvvm.ComponentModel; |
| 4 | using CommunityToolkit.Mvvm.Input; |
| 5 | |
| 6 | namespace CascadeIDE.ViewModels; |
| 7 | |
| 8 | public partial class InstallModelDialogViewModel : ViewModelBase |
| 9 | { |
| 10 | private readonly Services.IOllamaService _ollama; |
| 11 | private readonly Action _closeRequested; |
| 12 | |
| 13 | public InstallModelDialogViewModel(Services.IOllamaService ollama, Action closeRequested) |
| 14 | { |
| 15 | _ollama = ollama; |
| 16 | _closeRequested = closeRequested; |
| 17 | var recommended = Services.MachineInfo.GetRecommendedModels(); |
| 18 | RecommendedModels.Clear(); |
| 19 | foreach (var r in recommended) |
| 20 | RecommendedModels.Add(r); |
| 21 | } |
| 22 | |
| 23 | public ObservableCollection<Models.RecommendedModel> RecommendedModels { get; } = []; |
| 24 | |
| 25 | [ObservableProperty] |
| 26 | [NotifyCanExecuteChangedFor(nameof(InstallCommand))] |
| 27 | private Models.RecommendedModel? _selectedRecommended; |
| 28 | |
| 29 | [ObservableProperty] |
| 30 | [NotifyCanExecuteChangedFor(nameof(InstallCommand))] |
| 31 | private string _customModelName = ""; |
| 32 | |
| 33 | [ObservableProperty] |
| 34 | [NotifyCanExecuteChangedFor(nameof(InstallCommand))] |
| 35 | private bool _isPulling; |
| 36 | |
| 37 | [ObservableProperty] |
| 38 | private string _progressText = ""; |
| 39 | |
| 40 | [ObservableProperty] |
| 41 | private string _errorText = ""; |
| 42 | |
| 43 | [RelayCommand(CanExecute = nameof(CanInstall))] |
| 44 | private async Task InstallAsync() |
| 45 | { |
| 46 | var model = SelectedRecommended?.ModelName ?? CustomModelName?.Trim() ?? ""; |
| 47 | if (string.IsNullOrEmpty(model)) |
| 48 | return; |
| 49 | |
| 50 | ErrorText = ""; |
| 51 | IsPulling = true; |
| 52 | ProgressText = $"Скачивание {model}…"; |
| 53 | |
| 54 | try |
| 55 | { |
| 56 | await foreach (var status in _ollama.PullModelAsync(model, CancellationToken.None)) |
| 57 | { |
| 58 | var s = status; |
| 59 | UiScheduler.Default.Post(() => ProgressText = s); |
| 60 | } |
| 61 | await UiScheduler.Default.InvokeAsync(() => |
| 62 | { |
| 63 | ProgressText = "Готово."; |
| 64 | _closeRequested(); |
| 65 | }); |
| 66 | } |
| 67 | catch (Exception ex) |
| 68 | { |
| 69 | await UiScheduler.Default.InvokeAsync(() => ErrorText = ex.Message); |
| 70 | } |
| 71 | finally |
| 72 | { |
| 73 | await UiScheduler.Default.InvokeAsync(() => IsPulling = false); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | private bool CanInstall() => !IsPulling && (!string.IsNullOrWhiteSpace(CustomModelName) || SelectedRecommended is not null); |
| 78 | |
| 79 | partial void OnCustomModelNameChanged(string value) |
| 80 | { |
| 81 | if (!string.IsNullOrWhiteSpace(value)) |
| 82 | SelectedRecommended = null; |
| 83 | } |
| 84 | |
| 85 | [RelayCommand] |
| 86 | private void Cancel() |
| 87 | { |
| 88 | _closeRequested(); |
| 89 | } |
| 90 | |
| 91 | [RelayCommand] |
| 92 | private void SelectRecommended(Models.RecommendedModel? model) |
| 93 | { |
| 94 | if (model is null) return; |
| 95 | SelectedRecommended = model; |
| 96 | CustomModelName = ""; |
| 97 | } |
| 98 | } |
| 99 | |