mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-15 21:33:47 +00:00
first version of the import function
This commit is contained in:
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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user