mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-09-27 02:53:38 +00:00
Merge 003922efff into 4baf21656a
This commit is contained in:
commit
3b23a2682d
@ -47,6 +47,7 @@ else
|
||||
<MudButton OnClick="@this.AddPolicy" Disabled="@this.ArePolicyControlsDisabled" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@T("Add policy")
|
||||
</MudButton>
|
||||
<LockableButton Text="@T("Import document analysis policy")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))" Disabled="@(() => this.ArePolicyControlsDisabled)" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportPolicy" ButtonVariant="Variant.Outlined" NoMargin="true" />
|
||||
<MudButton OnClick="@this.RemovePolicy" Disabled="@(this.ArePolicyControlsDisabled || (this.selectedPolicy?.IsProtected ?? true) || (this.selectedPolicy?.IsEnterpriseConfiguration ?? true))" Variant="Variant.Filled" Color="Color.Error">
|
||||
@T("Delete this policy")
|
||||
</MudButton>
|
||||
|
||||
@ -9,6 +9,7 @@ using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using LuaTable = Lua.LuaTable;
|
||||
|
||||
using SharedTools;
|
||||
|
||||
@ -483,6 +484,39 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ImportPolicy()
|
||||
{
|
||||
if (this.ArePolicyControlsDisabled || !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))
|
||||
return;
|
||||
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "DOCUMENT_ANALYSIS_POLICIES", T("Import document analysis policy"));
|
||||
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))
|
||||
await this.AddImportedPolicy(table);
|
||||
}
|
||||
|
||||
private async Task AddImportedPolicy(LuaTable importedConfiguration)
|
||||
{
|
||||
if (this.ArePolicyControlsDisabled || !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))
|
||||
return;
|
||||
|
||||
var parameters = new DialogParameters<DocumentAnalysisPolicyDialog>
|
||||
{
|
||||
{ x => x.ImportedConfiguration, importedConfiguration },
|
||||
};
|
||||
var dialogReference = await this.DialogService.ShowAsync<DocumentAnalysisPolicyDialog>(T("Add policy"), parameters, DialogOptions.FULLSCREEN);
|
||||
var result = await dialogReference.Result;
|
||||
if (result is null || result.Canceled || result.Data is not DataDocumentAnalysisPolicy policy ||
|
||||
!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DOCUMENT_ANALYSIS_POLICIES"))
|
||||
return;
|
||||
|
||||
var addedPolicy = policy with
|
||||
{
|
||||
Num = this.SettingsManager.ConfigurationData.NextDocumentAnalysisPolicyNum++,
|
||||
};
|
||||
this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Add(addedPolicy);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
this.SelectedPolicyChanged(addedPolicy);
|
||||
}
|
||||
|
||||
private async Task AddPolicy()
|
||||
{
|
||||
if (this.ArePolicyControlsDisabled)
|
||||
|
||||
@ -1216,6 +1216,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTA
|
||||
-- A policy with this name already exists. Please choose a different name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3584374593"] = "A policy with this name already exists. Please choose a different name."
|
||||
|
||||
-- Import document analysis policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3800115485"] = "Import document analysis policy"
|
||||
|
||||
-- Load analysis rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3813558135"] = "Load analysis rules from document"
|
||||
|
||||
@ -3925,6 +3928,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T750361472"] = "Can
|
||||
-- External Data (ERI-Server v1)
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T774473996"] = "External Data (ERI-Server v1)"
|
||||
|
||||
-- Import ERI v1 Data Source
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T777621786"] = "Import ERI v1 Data Source"
|
||||
|
||||
-- Cannot export this ERI data source because no authentication secret is configured. The issue was: {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T782820095"] = "Cannot export this ERI data source because no authentication secret is configured. The issue was: {0}"
|
||||
|
||||
@ -4885,6 +4891,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T21748
|
||||
-- Model
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T2189814010"] = "Model"
|
||||
|
||||
-- Import Embedding Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T229678442"] = "Import Embedding Provider"
|
||||
|
||||
-- Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and 'joyful' would be more alike than those for 'happy' and 'sad'.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T2419962612"] = "Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and 'joyful' would be more alike than those for 'happy' and 'sad'."
|
||||
|
||||
@ -4960,6 +4969,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T146957
|
||||
-- Uses the provider-configured model
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1760715963"] = "Uses the provider-configured model"
|
||||
|
||||
-- Import LLM Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T178720520"] = "Import LLM Provider"
|
||||
|
||||
-- Add Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1806589097"] = "Add Provider"
|
||||
|
||||
@ -5098,6 +5110,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T58
|
||||
-- This transcription provider is trusted by your organization for data source security checks.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T601264181"] = "This transcription provider is trusted by your organization for data source security checks."
|
||||
|
||||
-- Import Transcription Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T682006405"] = "Import Transcription Provider"
|
||||
|
||||
-- This transcription provider is managed by your organization. You can set your own API key.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T690752279"] = "This transcription provider is managed by your organization. You can set your own API key."
|
||||
|
||||
@ -5746,6 +5761,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T204496403"] = "The chat
|
||||
-- Profile Usage
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profile Usage"
|
||||
|
||||
-- Relink attachment: {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2234793579"] = "Relink attachment: {0}"
|
||||
|
||||
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs."
|
||||
|
||||
@ -5773,6 +5791,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579080722"] = "No, pro
|
||||
-- You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579208700"] = "You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with."
|
||||
|
||||
-- Enter an absolute path to an existing local file, or remove this attachment.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2603688456"] = "Enter an absolute path to an existing local file, or remove this attachment."
|
||||
|
||||
-- Predefined User Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2607897066"] = "Predefined User Input"
|
||||
|
||||
@ -5812,6 +5833,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3268470871"] = "No, cha
|
||||
-- A chat template may decide which tools a chat starts with. Without such a decision, those chats start with the tools you chose as your default in the chat options. Deciding and then picking nothing is a different statement: such chats start with no tool at all, no matter what your default says.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3329415439"] = "A chat template may decide which tools a chat starts with. Without such a decision, those chats start with the tools you chose as your default in the chat options. Deciding and then picking nothing is a different statement: such chats start with no tool at all, no matter what your default says."
|
||||
|
||||
-- Some attachments in this chat template are missing. Relink or remove them before adding the template.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3342669429"] = "Some attachments in this chat template are missing. Relink or remove them before adding the template."
|
||||
|
||||
-- Add a message
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Add a message"
|
||||
|
||||
@ -5821,6 +5845,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3448155331"] = "Close"
|
||||
-- Unsupported content type
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Unsupported content type"
|
||||
|
||||
-- Relink the missing attachment '{0}' to an existing local file or remove it before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3582611137"] = "Relink the missing attachment '{0}' to an existing local file or remove it before saving."
|
||||
|
||||
-- What system prompt do you want to use?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3652587353"] = "What system prompt do you want to use?"
|
||||
|
||||
@ -5860,6 +5887,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4199560726"] = "Create
|
||||
-- Enter a message
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T446374405"] = "Enter a message"
|
||||
|
||||
-- The chat template fields are malformed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T539301384"] = "The chat template fields are malformed."
|
||||
|
||||
-- Data Sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T558345131"] = "Data Sources"
|
||||
|
||||
@ -5869,6 +5899,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T628396066"] = "System P
|
||||
-- A chat started with this template begins with these data sources and options. All of it stays changeable in the chat itself.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T650711085"] = "A chat started with this template begins with these data sources and options. All of it stays changeable in the chat itself."
|
||||
|
||||
-- Remove this attachment
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T70791272"] = "Remove this attachment"
|
||||
|
||||
-- The selection stays changeable in the chat, and every tool still has to meet the confidence requirements of the provider in use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T722788866"] = "The selection stays changeable in the chat, and every tool still has to meet the confidence requirements of the provider in use."
|
||||
|
||||
@ -5959,12 +5992,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T767586087"
|
||||
-- {0} embedding provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T818101181"] = "{0} embedding provider"
|
||||
|
||||
-- Configuration snippet
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T3867536704"] = "Configuration snippet"
|
||||
|
||||
-- Import is locked by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T4101825749"] = "Import is locked by your organization."
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T4214407984"] = "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."
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- No
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIRMDIALOG::T1642511898"] = "No"
|
||||
|
||||
-- Yes
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIRMDIALOG::T3013883440"] = "Yes"
|
||||
|
||||
-- The imported retrieval process '{0}' is unavailable. Select another process before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DATASOURCEERI_V1DIALOG::T1759490982"] = "The imported retrieval process '{0}' is unavailable. Select another process before saving."
|
||||
|
||||
-- How many matches do you want at most per query?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DATASOURCEERI_V1DIALOG::T1827669611"] = "How many matches do you want at most per query?"
|
||||
|
||||
@ -6538,6 +6586,84 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DIRECTCHATLAUNCHERSETTINGSDIALOG::T854110894
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DIRECTCHATLAUNCHERSETTINGSDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- No, the policy can be edited
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1286595725"] = "No, the policy can be edited"
|
||||
|
||||
-- Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1291179736"] = "Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents."
|
||||
|
||||
-- Yes, protect this policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1762380857"] = "Yes, protect this policy"
|
||||
|
||||
-- Hide the policy definition when distributed via configuration plugin?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1875622568"] = "Hide the policy definition when distributed via configuration plugin?"
|
||||
|
||||
-- No profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2028602035"] = "No profile"
|
||||
|
||||
-- Load output rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2168201568"] = "Load output rules from document"
|
||||
|
||||
-- Preselect a profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2322771068"] = "Preselect a profile"
|
||||
|
||||
-- The name of your policy must be between 6 and 60 characters long.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2435013256"] = "The name of your policy must be between 6 and 60 characters long."
|
||||
|
||||
-- Preselect a provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2440815970"] = "Preselect a provider"
|
||||
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- Policy name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2879019438"] = "Policy name"
|
||||
|
||||
-- Analysis rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3108719748"] = "Analysis rules"
|
||||
|
||||
-- Tools this policy permits
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T31356122"] = "Tools this policy permits"
|
||||
|
||||
-- No, show the policy definition
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3166091879"] = "No, show the policy definition"
|
||||
|
||||
-- The description of your policy must be between 32 and 512 characters long.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3285636934"] = "The description of your policy must be between 32 and 512 characters long."
|
||||
|
||||
-- A policy with this name already exists. Please choose a different name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3584374593"] = "A policy with this name already exists. Please choose a different name."
|
||||
|
||||
-- Use app default profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3587225583"] = "Use app default profile"
|
||||
|
||||
-- No provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3740605451"] = "No provider"
|
||||
|
||||
-- Load analysis rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3813558135"] = "Load analysis rules from document"
|
||||
|
||||
-- Output rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3918193587"] = "Output rules"
|
||||
|
||||
-- Please provide a name for your policy. This name will be used to identify the policy in AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T4040507702"] = "Please provide a name for your policy. This name will be used to identify the policy in AI Studio."
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T652187065"] = "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."
|
||||
|
||||
-- Policy description
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T748735777"] = "Policy description"
|
||||
|
||||
-- Would you like to protect this policy so that you cannot accidentally edit or delete it?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T80597472"] = "Would you like to protect this policy so that you cannot accidentally edit or delete it?"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- Yes, hide the policy definition
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T940701960"] = "Yes, hide the policy definition"
|
||||
|
||||
-- Please wait while we load the content of your file. Depending on the file type and size, this may take a moment.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T1205126512"] = "Please wait while we load the content of your file. Depending on the file type and size, this may take a moment."
|
||||
|
||||
@ -7879,6 +8005,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T23198
|
||||
-- This chat template preselects data sources which exist on this machine only: {0}. They cannot be rolled out, so a chat started with this template elsewhere begins without them. Do you want to export the template anyway?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T2563631533"] = "This chat template preselects data sources which exist on this machine only: {0}. They cannot be rolled out, so a chat started with this template elsewhere begins without them. Do you want to export the template anyway?"
|
||||
|
||||
-- Import Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T263213584"] = "Import Chat Template"
|
||||
|
||||
-- Chat Template Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T275026390"] = "Chat Template Name"
|
||||
|
||||
@ -8200,6 +8329,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGMYTASKS::T711745239"
|
||||
-- Edit Profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1143111468"] = "Edit Profile"
|
||||
|
||||
-- Import Profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1215374025"] = "Import Profile"
|
||||
|
||||
-- No profiles configured yet.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1433534732"] = "No profiles configured yet."
|
||||
|
||||
@ -11590,6 +11722,114 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTSECUR
|
||||
-- The plugin code changed after the last security audit. The stored result no longer matches the current code, so this assistant plugin must be audited again before it may be enabled or used.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTSECURITYRESOLVER::T995107927"] = "The plugin code changed after the last security audit. The stored result no longer matches the current code, so this assistant plugin must be audited again before it may be enabled or used."
|
||||
|
||||
-- The embedded credential could not be decrypted on this device. Enter your own credential before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T1017166692"] = "The embedded credential could not be decrypted on this device. Enter your own credential before saving."
|
||||
|
||||
-- tool {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T1561005241"] = "tool {0}"
|
||||
|
||||
-- provider {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T1867642302"] = "provider {0}"
|
||||
|
||||
-- The '{0}' field must be a string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T224834942"] = "The '{0}' field must be a string."
|
||||
|
||||
-- The exported item has an invalid ID.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T2640740755"] = "The exported item has an invalid ID."
|
||||
|
||||
-- The '{0}' field has an unknown value.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T2930445640"] = "The '{0}' field has an unknown value."
|
||||
|
||||
-- data source {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3277968944"] = "data source {0}"
|
||||
|
||||
-- The '{0}' field must be a whole number.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3419687389"] = "The '{0}' field must be a whole number."
|
||||
|
||||
-- profile {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3445783292"] = "profile {0}"
|
||||
|
||||
-- The '{0}' field must be true or false.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3600915840"] = "The '{0}' field must be true or false."
|
||||
|
||||
-- Unavailable references: {0}. Review the selections before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3637224236"] = "Unavailable references: {0}. Review the selections before saving."
|
||||
|
||||
-- The '{0}' field must contain an ENC:v1 credential.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T4261018324"] = "The '{0}' field must contain an ENC:v1 credential."
|
||||
|
||||
-- The '{0}' field must be a table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T432554775"] = "The '{0}' field must be a table."
|
||||
|
||||
-- The '{0}' field contains an invalid entry.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T948874113"] = "The '{0}' field contains an invalid entry."
|
||||
|
||||
-- The 'Port' field must be between 1 and 65535.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T1817689659"] = "The 'Port' field must be between 1 and 65535."
|
||||
|
||||
-- The 'MaxMatches' field is outside the allowed range.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T2009159518"] = "The 'MaxMatches' field is outside the allowed range."
|
||||
|
||||
-- This configuration section cannot be imported here.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T2657742500"] = "This configuration section cannot be imported here."
|
||||
|
||||
-- An example conversation message is empty.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T3131294453"] = "An example conversation message is empty."
|
||||
|
||||
-- An example conversation entry is not a table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T3912378391"] = "An example conversation entry is not a table."
|
||||
|
||||
-- Kerberos data sources cannot be imported from configuration snippets.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T4019138476"] = "Kerberos data sources cannot be imported from configuration snippets."
|
||||
|
||||
-- The chat template fields are malformed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T539301384"] = "The chat template fields are malformed."
|
||||
|
||||
-- This data source is not an ERI v1 data source.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T724971220"] = "This data source is not an ERI v1 data source."
|
||||
|
||||
-- Only literal values are allowed at character {0}; executable Lua is not accepted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1200702179"] = "Only literal values are allowed at character {0}; executable Lua is not accepted."
|
||||
|
||||
-- Expected a quoted string at character {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1235695745"] = "Expected a quoted string at character {0}."
|
||||
|
||||
-- Unterminated long string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1583706986"] = "Unterminated long string."
|
||||
|
||||
-- The configuration section names do not match.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1700453124"] = "The configuration section names do not match."
|
||||
|
||||
-- The snippet contains too many nested tables.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1872841947"] = "The snippet contains too many nested tables."
|
||||
|
||||
-- Expected '{0}' at character {1}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T2092509274"] = "Expected '{0}' at character {1}."
|
||||
|
||||
-- Unsupported string escape sequence: {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T2686740263"] = "Unsupported string escape sequence: {0}"
|
||||
|
||||
-- Paste one exported configuration snippet (up to 1 MB).
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T2743278967"] = "Paste one exported configuration snippet (up to 1 MB)."
|
||||
|
||||
-- Expected a comma or closing brace at character {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T3466555352"] = "Expected a comma or closing brace at character {0}."
|
||||
|
||||
-- This is a {0} snippet. Paste a {1} snippet here.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T3577683445"] = "This is a {0} snippet. Paste a {1} snippet here."
|
||||
|
||||
-- Unterminated quoted string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T3882373828"] = "Unterminated quoted string."
|
||||
|
||||
-- A quoted string contains an unescaped newline.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T4168367785"] = "A quoted string contains an unescaped newline."
|
||||
|
||||
-- The field '{0}' occurs more than once.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T560836334"] = "The field '{0}' occurs more than once."
|
||||
|
||||
-- The snippet must contain exactly one table assignment and no executable code.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T873986754"] = "The snippet must contain exactly one table assignment and no executable code."
|
||||
|
||||
-- The table AUTHORS does not exist or is using an invalid syntax.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "The table AUTHORS does not exist or is using an invalid syntax."
|
||||
|
||||
|
||||
@ -105,14 +105,17 @@
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<MudMenu EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@T("Add Data Source")" Color="Color.Primary" Variant="Variant.Filled" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" Class="mt-3 mb-6">
|
||||
<MudMenuItem OnClick="@(() => this.AddDataSource(DataSourceType.ERI_V1))">
|
||||
@T("External Data (ERI-Server v1)")
|
||||
</MudMenuItem>
|
||||
<MudMenuItem OnClick="@(() => this.AddDataSource(DataSourceType.LOCAL_DIRECTORY))">
|
||||
@T("Local Directory")
|
||||
</MudMenuItem>
|
||||
<MudMenuItem OnClick="@(() => this.AddDataSource(DataSourceType.LOCAL_FILE))">
|
||||
@T("Local File")
|
||||
</MudMenuItem>
|
||||
</MudMenu>
|
||||
<MudStack Row="true" Wrap="Wrap.Wrap" Class="mt-3 mb-6">
|
||||
<MudMenu EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@T("Add Data Source")" Color="Color.Primary" Variant="Variant.Filled" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft">
|
||||
<MudMenuItem OnClick="@(() => this.AddDataSource(DataSourceType.ERI_V1))">
|
||||
@T("External Data (ERI-Server v1)")
|
||||
</MudMenuItem>
|
||||
<MudMenuItem OnClick="@(() => this.AddDataSource(DataSourceType.LOCAL_DIRECTORY))">
|
||||
@T("Local Directory")
|
||||
</MudMenuItem>
|
||||
<MudMenuItem OnClick="@(() => this.AddDataSource(DataSourceType.LOCAL_FILE))">
|
||||
@T("Local File")
|
||||
</MudMenuItem>
|
||||
</MudMenu>
|
||||
<LockableButton Text="@T("Import ERI v1 Data Source")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES"))" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportERIDataSource" ButtonVariant="Variant.Outlined" NoMargin="true" />
|
||||
</MudStack>
|
||||
|
||||
@ -6,6 +6,7 @@ using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Lua;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
@ -170,8 +171,20 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task AddDataSource(DataSourceType type)
|
||||
private async Task ImportERIDataSource()
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES"))
|
||||
return;
|
||||
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "DATA_SOURCES", T("Import ERI v1 Data Source"));
|
||||
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES"))
|
||||
await this.AddDataSource(DataSourceType.ERI_V1, table);
|
||||
}
|
||||
|
||||
private async Task AddDataSource(DataSourceType type, LuaTable? importedConfiguration = null)
|
||||
{
|
||||
if (importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES"))
|
||||
return;
|
||||
|
||||
IDataSource? addedDataSource = null;
|
||||
switch (type)
|
||||
{
|
||||
@ -214,10 +227,13 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
if (importedConfiguration is not null)
|
||||
eriDialogParameters.Add(x => x.ImportedConfiguration, importedConfiguration);
|
||||
|
||||
var eriDialogReference = await this.DialogService.ShowAsync<DataSourceERI_V1Dialog>(T("Add ERI v1 Data Source"), eriDialogParameters, DialogOptions.FULLSCREEN);
|
||||
var eriDialogResult = await eriDialogReference.Result;
|
||||
if (eriDialogResult is null || eriDialogResult.Canceled)
|
||||
if (eriDialogResult is null || eriDialogResult.Canceled ||
|
||||
(importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("DATA_SOURCES")))
|
||||
return;
|
||||
|
||||
var eriDataSource = (DataSourceERI_V1)eriDialogResult.Data!;
|
||||
|
||||
@ -93,6 +93,9 @@
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<LockableButton Text="@T("Add Embedding")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddEmbeddingProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddEmbeddingProvider" Class="mt-3" />
|
||||
<MudStack Row="true" Wrap="Wrap.Wrap" Class="mt-3 mb-6">
|
||||
<LockableButton Text="@T("Add Embedding")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddEmbeddingProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddEmbeddingProvider" Class="flex-grow-0" NoMargin="true" />
|
||||
<LockableButton Text="@T("Import Embedding Provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("EMBEDDING_PROVIDERS"))" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportEmbeddingProvider" ButtonVariant="Variant.Outlined" Class="flex-grow-0" NoMargin="true" />
|
||||
</MudStack>
|
||||
</ExpansionPanel>
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelEmbeddings : SettingsPanelProviderBase
|
||||
@ -53,16 +55,33 @@ public partial class SettingsPanelEmbeddings : SettingsPanelProviderBase
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task AddEmbeddingProvider()
|
||||
private Task AddEmbeddingProvider() => this.AddEmbeddingProvider(null);
|
||||
|
||||
private async Task ImportEmbeddingProvider()
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("EMBEDDING_PROVIDERS"))
|
||||
return;
|
||||
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "EMBEDDING_PROVIDERS", T("Import Embedding Provider"));
|
||||
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("EMBEDDING_PROVIDERS"))
|
||||
await this.AddEmbeddingProvider(table);
|
||||
}
|
||||
|
||||
private async Task AddEmbeddingProvider(LuaTable? importedConfiguration)
|
||||
{
|
||||
if (importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("EMBEDDING_PROVIDERS"))
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<EmbeddingProviderDialog>
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
if (importedConfiguration is not null)
|
||||
dialogParameters.Add(x => x.ImportedConfiguration, importedConfiguration);
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<EmbeddingProviderDialog>(T("Add Embedding Provider"), 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("EMBEDDING_PROVIDERS")))
|
||||
return;
|
||||
|
||||
var addedEmbedding = (EmbeddingProvider)dialogResult.Data!;
|
||||
|
||||
@ -78,5 +78,8 @@
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<LockableButton Text="@T("Add Provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddLLMProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddLLMProvider" Class="mt-3" />
|
||||
<MudStack Row="true" Wrap="Wrap.Wrap" Class="mt-3 mb-6">
|
||||
<LockableButton Text="@T("Add Provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddLLMProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddLLMProvider" Class="flex-grow-0" NoMargin="true" />
|
||||
<LockableButton Text="@T("Import LLM Provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("LLM_PROVIDERS"))" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportProvider" ButtonVariant="Variant.Outlined" Class="flex-grow-0" NoMargin="true" />
|
||||
</MudStack>
|
||||
</ExpansionPanel>
|
||||
|
||||
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelProviders : SettingsPanelProviderBase
|
||||
@ -39,17 +41,34 @@ public partial class SettingsPanelProviders : SettingsPanelProviderBase
|
||||
|
||||
#endregion
|
||||
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed", Justification = "Managing the provider list is the purpose of this settings panel. Reading providers goes through the settings manager, but adding, editing, and removing them stays here on purpose.")]
|
||||
private async Task AddLLMProvider()
|
||||
private Task AddLLMProvider() => this.AddLLMProvider(null);
|
||||
|
||||
private async Task ImportProvider()
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("LLM_PROVIDERS"))
|
||||
return;
|
||||
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "LLM_PROVIDERS", T("Import LLM Provider"));
|
||||
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("LLM_PROVIDERS"))
|
||||
await this.AddLLMProvider(table);
|
||||
}
|
||||
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed", Justification = "Managing the provider list is the purpose of this settings panel. Reading providers goes through the settings manager, but adding, editing, and removing them stays here on purpose.")]
|
||||
private async Task AddLLMProvider(LuaTable? importedConfiguration)
|
||||
{
|
||||
if (importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("LLM_PROVIDERS"))
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ProviderDialog>
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
if (importedConfiguration is not null)
|
||||
dialogParameters.Add(x => x.ImportedConfiguration, importedConfiguration);
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>(T("Add LLM Provider"), 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("LLM_PROVIDERS")))
|
||||
return;
|
||||
|
||||
var addedProvider = (AIStudio.Settings.Provider)dialogResult.Data!;
|
||||
|
||||
@ -83,6 +83,9 @@
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<LockableButton Text="@T("Add transcription provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddTranscriptionProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddTranscriptionProvider" Class="mt-3" />
|
||||
<MudStack Row="true" Wrap="Wrap.Wrap" Class="mt-3 mb-6">
|
||||
<LockableButton Text="@T("Add transcription provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddTranscriptionProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddTranscriptionProvider" Class="flex-grow-0" NoMargin="true" />
|
||||
<LockableButton Text="@T("Import Transcription Provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("TRANSCRIPTION_PROVIDERS"))" Icon="@Icons.Material.Filled.FileDownload" OnClickAsync="@this.ImportTranscriptionProvider" ButtonVariant="Variant.Outlined" Class="flex-grow-0" NoMargin="true" />
|
||||
</MudStack>
|
||||
</ExpansionPanel>
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelTranscription : SettingsPanelProviderBase
|
||||
@ -47,16 +49,33 @@ public partial class SettingsPanelTranscription : SettingsPanelProviderBase
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task AddTranscriptionProvider()
|
||||
private Task AddTranscriptionProvider() => this.AddTranscriptionProvider(null);
|
||||
|
||||
private async Task ImportTranscriptionProvider()
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("TRANSCRIPTION_PROVIDERS"))
|
||||
return;
|
||||
var table = await ConfigurationSnippetImportDialog.ShowAsync(this.DialogService, "TRANSCRIPTION_PROVIDERS", T("Import Transcription Provider"));
|
||||
if (table is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("TRANSCRIPTION_PROVIDERS"))
|
||||
await this.AddTranscriptionProvider(table);
|
||||
}
|
||||
|
||||
private async Task AddTranscriptionProvider(LuaTable? importedConfiguration)
|
||||
{
|
||||
if (importedConfiguration is not null && !this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("TRANSCRIPTION_PROVIDERS"))
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<TranscriptionProviderDialog>
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
if (importedConfiguration is not null)
|
||||
dialogParameters.Add(x => x.ImportedConfiguration, importedConfiguration);
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<TranscriptionProviderDialog>(T("Add Transcription Provider"), 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("TRANSCRIPTION_PROVIDERS")))
|
||||
return;
|
||||
|
||||
var addedTranscription = (TranscriptionProvider)dialogResult.Data!;
|
||||
|
||||
@ -4,6 +4,13 @@
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
@if (!this.IsEditing && !this.IsReadOnly)
|
||||
{
|
||||
@if (!string.IsNullOrWhiteSpace(this.importReferenceIssue))
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Class="mb-2">@this.importReferenceIssue</MudAlert>
|
||||
}
|
||||
}
|
||||
@* 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. *@
|
||||
@ -94,6 +101,33 @@
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.")
|
||||
</MudJustifiedText>
|
||||
@if (!this.IsEditing && !this.IsReadOnly)
|
||||
{
|
||||
@if (this.HasUnresolvedAttachments)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Filled" Icon="@Icons.Material.Filled.WarningAmber" Class="mb-3">
|
||||
@T("Some attachments in this chat template are missing. Relink or remove them before adding the template.")
|
||||
</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;
|
||||
var attachment = this.attachmentsToRelink[index];
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-2">
|
||||
<MudTextField T="string" Value="@attachment.ReplacementPath"
|
||||
ValueChanged="@(value => this.UpdateRelinkPath(currentIndex, value))"
|
||||
Label="@string.Format(T("Relink attachment: {0}"), attachment.OriginalPath)"
|
||||
HelperText="@T("Enter an absolute path to an existing local file, or remove this attachment.")" Variant="Variant.Outlined" Immediate="@true" />
|
||||
<MudTooltip Text="@T("Remove this attachment")">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@(() => this.RemoveAttachmentToRelink(currentIndex))" />
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
}
|
||||
<AttachDocuments
|
||||
Name="ChatTemplateFileAttachments"
|
||||
@bind-DocumentPaths="@this.fileAttachments"
|
||||
@ -252,7 +286,7 @@
|
||||
|
||||
@if (!this.isInlineEditOnGoing)
|
||||
{
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary" Disabled="@this.HasUnresolvedAttachments">
|
||||
@if (this.IsEditing)
|
||||
{
|
||||
@T("Update")
|
||||
|
||||
@ -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,10 @@ 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 bool HasUnresolvedAttachments => this.attachmentsToRelink.Any(attachment => !ConfigurationImportFields.IsExistingLocalFile(attachment.ReplacementPath));
|
||||
private string importReferenceIssue = string.Empty;
|
||||
private bool preselectTools;
|
||||
private HashSet<string> selectedToolIds = new(StringComparer.Ordinal);
|
||||
private bool preselectDataSources;
|
||||
@ -143,6 +157,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
this.DataName = this.ExistingChatThread.Name;
|
||||
}
|
||||
|
||||
if (this.ImportedConfiguration is not null && this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
|
||||
await this.ImportConfiguration(this.ImportedConfiguration);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@ -153,6 +170,10 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
if(!this.IsEditing && firstRender)
|
||||
this.form.ResetValidation();
|
||||
|
||||
if (firstRender && this.ImportedConfiguration is not null &&
|
||||
!this.SettingsManager.ConfigurationData.App.CanImportConfigurationSnippet("CHAT_TEMPLATES"))
|
||||
this.MudDialog.Cancel();
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
@ -176,6 +197,62 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
private async Task ImportConfiguration(LuaTable table)
|
||||
{
|
||||
ConfigurationSnippetImportValidation.Validate("CHAT_TEMPLATES", table);
|
||||
if (!ChatTemplate.TryParseChatTemplateTable(0, table, Guid.Empty, string.Empty, out var parsed) || parsed is not ChatTemplate template)
|
||||
throw new FormatException(T("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();
|
||||
}
|
||||
|
||||
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(ConfigurationImportFields.MissingDataSourceReference)
|
||||
.Concat(missingTools.Select(ConfigurationImportFields.MissingToolReference)).ToList();
|
||||
return ConfigurationImportFields.UnavailableReferencesIssue(missing);
|
||||
}
|
||||
|
||||
private void UpdateRelinkPath(int index, string? path)
|
||||
{
|
||||
this.attachmentsToRelink[index] = (this.attachmentsToRelink[index].OriginalPath, path ?? string.Empty);
|
||||
if (!this.HasUnresolvedAttachments)
|
||||
this.relinkIssue = string.Empty;
|
||||
}
|
||||
|
||||
private void RemoveAttachmentToRelink(int index)
|
||||
{
|
||||
this.attachmentsToRelink.RemoveAt(index);
|
||||
if (!this.HasUnresolvedAttachments)
|
||||
this.relinkIssue = string.Empty;
|
||||
}
|
||||
|
||||
private void SetSelectedToolIds(HashSet<string> toolIds) => this.selectedToolIds = toolIds;
|
||||
|
||||
private void RemoveMessage(ContentBlock item)
|
||||
@ -271,9 +348,24 @@ 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;
|
||||
|
||||
// Only check the relinked attachments here. They are added right before closing, so that a
|
||||
// failed save does not leave a path behind which the user changes afterward:
|
||||
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 or remove it before saving."), originalPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.form.Validate();
|
||||
|
||||
// When the data is not valid, we don't store it:
|
||||
@ -284,6 +376,10 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
if (this.isInlineEditOnGoing)
|
||||
return;
|
||||
|
||||
foreach (var (_, replacementPath) in this.attachmentsToRelink)
|
||||
this.fileAttachments.Add(FileAttachment.FromPath(replacementPath));
|
||||
this.attachmentsToRelink.Clear();
|
||||
|
||||
// Use the data model to store the chat template.
|
||||
// We just return this data to the parent component:
|
||||
var addedChatTemplateSettings = this.CreateChatTemplateSettings();
|
||||
|
||||
@ -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
|
||||
@ -102,7 +113,7 @@
|
||||
|
||||
@if (this.availableRetrievalProcesses.Count > 0)
|
||||
{
|
||||
<MudSelect @bind-Value="@this.dataSelectedRetrievalProcess" Text="@this.dataSelectedRetrievalProcess.Name" Label="@T("Select one retrieval process")" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.dataSourceValidation.ValidateRetrievalProcess">
|
||||
<MudSelect @bind-Value="@this.dataSelectedRetrievalProcess" @bind-Value:after="@this.ClearImportRetrievalIssue" Text="@this.dataSelectedRetrievalProcess.Name" Label="@T("Select one retrieval process")" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.dataSourceValidation.ValidateRetrievalProcess">
|
||||
@foreach (var retrievalProcess in this.availableRetrievalProcesses)
|
||||
{
|
||||
<MudSelectItem Value="@retrievalProcess">
|
||||
|
||||
@ -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,9 @@ 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 importedRetrievalId = string.Empty;
|
||||
private string dataEditingPreviousInstanceName = string.Empty;
|
||||
private List<AuthMethod> availableAuthMethods = [];
|
||||
private DataSourceSecurity dataSecurityPolicy;
|
||||
@ -144,6 +153,15 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
|
||||
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);
|
||||
}
|
||||
|
||||
@ -177,6 +195,48 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
|
||||
};
|
||||
}
|
||||
|
||||
private Task ImportConfiguration(LuaTable table)
|
||||
{
|
||||
ConfigurationSnippetImportValidation.Validate("DATA_SOURCES", table);
|
||||
var name = ConfigurationImportFields.String(table, "Name");
|
||||
var hostname = ConfigurationImportFields.String(table, "Hostname");
|
||||
var port = ConfigurationImportFields.Int(table, "Port");
|
||||
var authMethod = ConfigurationImportFields.Enum<AuthMethod>(table, "AuthMethod");
|
||||
var securityPolicy = ConfigurationImportFields.Enum<DataSourceSecurity>(table, "SecurityPolicy");
|
||||
var retrievalId = ConfigurationImportFields.String(table, "SelectedRetrievalId");
|
||||
var maxMatches = ConfigurationImportFields.Int(table, "MaxMatches", 10);
|
||||
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.importedRetrievalId = retrievalId;
|
||||
this.connectionTested = false;
|
||||
this.connectionSuccessfulTested = false;
|
||||
this.form.ResetValidation();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ClearImportRetrievalIssue() => this.importRetrievalIssue = string.Empty;
|
||||
|
||||
private bool IsConnectionEncrypted() => this.dataHostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
private bool IsConnectionPossible()
|
||||
@ -251,6 +311,24 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
|
||||
}
|
||||
|
||||
this.availableRetrievalProcesses = retrievalInfoRequest.Data ?? [];
|
||||
// Only the first successful test after an import resolves the imported retrieval ID;
|
||||
// afterward, the selection belongs to the user:
|
||||
if (!string.IsNullOrWhiteSpace(this.importedRetrievalId))
|
||||
{
|
||||
var importedRetrieval = this.availableRetrievalProcesses.FirstOrDefault(item => item.Id == this.importedRetrievalId);
|
||||
if (importedRetrieval != default)
|
||||
{
|
||||
this.dataSelectedRetrievalProcess = importedRetrieval;
|
||||
this.importRetrievalIssue = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.importRetrievalIssue = string.Format(T("The imported retrieval process '{0}' is unavailable. Select another process before saving."), this.importedRetrievalId);
|
||||
this.dataSelectedRetrievalProcess = default;
|
||||
}
|
||||
|
||||
this.importedRetrievalId = string.Empty;
|
||||
}
|
||||
|
||||
this.connectionTested = true;
|
||||
this.connectionSuccessfulTested = true;
|
||||
@ -304,6 +382,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();
|
||||
|
||||
@ -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?")" LabelOn="@T("Yes, hide the policy definition")" LabelOff="@T("No, show the policy definition")" />
|
||||
<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?")" LabelOn="@T("Yes, protect this policy")" LabelOff="@T("No, the policy can be edited")" />
|
||||
<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(ConfigurationImportFields.MissingProviderReference(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(ConfigurationImportFields.MissingProfileReference(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(ConfigurationImportFields.MissingToolReference));
|
||||
this.referenceIssue = ConfigurationImportFields.UnavailableReferencesIssue(missing);
|
||||
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;
|
||||
@ -191,6 +198,40 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@ -248,6 +289,15 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
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();
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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()
|
||||
@ -298,6 +340,15 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
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,9 +81,12 @@
|
||||
</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">
|
||||
|
||||
@ -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!;
|
||||
|
||||
@ -64,9 +64,12 @@
|
||||
</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">
|
||||
|
||||
@ -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!;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -154,6 +161,33 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@ -206,6 +240,15 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
|
||||
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;
|
||||
|
||||
|
||||
@ -395,6 +395,20 @@ CONFIG["SETTINGS"] = {}
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddEmbeddingProvider"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddTranscriptionProvider"] = false
|
||||
|
||||
-- Control whether users may paste exported configuration snippets to create local items.
|
||||
-- The master setting and the matching item setting must both be true. Provider imports
|
||||
-- also require DataApp.AllowUserToAddProvider and the matching provider Add setting.
|
||||
-- Blocked Import buttons remain visible with a lock. These settings do not affect
|
||||
-- plugin archive imports or exports.
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportConfigurationSnippets"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportProfile"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportLLMProvider"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportEmbeddingProvider"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportTranscriptionProvider"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportChatTemplate"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportERIDataSource"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportDocumentAnalysisPolicy"] = false
|
||||
|
||||
-- Configure the user permission to import plugin archives from disk.
|
||||
-- When set to false, the import button on the plugins page stays visible but is disabled.
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToImportPlugins"] = false
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1218,6 +1218,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTA
|
||||
-- A policy with this name already exists. Please choose a different name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3584374593"] = "A policy with this name already exists. Please choose a different name."
|
||||
|
||||
-- Import document analysis policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3800115485"] = "Import document analysis policy"
|
||||
|
||||
-- Load analysis rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3813558135"] = "Load analysis rules from document"
|
||||
|
||||
@ -3927,6 +3930,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T750361472"] = "Can
|
||||
-- External Data (ERI-Server v1)
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T774473996"] = "External Data (ERI-Server v1)"
|
||||
|
||||
-- Import ERI v1 Data Source
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T777621786"] = "Import ERI v1 Data Source"
|
||||
|
||||
-- Cannot export this ERI data source because no authentication secret is configured. The issue was: {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCEMANAGEMENT::T782820095"] = "Cannot export this ERI data source because no authentication secret is configured. The issue was: {0}"
|
||||
|
||||
@ -4887,6 +4893,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T21748
|
||||
-- Model
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T2189814010"] = "Model"
|
||||
|
||||
-- Import Embedding Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T229678442"] = "Import Embedding Provider"
|
||||
|
||||
-- Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and 'joyful' would be more alike than those for 'happy' and 'sad'.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T2419962612"] = "Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and 'joyful' would be more alike than those for 'happy' and 'sad'."
|
||||
|
||||
@ -4962,6 +4971,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T146957
|
||||
-- Uses the provider-configured model
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1760715963"] = "Uses the provider-configured model"
|
||||
|
||||
-- Import LLM Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T178720520"] = "Import LLM Provider"
|
||||
|
||||
-- Add Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1806589097"] = "Add Provider"
|
||||
|
||||
@ -5100,6 +5112,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T58
|
||||
-- This transcription provider is trusted by your organization for data source security checks.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T601264181"] = "This transcription provider is trusted by your organization for data source security checks."
|
||||
|
||||
-- Import Transcription Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T682006405"] = "Import Transcription Provider"
|
||||
|
||||
-- This transcription provider is managed by your organization. You can set your own API key.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T690752279"] = "This transcription provider is managed by your organization. You can set your own API key."
|
||||
|
||||
@ -5748,6 +5763,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T204496403"] = "The chat
|
||||
-- Profile Usage
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profile Usage"
|
||||
|
||||
-- Relink attachment: {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2234793579"] = "Relink attachment: {0}"
|
||||
|
||||
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs."
|
||||
|
||||
@ -5775,6 +5793,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579080722"] = "No, pro
|
||||
-- You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579208700"] = "You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with."
|
||||
|
||||
-- Enter an absolute path to an existing local file, or remove this attachment.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2603688456"] = "Enter an absolute path to an existing local file, or remove this attachment."
|
||||
|
||||
-- Predefined User Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2607897066"] = "Predefined User Input"
|
||||
|
||||
@ -5814,6 +5835,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3268470871"] = "No, cha
|
||||
-- A chat template may decide which tools a chat starts with. Without such a decision, those chats start with the tools you chose as your default in the chat options. Deciding and then picking nothing is a different statement: such chats start with no tool at all, no matter what your default says.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3329415439"] = "A chat template may decide which tools a chat starts with. Without such a decision, those chats start with the tools you chose as your default in the chat options. Deciding and then picking nothing is a different statement: such chats start with no tool at all, no matter what your default says."
|
||||
|
||||
-- Some attachments in this chat template are missing. Relink or remove them before adding the template.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3342669429"] = "Some attachments in this chat template are missing. Relink or remove them before adding the template."
|
||||
|
||||
-- Add a message
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Add a message"
|
||||
|
||||
@ -5823,6 +5847,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3448155331"] = "Close"
|
||||
-- Unsupported content type
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Unsupported content type"
|
||||
|
||||
-- Relink the missing attachment '{0}' to an existing local file or remove it before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3582611137"] = "Relink the missing attachment '{0}' to an existing local file or remove it before saving."
|
||||
|
||||
-- What system prompt do you want to use?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3652587353"] = "What system prompt do you want to use?"
|
||||
|
||||
@ -5862,6 +5889,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4199560726"] = "Create
|
||||
-- Enter a message
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T446374405"] = "Enter a message"
|
||||
|
||||
-- The chat template fields are malformed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T539301384"] = "The chat template fields are malformed."
|
||||
|
||||
-- Data Sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T558345131"] = "Data Sources"
|
||||
|
||||
@ -5871,6 +5901,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T628396066"] = "System P
|
||||
-- A chat started with this template begins with these data sources and options. All of it stays changeable in the chat itself.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T650711085"] = "A chat started with this template begins with these data sources and options. All of it stays changeable in the chat itself."
|
||||
|
||||
-- Remove this attachment
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T70791272"] = "Remove this attachment"
|
||||
|
||||
-- The selection stays changeable in the chat, and every tool still has to meet the confidence requirements of the provider in use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T722788866"] = "The selection stays changeable in the chat, and every tool still has to meet the confidence requirements of the provider in use."
|
||||
|
||||
@ -5961,12 +5994,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T767586087"
|
||||
-- {0} embedding provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T818101181"] = "{0} embedding provider"
|
||||
|
||||
-- Configuration snippet
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T3867536704"] = "Configuration snippet"
|
||||
|
||||
-- Import is locked by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T4101825749"] = "Import is locked by your organization."
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T4214407984"] = "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."
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONSNIPPETIMPORTDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- No
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIRMDIALOG::T1642511898"] = "No"
|
||||
|
||||
-- Yes
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIRMDIALOG::T3013883440"] = "Yes"
|
||||
|
||||
-- The imported retrieval process '{0}' is unavailable. Select another process before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DATASOURCEERI_V1DIALOG::T1759490982"] = "The imported retrieval process '{0}' is unavailable. Select another process before saving."
|
||||
|
||||
-- How many matches do you want at most per query?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DATASOURCEERI_V1DIALOG::T1827669611"] = "How many matches do you want at most per query?"
|
||||
|
||||
@ -6540,6 +6588,84 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DIRECTCHATLAUNCHERSETTINGSDIALOG::T854110894
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DIRECTCHATLAUNCHERSETTINGSDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- No, the policy can be edited
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1286595725"] = "No, the policy can be edited"
|
||||
|
||||
-- Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1291179736"] = "Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents."
|
||||
|
||||
-- Yes, protect this policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1762380857"] = "Yes, protect this policy"
|
||||
|
||||
-- Hide the policy definition when distributed via configuration plugin?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T1875622568"] = "Hide the policy definition when distributed via configuration plugin?"
|
||||
|
||||
-- No profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2028602035"] = "No profile"
|
||||
|
||||
-- Load output rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2168201568"] = "Load output rules from document"
|
||||
|
||||
-- Preselect a profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2322771068"] = "Preselect a profile"
|
||||
|
||||
-- The name of your policy must be between 6 and 60 characters long.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2435013256"] = "The name of your policy must be between 6 and 60 characters long."
|
||||
|
||||
-- Preselect a provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2440815970"] = "Preselect a provider"
|
||||
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- Policy name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T2879019438"] = "Policy name"
|
||||
|
||||
-- Analysis rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3108719748"] = "Analysis rules"
|
||||
|
||||
-- Tools this policy permits
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T31356122"] = "Tools this policy permits"
|
||||
|
||||
-- No, show the policy definition
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3166091879"] = "No, show the policy definition"
|
||||
|
||||
-- The description of your policy must be between 32 and 512 characters long.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3285636934"] = "The description of your policy must be between 32 and 512 characters long."
|
||||
|
||||
-- A policy with this name already exists. Please choose a different name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3584374593"] = "A policy with this name already exists. Please choose a different name."
|
||||
|
||||
-- Use app default profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3587225583"] = "Use app default profile"
|
||||
|
||||
-- No provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3740605451"] = "No provider"
|
||||
|
||||
-- Load analysis rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3813558135"] = "Load analysis rules from document"
|
||||
|
||||
-- Output rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T3918193587"] = "Output rules"
|
||||
|
||||
-- Please provide a name for your policy. This name will be used to identify the policy in AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T4040507702"] = "Please provide a name for your policy. This name will be used to identify the policy in AI Studio."
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T652187065"] = "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."
|
||||
|
||||
-- Policy description
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T748735777"] = "Policy description"
|
||||
|
||||
-- Would you like to protect this policy so that you cannot accidentally edit or delete it?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T80597472"] = "Would you like to protect this policy so that you cannot accidentally edit or delete it?"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- Yes, hide the policy definition
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTANALYSISPOLICYDIALOG::T940701960"] = "Yes, hide the policy definition"
|
||||
|
||||
-- Please wait while we load the content of your file. Depending on the file type and size, this may take a moment.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T1205126512"] = "Please wait while we load the content of your file. Depending on the file type and size, this may take a moment."
|
||||
|
||||
@ -7881,6 +8007,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T23198
|
||||
-- This chat template preselects data sources which exist on this machine only: {0}. They cannot be rolled out, so a chat started with this template elsewhere begins without them. Do you want to export the template anyway?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T2563631533"] = "This chat template preselects data sources which exist on this machine only: {0}. They cannot be rolled out, so a chat started with this template elsewhere begins without them. Do you want to export the template anyway?"
|
||||
|
||||
-- Import Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T263213584"] = "Import Chat Template"
|
||||
|
||||
-- Chat Template Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T275026390"] = "Chat Template Name"
|
||||
|
||||
@ -8202,6 +8331,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGMYTASKS::T711745239"
|
||||
-- Edit Profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1143111468"] = "Edit Profile"
|
||||
|
||||
-- Import Profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1215374025"] = "Import Profile"
|
||||
|
||||
-- No profiles configured yet.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1433534732"] = "No profiles configured yet."
|
||||
|
||||
@ -11592,6 +11724,114 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTSECUR
|
||||
-- The plugin code changed after the last security audit. The stored result no longer matches the current code, so this assistant plugin must be audited again before it may be enabled or used.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTSECURITYRESOLVER::T995107927"] = "The plugin code changed after the last security audit. The stored result no longer matches the current code, so this assistant plugin must be audited again before it may be enabled or used."
|
||||
|
||||
-- The embedded credential could not be decrypted on this device. Enter your own credential before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T1017166692"] = "The embedded credential could not be decrypted on this device. Enter your own credential before saving."
|
||||
|
||||
-- tool {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T1561005241"] = "tool {0}"
|
||||
|
||||
-- provider {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T1867642302"] = "provider {0}"
|
||||
|
||||
-- The '{0}' field must be a string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T224834942"] = "The '{0}' field must be a string."
|
||||
|
||||
-- The exported item has an invalid ID.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T2640740755"] = "The exported item has an invalid ID."
|
||||
|
||||
-- The '{0}' field has an unknown value.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T2930445640"] = "The '{0}' field has an unknown value."
|
||||
|
||||
-- data source {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3277968944"] = "data source {0}"
|
||||
|
||||
-- The '{0}' field must be a whole number.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3419687389"] = "The '{0}' field must be a whole number."
|
||||
|
||||
-- profile {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3445783292"] = "profile {0}"
|
||||
|
||||
-- The '{0}' field must be true or false.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3600915840"] = "The '{0}' field must be true or false."
|
||||
|
||||
-- Unavailable references: {0}. Review the selections before saving.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T3637224236"] = "Unavailable references: {0}. Review the selections before saving."
|
||||
|
||||
-- The '{0}' field must contain an ENC:v1 credential.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T4261018324"] = "The '{0}' field must contain an ENC:v1 credential."
|
||||
|
||||
-- The '{0}' field must be a table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T432554775"] = "The '{0}' field must be a table."
|
||||
|
||||
-- The '{0}' field contains an invalid entry.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONIMPORTFIELDS::T948874113"] = "The '{0}' field contains an invalid entry."
|
||||
|
||||
-- The 'Port' field must be between 1 and 65535.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T1817689659"] = "The 'Port' field must be between 1 and 65535."
|
||||
|
||||
-- The 'MaxMatches' field is outside the allowed range.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T2009159518"] = "The 'MaxMatches' field is outside the allowed range."
|
||||
|
||||
-- This configuration section cannot be imported here.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T2657742500"] = "This configuration section cannot be imported here."
|
||||
|
||||
-- An example conversation message is empty.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T3131294453"] = "An example conversation message is empty."
|
||||
|
||||
-- An example conversation entry is not a table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T3912378391"] = "An example conversation entry is not a table."
|
||||
|
||||
-- Kerberos data sources cannot be imported from configuration snippets.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T4019138476"] = "Kerberos data sources cannot be imported from configuration snippets."
|
||||
|
||||
-- The chat template fields are malformed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T539301384"] = "The chat template fields are malformed."
|
||||
|
||||
-- This data source is not an ERI v1 data source.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETIMPORTVALIDATION::T724971220"] = "This data source is not an ERI v1 data source."
|
||||
|
||||
-- Only literal values are allowed at character {0}; executable Lua is not accepted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1200702179"] = "Only literal values are allowed at character {0}; executable Lua is not accepted."
|
||||
|
||||
-- Expected a quoted string at character {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1235695745"] = "Expected a quoted string at character {0}."
|
||||
|
||||
-- Unterminated long string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1583706986"] = "Unterminated long string."
|
||||
|
||||
-- The configuration section names do not match.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1700453124"] = "The configuration section names do not match."
|
||||
|
||||
-- The snippet contains too many nested tables.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T1872841947"] = "The snippet contains too many nested tables."
|
||||
|
||||
-- Expected '{0}' at character {1}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T2092509274"] = "Expected '{0}' at character {1}."
|
||||
|
||||
-- Unsupported string escape sequence: {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T2686740263"] = "Unsupported string escape sequence: {0}"
|
||||
|
||||
-- Paste one exported configuration snippet (up to 1 MB).
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T2743278967"] = "Paste one exported configuration snippet (up to 1 MB)."
|
||||
|
||||
-- Expected a comma or closing brace at character {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T3466555352"] = "Expected a comma or closing brace at character {0}."
|
||||
|
||||
-- This is a {0} snippet. Paste a {1} snippet here.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T3577683445"] = "This is a {0} snippet. Paste a {1} snippet here."
|
||||
|
||||
-- Unterminated quoted string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T3882373828"] = "Unterminated quoted string."
|
||||
|
||||
-- A quoted string contains an unescaped newline.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T4168367785"] = "A quoted string contains an unescaped newline."
|
||||
|
||||
-- The field '{0}' occurs more than once.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T560836334"] = "The field '{0}' occurs more than once."
|
||||
|
||||
-- The snippet must contain exactly one table assignment and no executable code.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::CONFIGURATIONSNIPPETPARSER::T873986754"] = "The snippet must contain exactly one table assignment and no executable code."
|
||||
|
||||
-- The table AUTHORS does not exist or is using an invalid syntax.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "The table AUTHORS does not exist or is using an invalid syntax."
|
||||
|
||||
|
||||
@ -181,6 +181,29 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
|
||||
/// </summary>
|
||||
public bool AllowUserToAddTranscriptionProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToAddTranscriptionProvider, true);
|
||||
|
||||
/// <summary>Should the user be allowed to import exported configuration snippets?</summary>
|
||||
public bool AllowUserToImportConfigurationSnippets { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportConfigurationSnippets, true);
|
||||
|
||||
public bool AllowUserToImportProfile { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportProfile, true);
|
||||
public bool AllowUserToImportLLMProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportLLMProvider, true);
|
||||
public bool AllowUserToImportEmbeddingProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportEmbeddingProvider, true);
|
||||
public bool AllowUserToImportTranscriptionProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportTranscriptionProvider, true);
|
||||
public bool AllowUserToImportChatTemplate { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportChatTemplate, true);
|
||||
public bool AllowUserToImportERIDataSource { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportERIDataSource, true);
|
||||
public bool AllowUserToImportDocumentAnalysisPolicy { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToImportDocumentAnalysisPolicy, true);
|
||||
|
||||
public bool CanImportConfigurationSnippet(string section) => this.AllowUserToImportConfigurationSnippets && (section switch
|
||||
{
|
||||
"PROFILES" => this.AllowUserToImportProfile,
|
||||
"LLM_PROVIDERS" => this.AllowUserToImportLLMProvider && this.AllowUserToAddProvider && this.AllowUserToAddLLMProvider,
|
||||
"EMBEDDING_PROVIDERS" => this.AllowUserToImportEmbeddingProvider && this.AllowUserToAddProvider && this.AllowUserToAddEmbeddingProvider,
|
||||
"TRANSCRIPTION_PROVIDERS" => this.AllowUserToImportTranscriptionProvider && this.AllowUserToAddProvider && this.AllowUserToAddTranscriptionProvider,
|
||||
"CHAT_TEMPLATES" => this.AllowUserToImportChatTemplate,
|
||||
"DATA_SOURCES" => this.AllowUserToImportERIDataSource,
|
||||
"DOCUMENT_ANALYSIS_POLICIES" => this.AllowUserToImportDocumentAnalysisPolicy,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Should the user be allowed to import plugin archives from disk?
|
||||
/// </summary>
|
||||
|
||||
@ -256,7 +256,7 @@ public sealed record EmbeddingProvider(
|
||||
["Name"] = "{{LuaTools.EscapeLuaString(this.Name)}}",
|
||||
["UsedLLMProvider"] = "{{this.UsedLLMProvider}}",
|
||||
|
||||
["TokenizerPath"] = "{{this.TokenizerPath}}",
|
||||
["TokenizerPath"] = "{{LuaTools.EscapeLuaString(this.TokenizerPath)}}",
|
||||
["TokenLimit"] = {{this.EffectiveTokenLimit}},
|
||||
["EmbeddingBatchSize"] = {{this.EffectiveEmbeddingBatchSize}},
|
||||
|
||||
|
||||
@ -279,7 +279,7 @@ public sealed record Provider(
|
||||
["InstanceName"] = "{{LuaTools.EscapeLuaString(this.InstanceName)}}",
|
||||
["UsedLLMProvider"] = "{{this.UsedLLMProvider}}",
|
||||
|
||||
["TokenizerPath"] = "{{this.TokenizerPath}}",
|
||||
["TokenizerPath"] = "{{LuaTools.EscapeLuaString(this.TokenizerPath)}}",
|
||||
|
||||
["Host"] = "{{this.Host}}",
|
||||
["Hostname"] = "{{LuaTools.EscapeLuaString(this.Hostname)}}",
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
using AIStudio.Tools;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class ConfigurationImportFields
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationImportFields).Namespace, nameof(ConfigurationImportFields));
|
||||
|
||||
public static void ValidateExportId(LuaTable table)
|
||||
{
|
||||
if (!Guid.TryParse(String(table, "Id"), out _))
|
||||
throw new FormatException(TB("The exported item has an invalid ID."));
|
||||
}
|
||||
|
||||
public static string String(LuaTable table, string name, bool required = true)
|
||||
{
|
||||
if (table.TryGetValue(name, out var value))
|
||||
{
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var text))
|
||||
return text;
|
||||
throw new FormatException(string.Format(TB("The '{0}' field must be a string."), name));
|
||||
}
|
||||
if (!required)
|
||||
return string.Empty;
|
||||
throw new FormatException(string.Format(TB("The '{0}' field must be a string."), name));
|
||||
}
|
||||
|
||||
public static LuaTable Table(LuaTable table, string name)
|
||||
{
|
||||
if (table.TryGetValue(name, out var value) && value.Type is LuaValueType.Table && value.TryRead<LuaTable>(out var nested))
|
||||
return nested;
|
||||
throw new FormatException(string.Format(TB("The '{0}' field must be a table."), name));
|
||||
}
|
||||
|
||||
public static T Enum<T>(LuaTable table, string name) where T : struct, Enum
|
||||
{
|
||||
var text = String(table, name);
|
||||
if (System.Enum.TryParse<T>(text, true, out var result) && System.Enum.IsDefined(result))
|
||||
return result;
|
||||
throw new FormatException(string.Format(TB("The '{0}' field has an unknown value."), name));
|
||||
}
|
||||
|
||||
public static bool Bool(LuaTable table, string name, bool fallback = false)
|
||||
{
|
||||
if (!table.TryGetValue(name, out var value))
|
||||
return fallback;
|
||||
if (value.Type is LuaValueType.Boolean && value.TryRead<bool>(out var result))
|
||||
return result;
|
||||
throw new FormatException(string.Format(TB("The '{0}' field must be true or false."), name));
|
||||
}
|
||||
|
||||
public static int Int(LuaTable table, string name, int fallback = 0)
|
||||
{
|
||||
if (!table.TryGetValue(name, out var value))
|
||||
return fallback;
|
||||
if (value.Type is LuaValueType.Number && value.TryRead<double>(out var number) && number >= int.MinValue && number <= int.MaxValue && number == Math.Truncate(number))
|
||||
return (int)number;
|
||||
throw new FormatException(string.Format(TB("The '{0}' field must be a whole number."), name));
|
||||
}
|
||||
|
||||
public static List<string> Strings(LuaTable table, string name)
|
||||
{
|
||||
var nested = Table(table, name);
|
||||
var result = new List<string>();
|
||||
for (var i = 1; i <= nested.ArrayLength; i++)
|
||||
{
|
||||
if (nested[i].Type is not LuaValueType.String || !nested[i].TryRead<string>(out var text) || string.IsNullOrWhiteSpace(text))
|
||||
throw new FormatException(string.Format(TB("The '{0}' field contains an invalid entry."), name));
|
||||
result.Add(text);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool IsExistingLocalFile(string path) => Path.IsPathFullyQualified(path) && File.Exists(path);
|
||||
|
||||
/// <summary>Lists the given references as a warning, or returns an empty text when nothing is missing.</summary>
|
||||
public static string UnavailableReferencesIssue(IReadOnlyCollection<string> missing) => missing.Count == 0
|
||||
? string.Empty
|
||||
: string.Format(TB("Unavailable references: {0}. Review the selections before saving."), string.Join(", ", missing));
|
||||
|
||||
public static string MissingProviderReference(string id) => string.Format(TB("provider {0}"), id);
|
||||
|
||||
public static string MissingProfileReference(string id) => string.Format(TB("profile {0}"), id);
|
||||
|
||||
public static string MissingToolReference(string id) => string.Format(TB("tool {0}"), id);
|
||||
|
||||
public static string MissingDataSourceReference(string id) => string.Format(TB("data source {0}"), id);
|
||||
|
||||
public static string Credential(LuaTable table, string name, out string issue, EnterpriseEncryption? decryptionService = null)
|
||||
{
|
||||
issue = string.Empty;
|
||||
var encrypted = String(table, name, required: false);
|
||||
if (string.IsNullOrEmpty(encrypted))
|
||||
return string.Empty;
|
||||
if (!EnterpriseEncryption.IsEncrypted(encrypted))
|
||||
throw new FormatException(string.Format(TB("The '{0}' field must contain an ENC:v1 credential."), name));
|
||||
var encryption = decryptionService ?? PluginFactory.EnterpriseEncryption;
|
||||
if (encryption?.IsAvailable == true && encryption.TryDecrypt(encrypted, out var decrypted))
|
||||
return decrypted;
|
||||
issue = TB("The embedded credential could not be decrypted on this device. Enter your own credential before saving.");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
using Lua;
|
||||
|
||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>Checks the fields used by the creation forms before leaving the paste dialog.</summary>
|
||||
public static class ConfigurationSnippetImportValidation
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationSnippetImportValidation).Namespace, nameof(ConfigurationSnippetImportValidation));
|
||||
|
||||
public static void Validate(string section, LuaTable table)
|
||||
{
|
||||
ConfigurationImportFields.ValidateExportId(table);
|
||||
switch (section)
|
||||
{
|
||||
case "PROFILES":
|
||||
ConfigurationImportFields.String(table, "Name");
|
||||
ConfigurationImportFields.String(table, "NeedToKnow");
|
||||
ConfigurationImportFields.String(table, "Actions");
|
||||
break;
|
||||
case "LLM_PROVIDERS":
|
||||
case "EMBEDDING_PROVIDERS":
|
||||
case "TRANSCRIPTION_PROVIDERS":
|
||||
ValidateProvider(section, table);
|
||||
break;
|
||||
case "CHAT_TEMPLATES":
|
||||
ValidateChatTemplate(table);
|
||||
break;
|
||||
case "DATA_SOURCES":
|
||||
ValidateERIDataSource(table);
|
||||
break;
|
||||
case "DOCUMENT_ANALYSIS_POLICIES":
|
||||
ConfigurationImportFields.String(table, "PolicyName");
|
||||
ConfigurationImportFields.String(table, "PolicyDescription");
|
||||
ConfigurationImportFields.String(table, "AnalysisRules");
|
||||
ConfigurationImportFields.String(table, "OutputRules");
|
||||
ConfigurationImportFields.Enum<ConfidenceLevel>(table, "MinimumProviderConfidence");
|
||||
ConfigurationImportFields.Strings(table, "AllowedToolIds");
|
||||
ConfigurationImportFields.String(table, "PreselectedProvider", required: false);
|
||||
ConfigurationImportFields.String(table, "PreselectedProfile", required: false);
|
||||
ConfigurationImportFields.Bool(table, "HidePolicyDefinition");
|
||||
break;
|
||||
default:
|
||||
throw new FormatException(TB("This configuration section cannot be imported here."));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateProvider(string section, LuaTable table)
|
||||
{
|
||||
var model = ConfigurationImportFields.Table(table, "Model");
|
||||
ConfigurationImportFields.String(model, "Id");
|
||||
ConfigurationImportFields.String(model, "DisplayName");
|
||||
ConfigurationImportFields.String(table, section == "LLM_PROVIDERS" ? "InstanceName" : "Name");
|
||||
ConfigurationImportFields.Enum<LLMProviders>(table, "UsedLLMProvider");
|
||||
ConfigurationImportFields.Enum<Host>(table, "Host");
|
||||
ConfigurationImportFields.String(table, "Hostname");
|
||||
if (table.TryGetValue("HFInferenceProvider", out _))
|
||||
ConfigurationImportFields.Enum<HFInferenceProvider>(table, "HFInferenceProvider");
|
||||
if (section != "TRANSCRIPTION_PROVIDERS")
|
||||
ConfigurationImportFields.String(table, "TokenizerPath", required: false);
|
||||
if (section == "LLM_PROVIDERS")
|
||||
ConfigurationImportFields.String(table, "AdditionalJsonApiParameters", required: false);
|
||||
if (section == "EMBEDDING_PROVIDERS")
|
||||
{
|
||||
ConfigurationImportFields.Int(table, "TokenLimit", EmbeddingProvider.DEFAULT_TOKEN_LIMIT);
|
||||
ConfigurationImportFields.Int(table, "EmbeddingBatchSize", EmbeddingProvider.DEFAULT_EMBEDDING_BATCH_SIZE);
|
||||
}
|
||||
ConfigurationImportFields.Credential(table, "APIKey", out _);
|
||||
}
|
||||
|
||||
private static void ValidateChatTemplate(LuaTable 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(TB("An example conversation entry is not a table."));
|
||||
ConfigurationImportFields.Enum<ChatRole>(message, "Role");
|
||||
if (string.IsNullOrWhiteSpace(ConfigurationImportFields.String(message, "Content")))
|
||||
throw new FormatException(TB("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 _))
|
||||
throw new FormatException(TB("The chat template fields are malformed."));
|
||||
ConfigurationImportFields.Strings(table, "FileAttachments");
|
||||
}
|
||||
|
||||
private static void ValidateERIDataSource(LuaTable table)
|
||||
{
|
||||
if (ConfigurationImportFields.String(table, "Type") != "ERI_V1")
|
||||
throw new FormatException(TB("This data source is not an ERI v1 data source."));
|
||||
ConfigurationImportFields.String(table, "Name");
|
||||
ConfigurationImportFields.String(table, "Hostname");
|
||||
var port = ConfigurationImportFields.Int(table, "Port");
|
||||
if (port is < 1 or > 65535)
|
||||
throw new FormatException(TB("The 'Port' field must be between 1 and 65535."));
|
||||
var authMethod = ConfigurationImportFields.Enum<AuthMethod>(table, "AuthMethod");
|
||||
if (authMethod is AuthMethod.KERBEROS)
|
||||
throw new FormatException(TB("Kerberos data sources cannot be imported from configuration snippets."));
|
||||
ConfigurationImportFields.Enum<DataSourceSecurity>(table, "SecurityPolicy");
|
||||
ConfigurationImportFields.String(table, "SelectedRetrievalId");
|
||||
var maxMatches = ConfigurationImportFields.Int(table, "MaxMatches", 10);
|
||||
if (maxMatches is < 1 or > ushort.MaxValue)
|
||||
throw new FormatException(TB("The 'MaxMatches' field is outside the allowed range."));
|
||||
var secretName = authMethod switch
|
||||
{
|
||||
AuthMethod.TOKEN => "Token",
|
||||
AuthMethod.USERNAME_PASSWORD => "Password",
|
||||
_ => string.Empty,
|
||||
};
|
||||
if (!string.IsNullOrEmpty(secretName))
|
||||
ConfigurationImportFields.Credential(table, secretName, out _);
|
||||
ConfigurationImportFields.String(table, "Username", required: false);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,243 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>Reads one exported configuration assignment as data. No Lua state is created.</summary>
|
||||
public sealed class ConfigurationSnippetParser
|
||||
{
|
||||
private readonly string source;
|
||||
private int position;
|
||||
|
||||
private ConfigurationSnippetParser(string source) => this.source = source;
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationSnippetParser).Namespace, nameof(ConfigurationSnippetParser));
|
||||
|
||||
public static bool TryParse(string source, string expectedSection, out LuaTable table, out string issue)
|
||||
{
|
||||
table = new LuaTable();
|
||||
issue = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(source) || source.Length > 1_000_000)
|
||||
{
|
||||
issue = TB("Paste one exported configuration snippet (up to 1 MB).");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var parser = new ConfigurationSnippetParser(source);
|
||||
parser.ExpectWord("CONFIG");
|
||||
var section = parser.ReadBracketedString();
|
||||
if (section != expectedSection)
|
||||
throw new FormatException(string.Format(TB("This is a {0} snippet. Paste a {1} snippet here."), section, expectedSection));
|
||||
|
||||
parser.Expect('[');
|
||||
parser.Expect('#');
|
||||
parser.ExpectWord("CONFIG");
|
||||
if (parser.ReadBracketedString() != expectedSection)
|
||||
throw new FormatException(TB("The configuration section names do not match."));
|
||||
parser.Expect('+');
|
||||
parser.Expect('1');
|
||||
parser.Expect(']');
|
||||
parser.Expect('=');
|
||||
table = parser.ReadTable(0);
|
||||
parser.SkipTrivia();
|
||||
if (parser.position != source.Length)
|
||||
throw new FormatException(TB("The snippet must contain exactly one table assignment and no executable code."));
|
||||
return true;
|
||||
}
|
||||
catch (FormatException exception)
|
||||
{
|
||||
issue = exception.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private LuaTable ReadTable(int depth)
|
||||
{
|
||||
if (depth > 32)
|
||||
throw new FormatException(TB("The snippet contains too many nested tables."));
|
||||
this.Expect('{');
|
||||
var table = new LuaTable();
|
||||
var arrayIndex = 1;
|
||||
var names = new HashSet<string>(StringComparer.Ordinal);
|
||||
while (true)
|
||||
{
|
||||
this.SkipTrivia();
|
||||
if (this.Take('}'))
|
||||
return table;
|
||||
|
||||
// "[[" or "[=" opens a long string, which is an array value rather than a bracketed key:
|
||||
if (this.Peek() == '[' && !this.IsLongStringStart())
|
||||
{
|
||||
this.position++;
|
||||
var key = this.ReadString();
|
||||
this.Expect(']');
|
||||
this.Expect('=');
|
||||
if (!names.Add(key))
|
||||
throw new FormatException(string.Format(TB("The field '{0}' occurs more than once."), key));
|
||||
table[key] = this.ReadValue(depth + 1);
|
||||
}
|
||||
else
|
||||
table[arrayIndex++] = this.ReadValue(depth + 1);
|
||||
|
||||
this.SkipTrivia();
|
||||
if (this.Take('}'))
|
||||
return table;
|
||||
if (!this.Take(',') && !this.Take(';'))
|
||||
throw new FormatException(string.Format(TB("Expected a comma or closing brace at character {0}."), this.position + 1));
|
||||
}
|
||||
}
|
||||
|
||||
private LuaValue ReadValue(int depth)
|
||||
{
|
||||
this.SkipTrivia();
|
||||
if (this.Peek() == '{')
|
||||
return this.ReadTable(depth);
|
||||
if (this.Peek() is '"' or '\'' || this.Peek() == '[')
|
||||
return this.ReadString();
|
||||
if (this.TakeWord("true"))
|
||||
return true;
|
||||
if (this.TakeWord("false"))
|
||||
return false;
|
||||
if (this.TakeWord("nil"))
|
||||
return LuaValue.Nil;
|
||||
|
||||
var start = this.position;
|
||||
if (this.Peek() == '-')
|
||||
this.position++;
|
||||
while (char.IsAsciiDigit(this.Peek()))
|
||||
this.position++;
|
||||
if (this.Peek() == '.')
|
||||
{
|
||||
this.position++;
|
||||
while (char.IsAsciiDigit(this.Peek()))
|
||||
this.position++;
|
||||
}
|
||||
if (this.position > start && double.TryParse(this.source[start..this.position], NumberStyles.Float, CultureInfo.InvariantCulture, out var number) && double.IsFinite(number))
|
||||
return number;
|
||||
throw new FormatException(string.Format(TB("Only literal values are allowed at character {0}; executable Lua is not accepted."), start + 1));
|
||||
}
|
||||
|
||||
private bool IsLongStringStart() => this.position + 1 < this.source.Length && this.source[this.position + 1] is '[' or '=';
|
||||
|
||||
private string ReadBracketedString()
|
||||
{
|
||||
this.Expect('[');
|
||||
var result = this.ReadString();
|
||||
this.Expect(']');
|
||||
return result;
|
||||
}
|
||||
|
||||
private string ReadString()
|
||||
{
|
||||
this.SkipTrivia();
|
||||
var quote = this.Peek();
|
||||
if (quote == '[')
|
||||
{
|
||||
this.position++;
|
||||
var equalsStart = this.position;
|
||||
while (this.Peek() == '=')
|
||||
this.position++;
|
||||
var equals = this.source[equalsStart..this.position];
|
||||
if (this.Peek() != '[')
|
||||
throw new FormatException(string.Format(TB("Expected '{0}' at character {1}."), '[', this.position + 1));
|
||||
this.position++;
|
||||
if (this.Peek() is '\r' or '\n')
|
||||
{
|
||||
if (this.Take('\r'))
|
||||
this.Take('\n');
|
||||
else
|
||||
this.position++;
|
||||
}
|
||||
var end = this.source.IndexOf("]" + equals + "]", this.position, StringComparison.Ordinal);
|
||||
if (end < 0)
|
||||
throw new FormatException(TB("Unterminated long string."));
|
||||
var value = this.source[this.position..end];
|
||||
this.position = end + equals.Length + 2;
|
||||
return value;
|
||||
}
|
||||
if (quote is not ('"' or '\''))
|
||||
throw new FormatException(string.Format(TB("Expected a quoted string at character {0}."), this.position + 1));
|
||||
this.position++;
|
||||
var builder = new StringBuilder();
|
||||
while (this.position < this.source.Length)
|
||||
{
|
||||
var c = this.source[this.position++];
|
||||
if (c == quote)
|
||||
return builder.ToString();
|
||||
if (c is '\r' or '\n')
|
||||
throw new FormatException(TB("A quoted string contains an unescaped newline."));
|
||||
if (c != '\\')
|
||||
{
|
||||
builder.Append(c);
|
||||
continue;
|
||||
}
|
||||
if (this.position == this.source.Length)
|
||||
break;
|
||||
c = this.source[this.position++];
|
||||
builder.Append(c switch
|
||||
{
|
||||
'n' => '\n', 'r' => '\r', 't' => '\t', 'a' => '\a', 'b' => '\b', 'f' => '\f', 'v' => '\v',
|
||||
'\\' => '\\', '"' => '"', '\'' => '\'',
|
||||
_ => throw new FormatException(string.Format(TB("Unsupported string escape sequence: {0}"), "\\" + c)),
|
||||
});
|
||||
}
|
||||
throw new FormatException(TB("Unterminated quoted string."));
|
||||
}
|
||||
|
||||
private void SkipTrivia()
|
||||
{
|
||||
while (this.position < this.source.Length)
|
||||
{
|
||||
if (char.IsWhiteSpace(this.source[this.position]))
|
||||
{
|
||||
this.position++;
|
||||
continue;
|
||||
}
|
||||
if (this.source.AsSpan(this.position).StartsWith("--"))
|
||||
{
|
||||
this.position += 2;
|
||||
while (this.position < this.source.Length && this.source[this.position] != '\n')
|
||||
this.position++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private char Peek() => this.position < this.source.Length ? this.source[this.position] : '\0';
|
||||
|
||||
private bool Take(char c)
|
||||
{
|
||||
this.SkipTrivia();
|
||||
if (this.Peek() != c)
|
||||
return false;
|
||||
this.position++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Expect(char c)
|
||||
{
|
||||
if (!this.Take(c))
|
||||
throw new FormatException(string.Format(TB("Expected '{0}' at character {1}."), c, this.position + 1));
|
||||
}
|
||||
|
||||
private bool TakeWord(string word)
|
||||
{
|
||||
this.SkipTrivia();
|
||||
if (!this.source.AsSpan(this.position).StartsWith(word) ||
|
||||
(this.position + word.Length < this.source.Length && (char.IsLetterOrDigit(this.source[this.position + word.Length]) || this.source[this.position + word.Length] == '_')))
|
||||
return false;
|
||||
this.position += word.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ExpectWord(string word)
|
||||
{
|
||||
if (!this.TakeWord(word))
|
||||
throw new FormatException(string.Format(TB("Expected '{0}' at character {1}."), word, this.position + 1));
|
||||
}
|
||||
}
|
||||
@ -250,6 +250,15 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
// Config: allow the user to add transcription providers?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToAddTranscriptionProvider, this.Id, settingsTable, dryRun);
|
||||
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportConfigurationSnippets, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportProfile, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportLLMProvider, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportEmbeddingProvider, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportTranscriptionProvider, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportChatTemplate, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportERIDataSource, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportDocumentAnalysisPolicy, this.Id, settingsTable, dryRun);
|
||||
|
||||
// Config: allow the user to import plugin archives?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToImportPlugins, this.Id, settingsTable, dryRun);
|
||||
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
# v26.9.1, build 256 (2026-09-xx xx:xx UTC)
|
||||
- Added labeled import buttons beside Add for your providers, profiles, chat templates, ERI v1 data sources, and document analysis policies. Paste an exported configuration snippet, review the filled form, and save it as your own item.
|
||||
- Added controls for organizations to allow or lock configuration snippet imports by item type. A locked Import button stays visible, and plugin archive imports keep their own controls.
|
||||
- Added a way to relink or remove missing file attachments when you create a chat template from an exported configuration.
|
||||
- Added a way to copy an entire chat, either with the button in the chat toolbar or next to the chat in the chat list. The copy opens right away so you can continue in it, while the original conversation stays exactly as it was. Many thanks to Peer Hogeterp (`peerschuett`) and Jens Erler (`j-erler`) for this feature.
|
||||
- Added a way to roll a chat back to an earlier AI response. The response you pick stays, and every message after it is removed permanently, together with the attachments of those messages.
|
||||
- Added a way to save a single code block of an answer. When an answer holds a web page, a LaTeX document, or a Markdown text, the export menu now offers that block as a file of its own.
|
||||
|
||||
@ -619,6 +619,16 @@ Some exports ask a question first: a provider with an API key offers to include
|
||||
|
||||
Handing a whole plugin to a colleague is a different thing: that is the **Share** function on the plugins page, which writes a `.mwplugin` archive and is governed by its own organization setting rather than by the administration settings.
|
||||
|
||||
### Importing one exported item for personal use
|
||||
|
||||
Each parent screen has a button naming the item to import beside **Add** for profiles, LLM providers, embedding providers, transcription providers, chat templates, and document analysis policies. The data source screen has **Import ERI v1 Data Source** beside **Add Data Source**. Copy an exported snippet from the item's **Export configuration** control, paste one snippet of the matching type into the import dialog, and choose its item-specific import button. AI Studio reads the table as data; it does not run Lua code. The existing creation form then opens with its fields filled in. Review them and choose **Add** to create the item. Canceling either dialog leaves nothing saved. Tool Settings snippets cannot be imported this way.
|
||||
|
||||
The imported item receives a new ID and the next local number. It belongs to you, even if the snippet came from an organization configuration. Existing provider, profile, tool, and data source references are kept; unavailable references are flagged so you can review them. Chat template attachments must point to existing local files. Relink or remove any missing or relative paths in the creation dialog before saving.
|
||||
|
||||
If an exported provider or ERI source contains an `ENC:v1` credential, AI Studio can fill it only on a device with the matching enterprise encryption secret. Otherwise, enter your own key, token, or password. A credential is stored in the local operating system secret store only when you save the item. Exporting still requires **Show administration settings**.
|
||||
|
||||
All eight snippet import settings default to `true`. `DataApp.AllowUserToImportConfigurationSnippets` is the master switch; `DataApp.AllowUserToImportProfile`, `DataApp.AllowUserToImportLLMProvider`, `DataApp.AllowUserToImportEmbeddingProvider`, `DataApp.AllowUserToImportTranscriptionProvider`, `DataApp.AllowUserToImportChatTemplate`, `DataApp.AllowUserToImportERIDataSource`, and `DataApp.AllowUserToImportDocumentAnalysisPolicy` control the seven item types. The master and matching item setting must both allow the import. Provider imports also require `DataApp.AllowUserToAddProvider` and the matching provider Add permission. A blocked Import button stays visible with a lock. AI Studio checks these permissions when opening the import dialog and again before saving. Plugin archive import permissions are independent.
|
||||
|
||||
## Encrypted API Keys
|
||||
|
||||
You can include encrypted API keys in your configuration plugins for cloud providers (like OpenAI, Anthropic) or secured on-premise models. This feature provides obfuscation to prevent casual exposure of API keys in configuration files.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user