first version of the import function

This commit is contained in:
Peer Hogeterp committed 2026-09-24 17:20:54 +02:00
1 parent fda42ac24b
commit 0e63f93696
39 files changed
+1535 -46

No files matched your search

@@ -4,6 +4,25 @@
<MudDialog>
<DialogContent>
@if (!this.IsEditing && !this.IsReadOnly)
{
@if (!string.IsNullOrWhiteSpace(this.importReferenceIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importReferenceIssue</MudAlert>
}
@if (!string.IsNullOrWhiteSpace(this.relinkIssue))
{
<MudAlert Severity="Severity.Error" Class="mb-2">@this.relinkIssue</MudAlert>
}
@for (var index = 0; index < this.attachmentsToRelink.Count; index++)
{
var currentIndex = index;
<MudTextField T="string" Value="@this.attachmentsToRelink[index].ReplacementPath"
ValueChanged="@(value => this.attachmentsToRelink[currentIndex] = (this.attachmentsToRelink[currentIndex].OriginalPath, value))"
Label="@string.Format(T("Relink attachment: {0}"), this.attachmentsToRelink[index].OriginalPath)"
HelperText="@T("Enter an absolute path to an existing local file before saving.")" Variant="Variant.Outlined" Class="mb-2" />
}
}
@* A drop anywhere in this dialog belongs to the dialog, not to the page behind it: *@
@* The name for the drop state is given although nobody uses it: the messages of this dialog
are listed in a table, whose rows would otherwise ask for the same name. *@
@@ -265,4 +284,4 @@
}
}
</DialogActions>
</MudDialog>
</MudDialog>
@@ -2,6 +2,10 @@ using AIStudio.Chat;
using AIStudio.Components;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.ToolCallingSystem;
using Lua;
using Microsoft.AspNetCore.Components;
@@ -9,6 +13,9 @@ namespace AIStudio.Dialogs;
public partial class ChatTemplateDialog : MSGComponentBase
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -81,6 +88,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
[Inject]
private ILogger<ChatTemplateDialog> Logger { get; init; } = null!;
[Inject]
private ToolRegistry ToolRegistry { get; init; } = null!;
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
/// <summary>
@@ -91,6 +101,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
private bool dataIsValid;
private List<ContentBlock> dataExampleConversation = [];
private HashSet<FileAttachment> fileAttachments = [];
private List<(string OriginalPath, string ReplacementPath)> attachmentsToRelink = [];
private string relinkIssue = string.Empty;
private string importReferenceIssue = string.Empty;
private bool preselectTools;
private HashSet<string> selectedToolIds = new(StringComparer.Ordinal);
private bool preselectDataSources;
@@ -153,6 +166,15 @@ public partial class ChatTemplateDialog : MSGComponentBase
if(!this.IsEditing && firstRender)
this.form.ResetValidation();
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
@@ -176,6 +198,72 @@ public partial class ChatTemplateDialog : MSGComponentBase
IsEnterpriseConfiguration = false,
};
private async Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
ConfigurationImportFields.String(table, "Name");
ConfigurationImportFields.String(table, "SystemPrompt");
ConfigurationImportFields.String(table, "PredefinedUserPrompt", required: false);
ConfigurationImportFields.Bool(table, "AllowProfileUsage");
var messages = ConfigurationImportFields.Table(table, "ExampleConversation");
for (var index = 1; index <= messages.ArrayLength; index++)
{
if (messages[index].Type is not LuaValueType.Table || !messages[index].TryRead<LuaTable>(out var message))
throw new FormatException("An example conversation entry is not a table.");
ConfigurationImportFields.Enum<ChatRole>(message, "Role");
if (string.IsNullOrWhiteSpace(ConfigurationImportFields.String(message, "Content")))
throw new FormatException("An example conversation message is empty.");
}
if (table.TryGetValue("ToolIds", out _))
ConfigurationImportFields.Strings(table, "ToolIds");
if (table.TryGetValue("DataSourceOptions", out _))
{
var options = ConfigurationImportFields.Table(table, "DataSourceOptions");
ConfigurationImportFields.Bool(options, "DisableDataSources");
ConfigurationImportFields.Bool(options, "AutomaticDataSourceSelection");
ConfigurationImportFields.Bool(options, "AutomaticValidation");
if (options.TryGetValue("PreselectedDataSourceIds", out _))
ConfigurationImportFields.Strings(options, "PreselectedDataSourceIds");
}
if (!ChatTemplate.TryParseChatTemplateTable(0, table, Guid.Empty, string.Empty, out var parsed) || parsed is not ChatTemplate template)
throw new FormatException("The chat template fields are malformed.");
var paths = ConfigurationImportFields.Strings(table, "FileAttachments");
var validAttachments = new HashSet<FileAttachment>();
var toRelink = new List<(string OriginalPath, string ReplacementPath)>();
foreach (var path in paths)
{
if (ConfigurationImportFields.IsExistingLocalFile(path))
validAttachments.Add(FileAttachment.FromPath(path));
else
toRelink.Add((path, string.Empty));
}
this.DataName = template.Name;
this.DataSystemPrompt = template.SystemPrompt;
this.PredefinedUserPrompt = template.PredefinedUserPrompt;
this.AllowProfileUsage = template.AllowProfileUsage;
this.dataExampleConversation = template.ExampleConversation.Select(block => block.DeepClone()).ToList();
this.fileAttachments = validAttachments;
this.attachmentsToRelink = toRelink;
this.preselectTools = template.ToolIds is not null;
this.selectedToolIds = template.ToolIds is null ? new(StringComparer.Ordinal) : new(template.ToolIds, StringComparer.Ordinal);
this.preselectDataSources = template.DataSourceOptions is not null;
this.templateDataSourceOptions = template.DataSourceOptions?.CreateCopy() ?? new DataSourceOptions { DisableDataSources = false };
this.importReferenceIssue = await this.BuildImportReferenceIssue();
this.form.ResetValidation();
}
private async Task<string> BuildImportReferenceIssue()
{
var missingSources = this.templateDataSourceOptions.PreselectedDataSourceIds
.Where(id => this.SettingsManager.ConfigurationData.DataSources.All(source => source.Id != id)).ToList();
var availableToolIds = (await this.ToolRegistry.GetCatalogAsync(AIStudio.Tools.Components.CHAT))
.Select(item => item.Definition.Id).ToHashSet(StringComparer.Ordinal);
var missingTools = this.selectedToolIds.Where(id => !availableToolIds.Contains(id)).ToList();
var missing = missingSources.Select(id => $"data source {id}").Concat(missingTools.Select(id => $"tool {id}")).ToList();
return missing.Count == 0 ? string.Empty : $"Unavailable references: {string.Join(", ", missing)}. Review the selection before saving.";
}
private void SetSelectedToolIds(HashSet<string> toolIds) => this.selectedToolIds = toolIds;
private void RemoveMessage(ContentBlock item)
@@ -271,9 +359,23 @@ public partial class ChatTemplateDialog : MSGComponentBase
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
return;
if (this.IsReadOnly)
return;
this.relinkIssue = string.Empty;
foreach (var (originalPath, replacementPath) in this.attachmentsToRelink)
{
if (!ConfigurationImportFields.IsExistingLocalFile(replacementPath))
{
this.relinkIssue = string.Format(T("Relink the missing attachment '{0}' to an existing local file before saving."), originalPath);
return;
}
this.fileAttachments.Add(FileAttachment.FromPath(replacementPath));
}
await this.form.Validate();
// When the data is not valid, we don't store it:
@@ -329,4 +431,4 @@ public partial class ChatTemplateDialog : MSGComponentBase
}
private void Cancel() => this.MudDialog.Cancel();
}
}
@@ -0,0 +1,18 @@
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("Copy one exported configuration snippet from the item's Export configuration control and paste it below. You can review and change the filled form before saving a new local item.")
</MudJustifiedText>
<MudTextField T="string" @bind-Text="@this.snippet" Immediate="true" Label="@T("Configuration snippet")" Lines="6" AutoGrow="true" MaxLines="14" Variant="Variant.Outlined" />
@if (!string.IsNullOrWhiteSpace(this.issue))
{
<MudAlert Severity="Severity.Error" Class="mt-3">@this.issue</MudAlert>
}
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Cancel">@T("Cancel")</MudButton>
<MudButton OnClick="@this.Import" Disabled="@string.IsNullOrWhiteSpace(this.snippet)" Color="Color.Primary" Variant="Variant.Filled">@this.ImportLabel</MudButton>
</DialogActions>
</MudDialog>
@@ -0,0 +1,58 @@
using AIStudio.Components;
using AIStudio.Tools.PluginSystem;
using Lua;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Dialogs;
public partial class ConfigurationSnippetImportDialog : MSGComponentBase
{
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public string Section { get; set; } = string.Empty;
[Parameter]
public string ImportLabel { get; set; } = string.Empty;
private string snippet = string.Empty;
private string issue = string.Empty;
private void Import()
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet(this.Section))
{
this.issue = T("Import is locked by your organization.");
return;
}
if (!ConfigurationSnippetParser.TryParse(this.snippet, this.Section, out var table, out this.issue))
return;
try
{
ConfigurationSnippetImportValidation.Validate(this.Section, table);
this.MudDialog.Close(DialogResult.Ok(table));
}
catch (FormatException exception)
{
this.issue = exception.Message;
}
}
private void Cancel() => this.MudDialog.Cancel();
public static async Task<LuaTable?> ShowAsync(IDialogService service, string section, string title)
{
var parameters = new DialogParameters<ConfigurationSnippetImportDialog>
{
{ x => x.Section, section },
{ x => x.ImportLabel, title },
};
var dialog = await service.ShowAsync<ConfigurationSnippetImportDialog>(title, parameters, DialogOptions.FULLSCREEN);
var result = await dialog.Result;
return result is { Canceled: false, Data: LuaTable table } ? table : null;
}
}
@@ -5,6 +5,17 @@
<MudDialog>
<DialogContent>
@if (!this.IsEditing)
{
@if (!string.IsNullOrWhiteSpace(this.importCredentialIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importCredentialIssue</MudAlert>
}
@if (!string.IsNullOrWhiteSpace(this.importRetrievalIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importRetrievalIssue</MudAlert>
}
}
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
@@ -141,4 +152,4 @@
}
</MudButton>
</DialogActions>
</MudDialog>
</MudDialog>
@@ -4,8 +4,11 @@ using AIStudio.Settings.DataModel;
using AIStudio.Tools.ERIClient;
using AIStudio.Tools.ERIClient.DataModel;
using AIStudio.Tools.Services;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Validation;
using Lua;
using Microsoft.AspNetCore.Components;
using RetrievalInfo = AIStudio.Tools.ERIClient.DataModel.RetrievalInfo;
@@ -15,6 +18,9 @@ namespace AIStudio.Dialogs;
public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -43,6 +49,8 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
private bool dataIsValid;
private string[] dataIssues = [];
private string dataSecretStorageIssue = string.Empty;
private string importCredentialIssue = string.Empty;
private string importRetrievalIssue = string.Empty;
private string dataEditingPreviousInstanceName = string.Empty;
private List<AuthMethod> availableAuthMethods = [];
private DataSourceSecurity dataSecurityPolicy;
@@ -143,6 +151,15 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
// We don't want to show validation errors when the user opens the dialog.
if(!this.IsEditing && firstRender)
this.form.ResetValidation();
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
@@ -176,6 +193,53 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
MaxMatches = this.dataMaxMatches,
};
}
private Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
if (ConfigurationImportFields.String(table, "Type") != "ERI_V1")
throw new FormatException("This data source is not an ERI v1 data source.");
var name = ConfigurationImportFields.String(table, "Name");
var hostname = ConfigurationImportFields.String(table, "Hostname");
var port = ConfigurationImportFields.Int(table, "Port");
if (port is < 1 or > 65535)
throw new FormatException("The 'Port' field must be between 1 and 65535.");
var authMethod = ConfigurationImportFields.Enum<AuthMethod>(table, "AuthMethod");
if (authMethod is AuthMethod.KERBEROS)
throw new FormatException("Kerberos data sources cannot be imported from configuration snippets.");
var securityPolicy = ConfigurationImportFields.Enum<DataSourceSecurity>(table, "SecurityPolicy");
var retrievalId = ConfigurationImportFields.String(table, "SelectedRetrievalId");
var maxMatches = ConfigurationImportFields.Int(table, "MaxMatches", 10);
if (maxMatches is < 1 or > ushort.MaxValue)
throw new FormatException("The 'MaxMatches' field is outside the allowed range.");
var secretName = authMethod switch
{
AuthMethod.TOKEN => "Token",
AuthMethod.USERNAME_PASSWORD => "Password",
_ => string.Empty,
};
var secret = string.Empty;
var credentialIssue = string.Empty;
if (!string.IsNullOrEmpty(secretName))
secret = ConfigurationImportFields.Credential(table, secretName, out credentialIssue);
var username = ConfigurationImportFields.String(table, "Username", required: false);
this.dataName = name;
this.dataHostname = hostname;
this.dataPort = port;
this.dataAuthMethod = authMethod;
this.dataUsername = username;
this.dataSecurityPolicy = securityPolicy;
this.dataSelectedRetrievalProcess = this.dataSelectedRetrievalProcess with { Id = retrievalId };
this.dataMaxMatches = (ushort)maxMatches;
this.dataSecret = secret;
this.importCredentialIssue = credentialIssue;
this.importRetrievalIssue = string.Empty;
this.connectionTested = false;
this.connectionSuccessfulTested = false;
this.form.ResetValidation();
return Task.CompletedTask;
}
private bool IsConnectionEncrypted() => this.dataHostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase);
@@ -251,6 +315,17 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
}
this.availableRetrievalProcesses = retrievalInfoRequest.Data ?? [];
if (!string.IsNullOrWhiteSpace(this.dataSelectedRetrievalProcess.Id))
{
var importedRetrieval = this.availableRetrievalProcesses.FirstOrDefault(item => item.Id == this.dataSelectedRetrievalProcess.Id);
if (importedRetrieval != default)
this.dataSelectedRetrievalProcess = importedRetrieval;
else
{
this.importRetrievalIssue = string.Format(T("The imported retrieval process '{0}' is unavailable. Select another process before saving."), this.dataSelectedRetrievalProcess.Id);
this.dataSelectedRetrievalProcess = default;
}
}
this.connectionTested = true;
this.connectionSuccessfulTested = true;
@@ -304,6 +379,9 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES"))
return;
await this.form.Validate();
var testConnectionValidation = this.dataSourceValidation.ValidateTestedConnection();
@@ -337,4 +415,4 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
}
private void Cancel() => this.MudDialog.Cancel();
}
}
@@ -0,0 +1,45 @@
@using AIStudio.Provider
@using AIStudio.Settings
@using AIStudio.Settings.DataModel
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
@if (!string.IsNullOrWhiteSpace(this.referenceIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.referenceIssue</MudAlert>
}
<MudForm @ref="@this.form" @bind-IsValid="@this.isValid" @bind-Errors="@this.issues">
<MudTextField T="string" @bind-Text="@this.name" Validation="@this.ValidateName" Immediate="true" Label="@T("Policy name")" Counter="60" MaxLength="60" Variant="Variant.Outlined" Class="mb-3" />
<MudTextField T="string" @bind-Text="@this.description" Validation="@this.ValidateDescription" Immediate="true" Label="@T("Policy description")" Counter="512" MaxLength="512" Lines="3" AutoGrow="true" Variant="Variant.Outlined" Class="mb-3" />
<MudTextSwitch @bind-Value="@this.hideDefinition" Label="@T("Hide the policy definition when distributed via configuration plugin?")" />
<ConfigurationMinConfidenceSelection RestrictToGlobalMinimumConfidence="true" SelectedValue="@(() => this.minimumConfidence)" SelectionUpdate="@(value => this.minimumConfidence = value)" />
<ToolSelectionField Component="@AIStudio.Tools.Components.DOCUMENT_ANALYSIS_ASSISTANT" SelectedToolIds="@this.allowedToolIds" SelectedToolIdsChanged="@(value => this.allowedToolIds = value)" Label="@T("Tools this policy permits")" />
<MudSelect T="string" @bind-Value="@this.providerId" Label="@T("Preselect a provider")" Variant="Variant.Outlined" Class="mb-3">
<MudSelectItem T="string" Value="@string.Empty">@T("No provider")</MudSelectItem>
@foreach (var provider in this.SettingsManager.GetAllProviders())
{
<MudSelectItem T="string" Value="@provider.Id">@provider.InstanceName</MudSelectItem>
}
</MudSelect>
<MudSelect T="string" @bind-Value="@this.profileId" Label="@T("Preselect a profile")" Variant="Variant.Outlined" Class="mb-3">
<MudSelectItem T="string" Value="@string.Empty">@T("Use app default profile")</MudSelectItem>
<MudSelectItem T="string" Value="@Profile.NO_PROFILE.Id">@T("No profile")</MudSelectItem>
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles)
{
<MudSelectItem T="string" Value="@profile.Id">@profile.Name</MudSelectItem>
}
</MudSelect>
<MudTextSwitch @bind-Value="@this.isProtected" Label="@T("Would you like to protect this policy so that you cannot accidentally edit or delete it?")" />
<MudTextField T="string" @bind-Text="@this.analysisRules" Validation="@this.ValidateAnalysisRules" Immediate="true" Label="@T("Analysis rules")" Lines="5" AutoGrow="true" MaxLines="26" Variant="Variant.Outlined" Class="mb-3" />
<ReadFileContent Text="@T("Load analysis rules from document")" @bind-FileContent="@this.analysisRules" EnableDragDrop="true" />
<MudTextField T="string" @bind-Text="@this.outputRules" Validation="@this.ValidateOutputRules" Immediate="true" Label="@T("Output rules")" Lines="5" AutoGrow="true" MaxLines="26" Variant="Variant.Outlined" Class="mb-3" />
<ReadFileContent Text="@T("Load output rules from document")" @bind-FileContent="@this.outputRules" EnableDragDrop="true" />
</MudForm>
<Issues IssuesData="@this.issues" />
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">@T("Cancel")</MudButton>
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">@T("Add")</MudButton>
</DialogActions>
</MudDialog>
@@ -0,0 +1,134 @@
using AIStudio.Components;
using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.ToolCallingSystem;
using Lua;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Dialogs;
public partial class DocumentAnalysisPolicyDialog : MSGComponentBase
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
[Inject]
private ToolRegistry ToolRegistry { get; init; } = null!;
private MudForm form = null!;
private bool isValid;
private string[] issues = [];
private string referenceIssue = string.Empty;
private string name = string.Empty;
private string description = string.Empty;
private string analysisRules = string.Empty;
private string outputRules = string.Empty;
private ConfidenceLevel minimumConfidence = ConfidenceLevel.NONE;
private HashSet<string> allowedToolIds = new(StringComparer.Ordinal);
private string providerId = string.Empty;
private string profileId = Profile.NO_PROFILE.Id;
private bool hideDefinition;
private bool isProtected;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
var importedName = ConfigurationImportFields.String(table, "PolicyName");
var importedDescription = ConfigurationImportFields.String(table, "PolicyDescription");
var importedAnalysisRules = ConfigurationImportFields.String(table, "AnalysisRules");
var importedOutputRules = ConfigurationImportFields.String(table, "OutputRules");
var confidence = ConfigurationImportFields.Enum<ConfidenceLevel>(table, "MinimumProviderConfidence");
var toolIds = ConfigurationImportFields.Strings(table, "AllowedToolIds");
var importedProviderId = ConfigurationImportFields.String(table, "PreselectedProvider", required: false);
var importedProfileId = ConfigurationImportFields.String(table, "PreselectedProfile", required: false);
var hide = ConfigurationImportFields.Bool(table, "HidePolicyDefinition");
this.name = importedName;
this.description = importedDescription;
this.analysisRules = importedAnalysisRules;
this.outputRules = importedOutputRules;
this.minimumConfidence = confidence;
this.allowedToolIds = new(toolIds, StringComparer.Ordinal);
this.providerId = importedProviderId;
this.profileId = importedProfileId;
this.hideDefinition = hide;
var missing = new List<string>();
if (!string.IsNullOrWhiteSpace(this.providerId) && this.SettingsManager.GetAllProviders().All(provider => provider.Id != this.providerId))
missing.Add($"provider {this.providerId}");
if (!string.IsNullOrWhiteSpace(this.profileId) && this.profileId != Profile.NO_PROFILE.Id && this.SettingsManager.ConfigurationData.Profiles.All(profile => profile.Id != this.profileId))
missing.Add($"profile {this.profileId}");
var availableToolIds = (await this.ToolRegistry.GetCatalogAsync(AIStudio.Tools.Components.DOCUMENT_ANALYSIS_ASSISTANT))
.Select(item => item.Definition.Id).ToHashSet(StringComparer.Ordinal);
missing.AddRange(this.allowedToolIds.Where(id => !availableToolIds.Contains(id)).Select(id => $"tool {id}"));
this.referenceIssue = missing.Count == 0 ? string.Empty : $"Unavailable references: {string.Join(", ", missing)}. Review the selections before saving.";
this.form.ResetValidation();
}
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))
return;
await this.form.Validate();
if (!this.isValid)
return;
this.MudDialog.Close(DialogResult.Ok(new DataDocumentAnalysisPolicy
{
Id = Guid.NewGuid().ToString(),
PolicyName = this.name.Trim(),
PolicyDescription = this.description.Trim(),
AnalysisRules = this.analysisRules.Trim(),
OutputRules = this.outputRules.Trim(),
MinimumProviderConfidence = this.minimumConfidence,
AllowedToolIds = new(this.allowedToolIds, StringComparer.Ordinal),
PreselectedProvider = this.providerId,
PreselectedProfile = this.profileId,
HidePolicyDefinition = this.hideDefinition,
IsProtected = this.isProtected,
}));
}
private string? ValidateName(string value)
{
if (string.IsNullOrWhiteSpace(value))
return T("Please provide a name for your policy. This name will be used to identify the policy in AI Studio.");
if (value.Length is < 6 or > 60)
return T("The name of your policy must be between 6 and 60 characters long.");
if (this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Any(policy => policy.PolicyName == value))
return T("A policy with this name already exists. Please choose a different name.");
return null;
}
private string? ValidateDescription(string value) => value.Length is < 32 or > 512
? T("The description of your policy must be between 32 and 512 characters long.") : null;
private string? ValidateAnalysisRules(string value) => string.IsNullOrWhiteSpace(value)
? T("Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents.") : null;
private string? ValidateOutputRules(string value) => string.IsNullOrWhiteSpace(value)
? T("Please provide a description of your output rules. This rules will be used to instruct the AI on how to format the output of the analysis.") : null;
private void Cancel() => this.MudDialog.Cancel();
}
@@ -5,6 +5,13 @@
<MudDialog>
<DialogContent>
@if (!this.IsEditing)
{
@if (!string.IsNullOrWhiteSpace(this.importCredentialIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importCredentialIssue</MudAlert>
}
}
@if (this.IsEnterpriseConfiguration)
{
<MudAlert Severity="Severity.Info" Class="mb-4">
@@ -4,8 +4,11 @@ using AIStudio.Provider.HuggingFace;
using AIStudio.Settings;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Validation;
using Lua;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Host = AIStudio.Provider.SelfHosted.Host;
@@ -14,6 +17,9 @@ namespace AIStudio.Dialogs;
public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -134,6 +140,7 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
private string dataAPIKey = string.Empty;
private bool dataHadStoredAPIKeyOnLoad;
private string dataAPIKeyStorageIssue = string.Empty;
private string importCredentialIssue = string.Empty;
private string dataEditingPreviousInstanceName = string.Empty;
private string dataLoadingModelsIssue = string.Empty;
private bool dataConfiguredModelIsNotOffered;
@@ -190,6 +197,40 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
HFInferenceProvider = this.HFInferenceProviderId,
};
}
private Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
var modelTable = ConfigurationImportFields.Table(table, "Model");
var model = new Model(ConfigurationImportFields.String(modelTable, "Id"), ConfigurationImportFields.String(modelTable, "DisplayName"));
var name = ConfigurationImportFields.String(table, "Name");
var provider = ConfigurationImportFields.Enum<LLMProviders>(table, "UsedLLMProvider");
var host = ConfigurationImportFields.Enum<Host>(table, "Host");
var hostname = ConfigurationImportFields.String(table, "Hostname");
var hfProvider = table.TryGetValue("HFInferenceProvider", out _)
? ConfigurationImportFields.Enum<HFInferenceProvider>(table, "HFInferenceProvider") : HFInferenceProvider.NONE;
var tokenizerPath = ConfigurationImportFields.String(table, "TokenizerPath", required: false);
var tokenLimit = ConfigurationImportFields.Int(table, "TokenLimit", EmbeddingProvider.DEFAULT_TOKEN_LIMIT);
var batchSize = ConfigurationImportFields.Int(table, "EmbeddingBatchSize", EmbeddingProvider.DEFAULT_EMBEDDING_BATCH_SIZE);
var credential = ConfigurationImportFields.Credential(table, "APIKey", out var credentialIssue);
this.DataName = name;
this.DataLLMProvider = provider;
this.DataHost = host;
this.DataHostname = hostname;
this.HFInferenceProviderId = hfProvider;
this.DataModel = model;
this.dataFilePath = tokenizerPath;
this.DataTokenLimit = tokenLimit;
this.DataEmbeddingBatchSize = batchSize;
this.showExpertSettings = !string.IsNullOrWhiteSpace(tokenizerPath) || tokenLimit != EmbeddingProvider.DEFAULT_TOKEN_LIMIT || batchSize != EmbeddingProvider.DEFAULT_EMBEDDING_BATCH_SIZE;
this.dataAPIKey = credential;
this.importCredentialIssue = credentialIssue;
if (this.availableModels.All(candidate => candidate.Id != model.Id))
this.availableModels.Add(model);
this.form.ResetValidation();
return Task.CompletedTask;
}
#region Overrides of ComponentBase
@@ -247,6 +288,15 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
// We don't want to show validation errors when the user opens the dialog.
if(!this.IsEditing && firstRender)
this.form.ResetValidation();
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("EMBEDDING_PROVIDERS"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
@@ -267,6 +317,9 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("EMBEDDING_PROVIDERS"))
return;
this.dataStoreWasAttempted = true;
await this.dataTokenizerValidationTask;
await this.form.Validate();
@@ -99,4 +99,4 @@
</MudButton>
}
</DialogActions>
</MudDialog>
</MudDialog>
@@ -1,5 +1,8 @@
using AIStudio.Components;
using AIStudio.Settings;
using AIStudio.Tools.PluginSystem;
using Lua;
using Microsoft.AspNetCore.Components;
@@ -7,6 +10,9 @@ namespace AIStudio.Dialogs;
public partial class ProfileDialog : MSGComponentBase
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -78,6 +84,19 @@ public partial class ProfileDialog : MSGComponentBase
IsEnterpriseConfiguration = false,
};
private Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
var name = ConfigurationImportFields.String(table, "Name");
var needToKnow = ConfigurationImportFields.String(table, "NeedToKnow");
var actions = ConfigurationImportFields.String(table, "Actions");
this.DataName = name;
this.DataNeedToKnow = needToKnow;
this.DataActions = actions;
this.form.ResetValidation();
return Task.CompletedTask;
}
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
@@ -104,6 +123,15 @@ public partial class ProfileDialog : MSGComponentBase
if(!this.IsEditing && firstRender)
this.form.ResetValidation();
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
@@ -111,6 +139,9 @@ public partial class ProfileDialog : MSGComponentBase
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES"))
return;
if (this.IsReadOnly)
return;
@@ -165,4 +196,4 @@ public partial class ProfileDialog : MSGComponentBase
}
private void Cancel() => this.MudDialog.Cancel();
}
}
@@ -4,6 +4,13 @@
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
@if (!this.IsEditing)
{
@if (!string.IsNullOrWhiteSpace(this.importCredentialIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importCredentialIssue</MudAlert>
}
}
@if (this.IsEnterpriseConfiguration)
{
<MudAlert Severity="Severity.Info" Class="mb-4">
@@ -10,8 +10,11 @@ using AIStudio.Tools.Rust;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.Services;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Validation;
using Lua;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
@@ -24,6 +27,9 @@ namespace AIStudio.Dialogs;
/// </summary>
public partial class ProviderDialog : MSGComponentBase, ISecretId
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
private enum ReasoningOverrideMode
{
AUTOMATIC,
@@ -154,6 +160,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
private bool dataHadStoredAPIKeyOnLoad;
private string dataManuallyModel = string.Empty;
private string dataAPIKeyStorageIssue = string.Empty;
private string importCredentialIssue = string.Empty;
private string dataEditingPreviousInstanceName = string.Empty;
private string dataLoadingModelsIssue = string.Empty;
private string dataFilePath = string.Empty;
@@ -232,6 +239,41 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
return this.DataModel;
}
private Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
var modelTable = ConfigurationImportFields.Table(table, "Model");
var model = new Model(ConfigurationImportFields.String(modelTable, "Id"), ConfigurationImportFields.String(modelTable, "DisplayName"));
var name = ConfigurationImportFields.String(table, "InstanceName");
var provider = ConfigurationImportFields.Enum<LLMProviders>(table, "UsedLLMProvider");
var host = ConfigurationImportFields.Enum<Host>(table, "Host");
var hostname = ConfigurationImportFields.String(table, "Hostname");
var hfProvider = table.TryGetValue("HFInferenceProvider", out _)
? ConfigurationImportFields.Enum<HFInferenceProvider>(table, "HFInferenceProvider") : HFInferenceProvider.NONE;
var tokenizerPath = ConfigurationImportFields.String(table, "TokenizerPath", required: false);
var additionalParameters = ConfigurationImportFields.String(table, "AdditionalJsonApiParameters", required: false);
var credential = ConfigurationImportFields.Credential(table, "APIKey", out var credentialIssue);
var overrides = ProviderCapabilityOverrides.TryParseFromLuaTable(0, table, Guid.Empty, this.Logger);
this.DataInstanceName = name;
this.DataLLMProvider = provider;
this.DataHost = host;
this.DataHostname = hostname;
this.HFInferenceProviderId = hfProvider;
this.DataModel = model;
this.dataManuallyModel = model.Id;
this.dataFilePath = tokenizerPath;
this.AdditionalJsonApiParameters = additionalParameters;
this.capabilityOverrides = overrides ?? new();
this.showExpertSettings = this.capabilityOverrides.HasOverrides || !string.IsNullOrWhiteSpace(this.dataFilePath) || !string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters);
this.dataAPIKey = credential;
this.importCredentialIssue = credentialIssue;
if (this.availableModels.All(candidate => candidate.Id != model.Id))
this.availableModels.Add(model);
this.form.ResetValidation();
return Task.CompletedTask;
}
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
@@ -297,6 +339,15 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
// We don't want to show validation errors when the user opens the dialog.
if(!this.IsEditing && firstRender)
this.form.ResetValidation();
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("LLM_PROVIDERS"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
@@ -317,6 +368,9 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("LLM_PROVIDERS"))
return;
this.dataStoreWasAttempted = true;
await this.dataTokenizerValidationTask;
await this.form.Validate();
@@ -81,13 +81,16 @@
</MudText>
}
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddChatTemplate">
@T("Add Chat Template")
</MudButton>
<MudStack Row="true" Wrap="Wrap.Wrap" Class="mt-3 mb-6">
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" OnClick="@this.AddChatTemplate">
@T("Add Chat Template")
</MudButton>
<LockableButton Text="@T("Import Chat Template")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportChatTemplate" ButtonVariant="Variant.Outlined" NoMargin="true" />
</MudStack>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
@T("Close")
</MudButton>
</DialogActions>
</MudDialog>
</MudDialog>
@@ -2,6 +2,8 @@ using AIStudio.Chat;
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
using Lua;
namespace AIStudio.Dialogs.Settings;
public partial class SettingsDialogChatTemplate : SettingsDialogBase
@@ -26,14 +28,30 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
#endregion
private async Task AddChatTemplate()
private Task AddChatTemplate() => this.AddChatTemplate(null);
private async Task ImportChatTemplate()
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
return;
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "CHAT_TEMPLATES", T("Import Chat Template"));
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
await this.AddChatTemplate(table);
}
private async Task AddChatTemplate(LuaTable? importedConfiguration)
{
if (importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
return;
var dialogParameters = new DialogParameters<ChatTemplateDialog>
{
{ x => x.IsEditing, false },
};
if (importedConfiguration is not null)
dialogParameters.Add(x => x.ImportedConfiguration, importedConfiguration);
if (this.CreateTemplateFromExistingChatThread)
if (this.CreateTemplateFromExistingChatThread && importedConfiguration is null)
{
dialogParameters.Add(x => x.CreateFromExistingChatThread, this.CreateTemplateFromExistingChatThread);
dialogParameters.Add(x => x.ExistingChatThread, this.ExistingChatThread);
@@ -41,7 +59,8 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Add Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled ||
(importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES")))
return;
var addedChatTemplate = (ChatTemplate)dialogResult.Data!;
@@ -229,4 +248,4 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
if (!string.IsNullOrWhiteSpace(luaCode))
await this.RustService.CopyText2Clipboard(luaCode);
}
}
}
@@ -64,13 +64,16 @@
</MudText>
}
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddProfile">
@T("Add Profile")
</MudButton>
<MudStack Row="true" Wrap="Wrap.Wrap" Class="mt-3 mb-6">
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" OnClick="@this.AddProfile">
@T("Add Profile")
</MudButton>
<LockableButton Text="@T("Import Profile")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES"))" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportProfile" ButtonVariant="Variant.Outlined" NoMargin="true" />
</MudStack>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
@T("Close")
</MudButton>
</DialogActions>
</MudDialog>
</MudDialog>
@@ -1,19 +1,38 @@
using AIStudio.Settings;
using Lua;
namespace AIStudio.Dialogs.Settings;
public partial class SettingsDialogProfiles : SettingsDialogBase
{
private async Task AddProfile()
private Task AddProfile() => this.AddProfile(null);
private async Task ImportProfile()
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES"))
return;
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "PROFILES", T("Import Profile"));
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES"))
await this.AddProfile(table);
}
private async Task AddProfile(LuaTable? importedConfiguration)
{
if (importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES"))
return;
var dialogParameters = new DialogParameters<ProfileDialog>
{
{ x => x.IsEditing, false },
};
if (importedConfiguration is not null)
dialogParameters.Add(x => x.ImportedConfiguration, importedConfiguration);
var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("Add Profile"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled ||
(importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("PROFILES")))
return;
var addedProfile = (Profile)dialogResult.Data!;
@@ -95,4 +114,4 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
}
}
@@ -5,6 +5,13 @@
<MudDialog>
<DialogContent>
@if (!this.IsEditing)
{
@if (!string.IsNullOrWhiteSpace(this.importCredentialIssue))
{
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importCredentialIssue</MudAlert>
}
}
@if (this.IsEnterpriseConfiguration)
{
<MudAlert Severity="Severity.Info" Class="mb-4">
@@ -3,8 +3,11 @@ using AIStudio.Provider;
using AIStudio.Provider.HuggingFace;
using AIStudio.Settings;
using AIStudio.Tools.Services;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Validation;
using Lua;
using Microsoft.AspNetCore.Components;
using Host = AIStudio.Provider.SelfHosted.Host;
@@ -13,6 +16,9 @@ namespace AIStudio.Dialogs;
public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
{
[Parameter]
public LuaTable? ImportedConfiguration { get; set; }
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -107,6 +113,7 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
private string dataAPIKey = string.Empty;
private bool dataHadStoredAPIKeyOnLoad;
private string dataAPIKeyStorageIssue = string.Empty;
private string importCredentialIssue = string.Empty;
private string dataEditingPreviousInstanceName = string.Empty;
private string dataLoadingModelsIssue = string.Empty;
@@ -153,6 +160,33 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
HFInferenceProvider = this.HFInferenceProviderId,
};
}
private Task ImportConfiguration(LuaTable table)
{
ConfigurationImportFields.ValidateExportId(table);
var modelTable = ConfigurationImportFields.Table(table, "Model");
var model = new Model(ConfigurationImportFields.String(modelTable, "Id"), ConfigurationImportFields.String(modelTable, "DisplayName"));
var name = ConfigurationImportFields.String(table, "Name");
var provider = ConfigurationImportFields.Enum<LLMProviders>(table, "UsedLLMProvider");
var host = ConfigurationImportFields.Enum<Host>(table, "Host");
var hostname = ConfigurationImportFields.String(table, "Hostname");
var hfProvider = table.TryGetValue("HFInferenceProvider", out _)
? ConfigurationImportFields.Enum<HFInferenceProvider>(table, "HFInferenceProvider") : HFInferenceProvider.NONE;
var credential = ConfigurationImportFields.Credential(table, "APIKey", out var credentialIssue);
this.DataName = name;
this.DataLLMProvider = provider;
this.DataHost = host;
this.DataHostname = hostname;
this.HFInferenceProviderId = hfProvider;
this.DataModel = model;
this.dataAPIKey = credential;
this.importCredentialIssue = credentialIssue;
if (this.availableModels.All(candidate => candidate.Id != model.Id))
this.availableModels.Add(model);
this.form.ResetValidation();
return Task.CompletedTask;
}
#region Overrides of ComponentBase
@@ -205,6 +239,15 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
// We don't want to show validation errors when the user opens the dialog.
if(!this.IsEditing && firstRender)
this.form.ResetValidation();
if (firstRender && this.ImportedConfiguration is not null)
{
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("TRANSCRIPTION_PROVIDERS"))
this.MudDialog.Cancel();
else
await this.ImportConfiguration(this.ImportedConfiguration);
this.StateHasChanged();
}
await base.OnAfterRenderAsync(firstRender);
}
@@ -225,6 +268,9 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
private async Task Store()
{
if (this.ImportedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("TRANSCRIPTION_PROVIDERS"))
return;
await this.form.Validate();
this.dataAPIKeyStorageIssue = string.Empty;