|
1 | 1 | @using MaIN.Domain.Configuration |
| 2 | +@using MaIN.Domain.Configuration.Vertex |
2 | 3 | @using MaIN.Domain.Models.Abstract |
3 | 4 | @inject SettingsService SettingsStorage |
4 | 5 | @inject MaINSettings MaINSettings |
|
64 | 65 | </div> |
65 | 66 | } |
66 | 67 |
|
| 68 | + @if (_selectedBackend?.BackendType == BackendType.Vertex) |
| 69 | + { |
| 70 | + <div class="settings-field"> |
| 71 | + <label class="settings-label">Project ID</label> |
| 72 | + <FluentTextField @bind-Value="_vertexProjectId" |
| 73 | + Placeholder="my-google-cloud-project" |
| 74 | + Style="width: 100%;" /> |
| 75 | + </div> |
| 76 | + |
| 77 | + <div class="settings-field"> |
| 78 | + <label class="settings-label">Client Email</label> |
| 79 | + <FluentTextField @bind-Value="_vertexClientEmail" |
| 80 | + Placeholder="name@project.iam.gserviceaccount.com" |
| 81 | + Style="width: 100%;" /> |
| 82 | + </div> |
| 83 | + |
| 84 | + <div class="settings-field"> |
| 85 | + <label class="settings-label">Private Key</label> |
| 86 | + <div class="api-key-row"> |
| 87 | + <FluentTextField @bind-Value="_vertexPrivateKey" |
| 88 | + Placeholder="Private key from service account JSON" |
| 89 | + TextFieldType="@(_showVertexKey ? TextFieldType.Text : TextFieldType.Password)" |
| 90 | + Style="flex: 1;" /> |
| 91 | + <FluentButton Appearance="Appearance.Lightweight" |
| 92 | + Style="--neutral-fill-stealth-rest: transparent; --neutral-fill-stealth-hover: rgba(128,128,128,0.15); --neutral-fill-stealth-active: rgba(128,128,128,0.25);" |
| 93 | + OnClick="@(() => _showVertexKey = !_showVertexKey)" |
| 94 | + IconStart="@(_showVertexKey |
| 95 | + ? new Icons.Regular.Size20.EyeOff() |
| 96 | + : (Icon)new Icons.Regular.Size20.Eye())"> |
| 97 | + </FluentButton> |
| 98 | + </div> |
| 99 | + <span class="api-key-hint">private_key field from the service account JSON file</span> |
| 100 | + </div> |
| 101 | + |
| 102 | + <div class="settings-field"> |
| 103 | + <label class="settings-label">Location</label> |
| 104 | + <FluentTextField @bind-Value="_vertexLocation" |
| 105 | + Placeholder="us-central1" |
| 106 | + Style="width: 100%;" /> |
| 107 | + <span class="api-key-hint">Optional — defaults to us-central1</span> |
| 108 | + </div> |
| 109 | + } |
| 110 | + |
67 | 111 | @if (_selectedBackend?.BackendType == BackendType.Self) |
68 | 112 | { |
69 | 113 | <div class="settings-field"> |
|
155 | 199 | private string? _savedKeyPreview; |
156 | 200 | private bool _showApiKey; |
157 | 201 |
|
| 202 | + // Vertex AI auth fields |
| 203 | + private string? _vertexProjectId; |
| 204 | + private string? _vertexClientEmail; |
| 205 | + private string? _vertexPrivateKey; |
| 206 | + private string? _vertexLocation; |
| 207 | + private bool _showVertexKey; |
| 208 | + |
158 | 209 | // "Will load:" path preview shown below the model path field (Self backend only) |
159 | 210 | private string? ResolvedModelPathPreview |
160 | 211 | { |
|
236 | 287 | private string? _mmProjName; |
237 | 288 |
|
238 | 289 | private bool RequiresApiKey => _selectedBackend?.RequiresApiKey == true; |
| 290 | + private bool IsVertexBackend => _selectedBackend?.BackendType == BackendType.Vertex; |
| 291 | + private bool HasVertexRequiredFields => !string.IsNullOrWhiteSpace(_vertexProjectId) |
| 292 | + && !string.IsNullOrWhiteSpace(_vertexClientEmail) |
| 293 | + && !string.IsNullOrWhiteSpace(_vertexPrivateKey); |
239 | 294 | private bool CanSave => !string.IsNullOrWhiteSpace(_modelName) |
240 | 295 | && _selectedBackend != null |
241 | | - && (!RequiresApiKey || !string.IsNullOrEmpty(_apiKeyInput) || !string.IsNullOrEmpty(_savedKeyPreview)); |
| 296 | + && (!RequiresApiKey || !string.IsNullOrEmpty(_apiKeyInput) || !string.IsNullOrEmpty(_savedKeyPreview)) |
| 297 | + && (!IsVertexBackend || HasVertexRequiredFields); |
242 | 298 |
|
243 | 299 | protected override async Task OnInitializedAsync() |
244 | 300 | { |
|
251 | 307 | new(5, "Anthropic", BackendType.Anthropic, true), |
252 | 308 | new(6, "xAI", BackendType.Xai, true), |
253 | 309 | new(7, "Ollama (Local)", BackendType.Ollama, false), |
254 | | - new(8, "Ollama (Cloud)", BackendType.Ollama, true) |
| 310 | + new(8, "Ollama (Cloud)", BackendType.Ollama, true), |
| 311 | + new(9, "Vertex AI", BackendType.Vertex, false) |
255 | 312 | }.OrderBy(x => x.DisplayName) |
256 | 313 | .Prepend(new BackendOption(0, "Local", BackendType.Self, false)) |
257 | 314 | .ToList(); |
|
281 | 338 | ? _backendOptions.First(o => o.Id == 8) |
282 | 339 | : _backendOptions.First(o => o.Id == 7); |
283 | 340 | } |
| 341 | + else if (backendType == BackendType.Vertex) |
| 342 | + { |
| 343 | + _selectedBackend = _backendOptions.First(o => o.Id == 9); |
| 344 | + } |
284 | 345 | else |
285 | 346 | { |
286 | 347 | _selectedBackend = _backendOptions.FirstOrDefault(o => o.BackendType == backendType && o.Id < 7); |
|
293 | 354 | _manualImageGen = settings.HasImageGen; |
294 | 355 | _mmProjName = settings.MmProjName; |
295 | 356 |
|
| 357 | + if (backendType == BackendType.Vertex) |
| 358 | + { |
| 359 | + var vertexAuth = await SettingsStorage.GetVertexAuthAsync(); |
| 360 | + if (vertexAuth != null) |
| 361 | + { |
| 362 | + _vertexProjectId = vertexAuth.ProjectId; |
| 363 | + _vertexClientEmail = vertexAuth.ClientEmail; |
| 364 | + _vertexPrivateKey = vertexAuth.PrivateKey; |
| 365 | + } |
| 366 | + _vertexLocation = settings.VertexLocation; |
| 367 | + } |
| 368 | + |
296 | 369 | OnModelNameChanged(); |
297 | 370 | } |
298 | 371 | else if (!Utils.NeedsConfiguration) |
|
302 | 375 | ? (Utils.HasApiKey |
303 | 376 | ? _backendOptions.First(o => o.Id == 8) |
304 | 377 | : _backendOptions.First(o => o.Id == 7)) |
305 | | - : _backendOptions.FirstOrDefault(o => o.BackendType == Utils.BackendType && o.Id < 7); |
| 378 | + : Utils.BackendType == BackendType.Vertex |
| 379 | + ? _backendOptions.First(o => o.Id == 9) |
| 380 | + : _backendOptions.FirstOrDefault(o => o.BackendType == Utils.BackendType && o.Id < 7); |
306 | 381 |
|
307 | 382 | _modelName = Utils.Model; |
308 | 383 | _modelPath = Utils.Path; |
|
343 | 418 | } |
344 | 419 | } |
345 | 420 |
|
| 421 | + if (IsVertexBackend) |
| 422 | + { |
| 423 | + var vertexAuth = await SettingsStorage.GetVertexAuthAsync(); |
| 424 | + if (vertexAuth != null) |
| 425 | + { |
| 426 | + _vertexProjectId = vertexAuth.ProjectId; |
| 427 | + _vertexClientEmail = vertexAuth.ClientEmail; |
| 428 | + _vertexPrivateKey = vertexAuth.PrivateKey; |
| 429 | + } |
| 430 | + else |
| 431 | + { |
| 432 | + _vertexProjectId = null; |
| 433 | + _vertexClientEmail = null; |
| 434 | + _vertexPrivateKey = null; |
| 435 | + } |
| 436 | + |
| 437 | + var settings = await SettingsStorage.LoadSettingsAsync(); |
| 438 | + _vertexLocation = settings?.VertexLocation; |
| 439 | + } |
| 440 | + |
346 | 441 | await LoadApiKeyPreview(); |
347 | 442 | _apiKeyInput = null; |
348 | 443 | } |
|
425 | 520 | HasReasoning = hasReasoning, |
426 | 521 | HasImageGen = hasImageGen, |
427 | 522 | ModelPath = _modelPath, |
428 | | - MmProjName = _mmProjName |
| 523 | + MmProjName = _mmProjName, |
| 524 | + VertexLocation = IsVertexBackend ? _vertexLocation : null |
429 | 525 | }; |
430 | 526 | await SettingsStorage.SaveSettingsAsync(settings); |
431 | 527 |
|
|
447 | 543 | } |
448 | 544 | } |
449 | 545 |
|
| 546 | + // Vertex AI: persist auth and build config |
| 547 | + GoogleServiceAccountConfig? vertexAuth = null; |
| 548 | + if (IsVertexBackend && HasVertexRequiredFields) |
| 549 | + { |
| 550 | + await SettingsStorage.SaveVertexAuthAsync(_vertexProjectId!, _vertexClientEmail!, _vertexPrivateKey!); |
| 551 | + vertexAuth = new GoogleServiceAccountConfig |
| 552 | + { |
| 553 | + ProjectId = _vertexProjectId!, |
| 554 | + ClientEmail = _vertexClientEmail!, |
| 555 | + PrivateKey = _vertexPrivateKey! |
| 556 | + }; |
| 557 | + } |
| 558 | + |
450 | 559 | Utils.ApplySettings( |
451 | 560 | _selectedBackend.BackendType, |
452 | 561 | _modelName, |
|
456 | 565 | hasImageGen, |
457 | 566 | _mmProjName, |
458 | 567 | MaINSettings, |
459 | | - apiKey); |
| 568 | + apiKey, |
| 569 | + vertexAuth); |
460 | 570 |
|
461 | 571 | await OnSettingsApplied.InvokeAsync(); |
462 | 572 | } |
|
466 | 576 | if (_selectedBackend == null) return "Self"; |
467 | 577 | if (_selectedBackend.Id == 7) return "OllamaLocal"; |
468 | 578 | if (_selectedBackend.Id == 8) return "OllamaCloud"; |
| 579 | + if (_selectedBackend.Id == 9) return "Vertex"; |
469 | 580 | return _selectedBackend.BackendType.ToString(); |
470 | 581 | } |
471 | 582 |
|
|
0 commit comments