using AIStudio.Dialogs; using AIStudio.Settings; using AIStudio.Tools.PluginSystem; using AIStudio.Tools.Services; using DialogOptions = AIStudio.Dialogs.DialogOptions; namespace AIStudio.Tools; /// /// Adds an embedding provider, either from scratch or from an imported configuration, and tells /// whether the user may do so. /// /// /// Kept here rather than in the places which offer it -- the embedding provider panel and the data /// source settings, which offer it while no embedding provider exists -- so that adding one works the /// same way everywhere: the organization's permission is checked, the provider is stored, and the /// data sources are checked against it. /// public static class EmbeddingProviderSetup { private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(EmbeddingProviderSetup).Namespace, nameof(EmbeddingProviderSetup)); /// /// Whether the organization lets the user add an embedding provider. /// public static bool MayAdd(SettingsManager settingsManager) => settingsManager.ConfigurationData.App.AllowUserToAddProvider && settingsManager.ConfigurationData.App.AllowUserToAddEmbeddingProvider; /// /// Whether the organization lets the user import an embedding provider. /// /// /// An import adds a provider, so this holds only when MayAdd holds as well. /// public static bool MayImport(SettingsManager settingsManager) => settingsManager.ConfigurationData.App.CanImportConfigurationSnippet(PluginConfigurationObjectType.EMBEDDING_PROVIDER); /// /// Whether no embedding provider exists, so that no local data source can be prepared for the AI. /// public static bool IsMissing(SettingsManager settingsManager) => settingsManager.ConfigurationData.EmbeddingProviders.Count == 0; /// /// The parameters which open the embedding provider dialog with the given provider filled in. /// /// The provider to fill in. /// True to edit this provider, false to add it as a new one, as an import does. public static DialogParameters CreateDialogParameters(EmbeddingProvider embeddingProvider, bool isEditing) => new() { { x => x.DataNum, embeddingProvider.Num }, { x => x.DataId, embeddingProvider.Id }, { x => x.DataName, embeddingProvider.Name }, { x => x.DataLLMProvider, embeddingProvider.UsedLLMProvider }, { x => x.DataCustomIconDataUrl, embeddingProvider.CustomIconDataUrl }, { x => x.DataModel, embeddingProvider.Model }, { x => x.DataHostname, embeddingProvider.Hostname }, { x => x.IsSelfHosted, embeddingProvider.IsSelfHosted }, { x => x.IsEditing, isEditing }, { x => x.DataHost, embeddingProvider.Host }, { x => x.DataTokenizerPath, embeddingProvider.TokenizerPath }, { x => x.DataTokenizerFingerprint, embeddingProvider.TokenizerFingerprint }, { x => x.DataTokenLimit, embeddingProvider.EffectiveTokenLimit }, { x => x.DataEmbeddingBatchSize, embeddingProvider.EffectiveEmbeddingBatchSize }, { x => x.HFInferenceProviderId, embeddingProvider.HFInferenceProvider }, { x => x.IsEnterpriseConfiguration, embeddingProvider.IsEnterpriseConfiguration }, }; /// /// Lets the user add an embedding provider from scratch, and stores it when they save. /// /// /// The caller tells the rest of the app that the configuration changed, as the sender of that message. /// /// The dialog service to show the provider settings with. /// The settings, which receive the provider. /// The service which checks the data sources against the new provider. /// The added provider, or null when the user cancelled or the organization does not allow adding one. public static async Task AddAsync(IDialogService dialogService, SettingsManager settingsManager, DataSourceEmbeddingService embeddingService) { if (!MayAdd(settingsManager)) return null; var dialogParameters = new DialogParameters { { x => x.IsEditing, false }, }; return await AddFromDialogAsync(dialogService, settingsManager, embeddingService, dialogParameters, isImport: false); } /// /// Lets the user import an embedding provider from a configuration snippet, and stores it when they save. /// /// /// The caller tells the rest of the app that the configuration changed, as the sender of that message. /// /// The dialog service to show the import and the provider settings with. /// The settings, which receive the provider. /// The service which checks the data sources against the new provider. /// The imported provider, or null when the user cancelled or the organization does not allow importing one. public static async Task ImportAsync(IDialogService dialogService, SettingsManager settingsManager, DataSourceEmbeddingService embeddingService) { if (!MayImport(settingsManager)) return null; var import = await ConfigurationSnippetImportDialog.ShowAsync(dialogService, PluginConfigurationObjectType.EMBEDDING_PROVIDER, TB("Import Embedding")); if (import?.ConfigurationObject is not EmbeddingProvider embeddingProvider) return null; var dialogParameters = CreateDialogParameters(embeddingProvider, isEditing: false); dialogParameters.Add(x => x.ImportedAPIKey, import.Secret); return await AddFromDialogAsync(dialogService, settingsManager, embeddingService, dialogParameters, isImport: true); } private static async Task AddFromDialogAsync(IDialogService dialogService, SettingsManager settingsManager, DataSourceEmbeddingService embeddingService, DialogParameters dialogParameters, bool isImport) { var dialogReference = await dialogService.ShowAsync(TB("Add Embedding Provider"), dialogParameters, DialogOptions.FULLSCREEN); var dialogResult = await dialogReference.Result; if (dialogResult is null || dialogResult.Canceled) return null; // // An organization can lock the import while the user is still filling in the form, e.g., // with a new version of its configuration plugin. Hence, the permission is checked once // more before the imported embedding provider is stored: // if (isImport && !MayImport(settingsManager)) return null; var addedEmbedding = (EmbeddingProvider)dialogResult.Data!; addedEmbedding = addedEmbedding with { Num = settingsManager.ConfigurationData.NextEmbeddingNum++ }; settingsManager.ConfigurationData.EmbeddingProviders.Add(addedEmbedding); await settingsManager.StoreSettings(); await embeddingService.QueueAllInternalDataSourcesAsync(); return addedEmbedding; } }