Added admin dialog for tool configuration exports (#949)

This commit is contained in:
Thorsten Sommer 2026-09-05 19:16:54 +02:00 committed by GitHub
parent 4141e7c504
commit 88bf475847
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 921 additions and 102 deletions

View File

@ -3334,6 +3334,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The lo
-- The image at the URL is too large (>10 MB). Skipping the image.
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image."
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ADMINEXPORTBUTTON::T975426229"] = "Export configuration"
-- Open Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
@ -4606,9 +4609,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T80509
-- Example text to embed
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T816748904"] = "Example text to embed"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T975426229"] = "Export configuration"
-- Cannot export the encrypted API key: No enterprise encryption secret is configured.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T1832230847"] = "Cannot export the encrypted API key: No enterprise encryption secret is configured."
@ -4681,9 +4681,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T579100
-- Open Dashboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T78223861"] = "Open Dashboard"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T975426229"] = "Export configuration"
-- Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1258653480"] = "Settings"
@ -4780,9 +4777,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T78
-- Are you sure you want to delete the transcription provider '{0}'?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T789660305"] = "Are you sure you want to delete the transcription provider '{0}'?"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T975426229"] = "Export configuration"
-- Copy {0} to the clipboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TEXTINFOLINE::T2206391442"] = "Copy {0} to the clipboard"
@ -7462,9 +7456,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T782820
-- Local Directory
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T926703547"] = "Local Directory"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T975426229"] = "Export configuration"
-- When enabled, you can preselect some ERI server options.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGERISERVER::T1280666275"] = "When enabled, you can preselect some ERI server options."
@ -7756,9 +7747,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T55364659"
-- Are you a project manager in a research facility? You might want to create a profile for your project management activities, one for your scientific work, and a profile for when you need to write program code. In these profiles, you can record how much experience you have or which methods you like or dislike using. Later, you can choose when and where you want to use each profile.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T56359901"] = "Are you a project manager in a research facility? You might want to create a profile for your project management activities, one for your scientific work, and a profile for when you need to write program code. In these profiles, you can record how much experience you have or which methods you like or dislike using. Later, you can choose when and where you want to use each profile."
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T975426229"] = "Export configuration"
-- Preselect the target language
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROMPTOPTIMIZER::T1417990312"] = "Preselect the target language"
@ -8218,6 +8206,81 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T403490413"] =
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Cancel"
-- The tool configuration could not be exported. Please try again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1064444653"] = "The tool configuration could not be exported. Please try again."
-- The selected areas contain no configured API keys or other secrets.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1362677286"] = "The selected areas contain no configured API keys or other secrets."
-- Loading tool configuration...
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1750745869"] = "Loading tool configuration..."
-- Select all
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1794248818"] = "Select all"
-- Include minimum provider confidence
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1823629028"] = "Include minimum provider confidence"
-- The selected areas contain no settings to export.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T197560416"] = "The selected areas contain no settings to export."
-- Include encrypted API keys and other secrets
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1978141571"] = "Include encrypted API keys and other secrets"
-- Settings to include
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2051465617"] = "Settings to include"
-- Editable defaults
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2389486789"] = "Editable defaults"
-- {0} of the selected settings are empty and are exported as empty locked values. Users cannot change a locked setting, so an empty required one leaves the tool unusable. Deselect the areas you have not configured, or export them as editable defaults.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2555293033"] = "{0} of the selected settings are empty and are exported as empty locked values. Users cannot change a locked setting, so an empty required one leaves the tool unusable. Deselect the areas you have not configured, or export them as editable defaults."
-- No minimum confidence level chosen
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2828607242"] = "No minimum confidence level chosen"
-- Secrets are always exported as locked settings. Recipients need the same enterprise encryption secret to use them.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3001812876"] = "Secrets are always exported as locked settings. Recipients need the same enterprise encryption secret to use them."
-- The tool configuration could not be loaded. Please close this dialog and try again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3388093684"] = "The tool configuration could not be loaded. Please close this dialog and try again."
-- Export tool configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3758205437"] = "Export tool configuration"
-- Export mode
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3810275878"] = "Export mode"
-- The selected tool could not be loaded.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3907843187"] = "The selected tool could not be loaded."
-- Each area is independent. Select general settings separately if you want to include them.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3911375461"] = "Each area is independent. Select general settings separately if you want to include them."
-- This choice applies to settings other than secrets and the minimum provider confidence.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T4236004495"] = "This choice applies to settings other than secrets and the minimum provider confidence."
-- Export to clipboard
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T508399334"] = "Export to clipboard"
-- No enterprise encryption secret is configured. API keys and other secrets cannot be exported.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T633982489"] = "No enterprise encryption secret is configured. API keys and other secrets cannot be exported."
-- Locked settings
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T651584564"] = "Locked settings"
-- Export saved settings as Lua code for your configuration plugin. You can combine exports and adapt the code before deploying it.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T744840132"] = "Export saved settings as Lua code for your configuration plugin. You can combine exports and adapt the code before deploying it."
-- This setting is always locked and applies to the entire tool. The configuration plugin locks minimum provider confidence levels together for all tools in its confidence table.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T857922074"] = "This setting is always locked and applies to the entire tool. The configuration plugin locks minimum provider confidence levels together for all tools in its confidence table."
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T900713019"] = "Cancel"
-- Current requirement: {0}
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T982466527"] = "Current requirement: {0}"
-- Save
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Save"
@ -11416,6 +11479,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::HARNESS::TOOLCALLINGMESSAGE
-- The tool calling request failed with status code {0}. See the logs for details.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::HARNESS::TOOLCALLINGMESSAGES::T3117779001"] = "The tool calling request failed with status code {0}. See the logs for details."
-- General
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T1432485131"] = "General"
-- Tool
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T3517012711"] = "Tool"
@ -11674,6 +11740,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSOPTIONSOURCES::
-- Any language
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSOPTIONSOURCES::T747012729"] = "Any language"
-- The tool's minimum provider confidence level is invalid.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T2093219126"] = "The tool's minimum provider confidence level is invalid."
-- Cannot export encrypted tool secrets: No enterprise encryption secret is configured.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T3174877792"] = "Cannot export encrypted tool secrets: No enterprise encryption secret is configured."
-- The tool secrets could not be encrypted. Nothing was exported.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T403101133"] = "The tool secrets could not be encrypted. Nothing was exported."
-- The file path is null or empty and the file therefore can not be loaded.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::USERFILE::T932243993"] = "The file path is null or empty and the file therefore can not be loaded."

View File

@ -0,0 +1,8 @@
@inherits MSGComponentBase
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Variant="@this.Variant" Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="@this.Export" aria-label="@T("Export configuration")" />
</MudTooltip>
}

View File

@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components;
/// <summary>
/// The common admin-only configuration export action. Callers decide what is exported.
/// </summary>
public partial class AdminExportButton : MSGComponentBase
{
[Parameter]
public EventCallback OnClick { get; set; }
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
}
private async Task Export()
{
if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
await this.OnClick.InvokeAsync();
}
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
if (triggeredEvent is Event.CONFIGURATION_CHANGED)
this.StateHasChanged();
return Task.CompletedTask;
}
}

View File

@ -73,12 +73,7 @@
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="@(() => this.EditEmbeddingProvider(context))"/>
</MudTooltip>
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="@(() => this.ExportEmbeddingProvider(context))"/>
</MudTooltip>
}
<AdminExportButton OnClick="@(() => this.ExportEmbeddingProvider(context))" />
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => this.DeleteEmbeddingProvider(context))"/>
</MudTooltip>

View File

@ -61,12 +61,7 @@
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="@(() => this.EditLLMProvider(context))"/>
</MudTooltip>
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="@(() => this.ExportLLMProvider(context))"/>
</MudTooltip>
}
<AdminExportButton OnClick="@(() => this.ExportLLMProvider(context))" />
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => this.DeleteLLMProvider(context))"/>
</MudTooltip>

View File

@ -53,7 +53,12 @@
}
</MudTd>
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.OpenSettings(context.Definition.Id))" />
<MudStack Row="true" Wrap="Wrap.NoWrap" AlignItems="AlignItems.Center" Spacing="0">
<MudTooltip Text="@T("Settings")">
<MudIconButton Icon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.OpenSettings(context.Definition.Id))" aria-label="@T("Settings")" />
</MudTooltip>
<AdminExportButton OnClick="@(() => this.OpenExport(context.Definition.Id))" />
</MudStack>
</MudTd>
</RowTemplate>
</MudTable>

View File

@ -34,6 +34,19 @@ public partial class SettingsPanelTools : SettingsPanelBase
this.StateHasChanged();
}
private async Task OpenExport(string toolId)
{
if (!this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
return;
var parameters = new DialogParameters<ToolSettingsExportDialog>
{
{ x => x.ToolId, toolId },
};
await this.DialogService.ShowAsync<ToolSettingsExportDialog>(null, parameters, Dialogs.DialogOptions.FULLSCREEN);
}
private string GetConfigurationTooltip(ToolCatalogItem item) => item.ConfigurationState.MissingRequiredFields.Count switch
{
_ when !string.IsNullOrWhiteSpace(item.ConfigurationState.Message) => item.ConfigurationState.Message,

View File

@ -66,12 +66,7 @@
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="@(() => this.EditTranscriptionProvider(context))"/>
</MudTooltip>
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="@(() => this.ExportTranscriptionProvider(context))"/>
</MudTooltip>
}
<AdminExportButton OnClick="@(() => this.ExportTranscriptionProvider(context))" />
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => this.DeleteTranscriptionProvider(context))"/>
</MudTooltip>

View File

@ -48,27 +48,22 @@
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="@(() => this.EditChatTemplate(context))"/>
</MudTooltip>
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
@if (context.FileAttachments.Count == 0)
{
@if (context.FileAttachments.Count == 0)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="@(() => this.ExportChatTemplateWithSharedAttachmentPaths(context))"/>
</MudTooltip>
}
else
{
<MudTooltip Text="@T("Export configuration")">
<MudMenu Icon="@Icons.Material.Filled.Dataset" Color="Color.Info" Variant="Variant.Text">
<MudMenuItem Icon="@Icons.Material.Filled.Link" OnClick="@(() => this.ExportChatTemplateWithSharedAttachmentPaths(context))">
@T("Use shared attachment paths")
</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Folder" Disabled="@this.isPluginDirectoryDialogOpen" OnClick="@(() => this.ExportChatTemplateWithPackagedAttachments(context))">
@T("Copy attachments into plugin")
</MudMenuItem>
</MudMenu>
</MudTooltip>
}
<AdminExportButton OnClick="@(() => this.ExportChatTemplateWithSharedAttachmentPaths(context))" />
}
else if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
<MudTooltip Text="@T("Export configuration")">
<MudMenu Icon="@Icons.Material.Filled.Dataset" Color="Color.Info" Variant="Variant.Text">
<MudMenuItem Icon="@Icons.Material.Filled.Link" OnClick="@(() => this.ExportChatTemplateWithSharedAttachmentPaths(context))">
@T("Use shared attachment paths")
</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Folder" Disabled="@this.isPluginDirectoryDialogOpen" OnClick="@(() => this.ExportChatTemplateWithPackagedAttachments(context))">
@T("Copy attachments into plugin")
</MudMenuItem>
</MudMenu>
</MudTooltip>
}
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => this.DeleteChatTemplate(context))"/>

View File

@ -49,11 +49,9 @@
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditDataSource(context)">
@T("Edit")
</MudButton>
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings && context is DataSourceERI_V1)
@if (context is DataSourceERI_V1)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Variant="Variant.Filled" Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="() => this.ExportDataSource(context)"/>
</MudTooltip>
<AdminExportButton Variant="Variant.Filled" OnClick="@(() => this.ExportDataSource(context))" />
}
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteDataSource(context)">
@T("Delete")

View File

@ -37,7 +37,7 @@
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
</MudTooltip>
<MudTooltip Text="@T("View")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Visibility" OnClick="() => this.ViewProfile(context)"/>
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Visibility" OnClick="@(() => this.ViewProfile(context))"/>
</MudTooltip>
</MudStack>
}
@ -45,16 +45,11 @@
{
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditProfile(context)"/>
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="@(() => this.EditProfile(context))"/>
</MudTooltip>
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
<MudTooltip Text="@T("Export configuration")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Dataset" OnClick="() => this.ExportProfile(context)"/>
</MudTooltip>
}
<AdminExportButton OnClick="@(() => this.ExportProfile(context))" />
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteProfile(context)"/>
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => this.DeleteProfile(context))"/>
</MudTooltip>
</MudStack>
}

View File

@ -0,0 +1,97 @@
@using AIStudio.Tools.ToolCallingSystem
@using AIStudio.Tools.PluginSystem
@inherits SettingsDialogBase
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6" Class="d-flex align-center">
<MudIcon Icon="@Icons.Material.Filled.Dataset" Class="mr-2" />
@T("Export tool configuration")
</MudText>
</TitleContent>
<DialogContent>
@if (this.IsAdmin)
{
@if (!string.IsNullOrWhiteSpace(this.message))
{
<MudAlert Severity="@this.messageSeverity" Class="mb-4">@this.message</MudAlert>
}
@if (this.isLoading)
{
<MudProgressCircular Indeterminate="@true" Size="Size.Small" />
<MudText>@T("Loading tool configuration...")</MudText>
}
else if (this.toolDefinition is null || this.implementation is null)
{
@if (string.IsNullOrWhiteSpace(this.message))
{
<MudText>@T("The selected tool could not be loaded.")</MudText>
}
}
else
{
<MudText Typo="Typo.subtitle1" Class="mb-2">@this.implementation.GetDisplayName()</MudText>
<MudText Typo="Typo.body2" Class="mb-4">
@T("Export saved settings as Lua code for your configuration plugin. You can combine exports and adapt the code before deploying it.")
</MudText>
@if (this.areas.Count > 0)
{
<MudPaper Class="pa-3 mb-4 border-dashed border rounded-lg">
<MudText Typo="Typo.subtitle2">@T("Settings to include")</MudText>
@if (this.areas.Count > 1)
{
<MudCheckBox T="bool" Label="@T("Select all")" Value="@this.AllAreasSelected" ValueChanged="@this.SelectAllAreas" Disabled="@this.isExporting" />
}
@foreach (var area in this.areas)
{
<MudCheckBox @key="area.Id" T="bool" Label="@area.Label" Value="@this.selectedAreaIds.Contains(area.Id)" ValueChanged="@(selected => this.SelectArea(area.Id, selected))" Disabled="@this.isExporting" />
}
<MudText Typo="Typo.caption">@T("Each area is independent. Select general settings separately if you want to include them.")</MudText>
</MudPaper>
}
<MudSelect T="ToolSettingsExportMode" Label="@T("Export mode")" @bind-Value="this.mode" Variant="Variant.Outlined" Class="mb-4" Disabled="@this.isExporting" HelperText="@T("This choice applies to settings other than secrets and the minimum provider confidence.")">
<MudSelectItem T="ToolSettingsExportMode" Value="@ToolSettingsExportMode.LOCKED">@T("Locked settings")</MudSelectItem>
<MudSelectItem T="ToolSettingsExportMode" Value="@ToolSettingsExportMode.DEFAULT">@T("Editable defaults")</MudSelectItem>
</MudSelect>
@if (this.WarnAboutEmptyLockedSettings)
{
<MudAlert Severity="Severity.Warning" Class="mb-4">
@string.Format(T("{0} of the selected settings are empty and are exported as empty locked values. Users cannot change a locked setting, so an empty required one leaves the tool unusable. Deselect the areas you have not configured, or export them as editable defaults."), this.EmptySelectedFieldCount)
</MudAlert>
}
<MudPaper Class="pa-3 mb-4 border-dashed border rounded-lg">
<MudCheckBox T="bool" Label="@T("Include encrypted API keys and other secrets")" @bind-Value="this.includeSecrets" Disabled="@(this.isExporting || !this.CanIncludeSecrets)" />
@if (PluginFactory.EnterpriseEncryption?.IsAvailable is not true)
{
<MudText Typo="Typo.body2">@T("No enterprise encryption secret is configured. API keys and other secrets cannot be exported.")</MudText>
}
else if (!this.HasSelectedSecrets)
{
<MudText Typo="Typo.body2">@T("The selected areas contain no configured API keys or other secrets.")</MudText>
}
else
{
<MudText Typo="Typo.body2">@T("Secrets are always exported as locked settings. Recipients need the same enterprise encryption secret to use them.")</MudText>
}
</MudPaper>
<MudPaper Class="pa-3 mb-4 border-dashed border rounded-lg">
<MudCheckBox T="bool" Label="@T("Include minimum provider confidence")" @bind-Value="this.includeMinimumProviderConfidence" Disabled="@this.isExporting" />
<MudText Typo="Typo.body2" Class="mb-2">@string.Format(T("Current requirement: {0}"), this.GetMinimumProviderConfidenceName())</MudText>
<MudText Typo="Typo.body2">@T("This setting is always locked and applies to the entire tool. The configuration plugin locks minimum provider confidence levels together for all tools in its confidence table.")</MudText>
</MudPaper>
}
}
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Close" Variant="Variant.Text">@T("Cancel")</MudButton>
<MudButton OnClick="@this.Export" Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" Disabled="@(!this.CanExport)">
@T("Export to clipboard")
</MudButton>
</DialogActions>
</MudDialog>

View File

@ -0,0 +1,204 @@
using AIStudio.Provider;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.ToolCallingSystem;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Dialogs.Settings;
public partial class ToolSettingsExportDialog : SettingsDialogBase
{
[Parameter]
public string ToolId { get; set; } = string.Empty;
[Inject]
private ToolRegistry ToolRegistry { get; init; } = null!;
[Inject]
private ToolSettingsService ToolSettingsService { get; init; } = null!;
[Inject]
private ILogger<ToolSettingsExportDialog> Logger { get; init; } = null!;
private ToolDefinition? toolDefinition;
private IToolImplementation? implementation;
private IReadOnlyList<ExportableSettings> areas = [];
private HashSet<string> selectedAreaIds = new(StringComparer.Ordinal);
private HashSet<string> configuredSecretFields = new(StringComparer.Ordinal);
private HashSet<string> emptyFieldNames = new(StringComparer.Ordinal);
private ToolSettingsExportMode mode = ToolSettingsExportMode.LOCKED;
private bool includeSecrets;
private bool includeMinimumProviderConfidence = true;
private bool isLoading = true;
private bool isExporting;
private bool isDisposed;
private string message = string.Empty;
private Severity messageSeverity = Severity.Error;
private bool IsAdmin => this.SettingsManager.ConfigurationData.App.ShowAdminSettings;
private bool AllAreasSelected => this.areas.Count > 0 && this.areas.All(area => this.selectedAreaIds.Contains(area.Id));
private bool HasSelectedSecrets => this.areas.Any(area => this.selectedAreaIds.Contains(area.Id) && area.FieldNames.Any(this.configuredSecretFields.Contains));
private bool CanIncludeSecrets => this.HasSelectedSecrets && PluginFactory.EnterpriseEncryption?.IsAvailable is true;
/// <summary>
/// How many of the selected settings hold no value, counting a field shared by two areas once.
/// </summary>
/// <remarks>
/// Saving a tool's settings writes every field of its schema, empty ones included, so an area
/// the administrator never filled in still exports. Locked, those empty values are what the
/// recipient is left with and cannot change, which is worth saying before the export.
/// </remarks>
private int EmptySelectedFieldCount => this.areas
.Where(area => this.selectedAreaIds.Contains(area.Id))
.SelectMany(area => area.FieldNames)
.Distinct(StringComparer.Ordinal)
.Count(this.emptyFieldNames.Contains);
private bool WarnAboutEmptyLockedSettings => this.mode is ToolSettingsExportMode.LOCKED && this.EmptySelectedFieldCount > 0;
private bool CanExport => this.IsAdmin && !this.isLoading && !this.isExporting && !this.isDisposed &&
this.toolDefinition is not null && this.implementation is not null && (this.selectedAreaIds.Count > 0 || this.includeMinimumProviderConfidence);
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (!this.IsAdmin)
{
this.Close();
return;
}
try
{
this.toolDefinition = this.ToolRegistry.GetDefinition(this.ToolId);
if (this.toolDefinition is null)
return;
this.implementation = this.ToolRegistry.GetImplementation(this.toolDefinition.ImplementationKey);
if (this.implementation is null)
return;
this.areas = this.implementation.GetExportableSettings(this.toolDefinition);
this.selectedAreaIds = this.areas.Select(area => area.Id).ToHashSet(StringComparer.Ordinal);
// Retain only field names, never the values themselves, so no plaintext secret lives
// in this component. ExportAsync reads effective settings again when the
// administrator exports.
var values = await this.ToolSettingsService.GetSettingsAsync(this.toolDefinition);
this.configuredSecretFields = this.toolDefinition.SettingsSchema.Properties
.Where(property => property.Value.Secret && values.TryGetValue(property.Key, out var value) && !string.IsNullOrWhiteSpace(value))
.Select(property => property.Key)
.ToHashSet(StringComparer.Ordinal);
// A field the export writes as an empty value: it has to be present, because a
// missing one is skipped rather than exported, and it has to be a non-secret,
// because an empty secret is skipped as well.
this.emptyFieldNames = this.toolDefinition.SettingsSchema.Properties
.Where(property => !property.Value.Secret && values.TryGetValue(property.Key, out var value) && string.IsNullOrWhiteSpace(value))
.Select(property => property.Key)
.ToHashSet(StringComparer.Ordinal);
}
catch (Exception e)
{
// A runtime error may contain secret data, so it goes to the log for diagnosis but
// never into the dialog:
this.Logger.LogError(e, "Failed to load the configuration of the tool '{ToolId}' for export.", this.ToolId);
this.toolDefinition = null;
this.message = T("The tool configuration could not be loaded. Please close this dialog and try again.");
}
finally
{
this.isLoading = false;
}
}
private void SelectArea(string areaId, bool selected)
{
if (selected)
this.selectedAreaIds.Add(areaId);
else
this.selectedAreaIds.Remove(areaId);
this.SelectionChanged();
}
private void SelectAllAreas(bool selected)
{
this.selectedAreaIds = selected ? this.areas.Select(area => area.Id).ToHashSet(StringComparer.Ordinal) : new(StringComparer.Ordinal);
this.SelectionChanged();
}
private void SelectionChanged()
{
// A new selection must not keep an invisible opt-in to secrets it no longer contains.
if (!this.CanIncludeSecrets)
this.includeSecrets = false;
this.message = string.Empty;
}
private string GetMinimumProviderConfidenceName()
{
var confidence = this.toolDefinition is null ? ConfidenceLevel.NONE : this.ToolRegistry.GetMinimumProviderConfidence(this.toolDefinition);
return confidence is ConfidenceLevel.NONE ? T("No minimum confidence level chosen") : confidence.GetName();
}
private async Task Export()
{
if (!this.CanExport || this.toolDefinition is null || this.implementation is null)
return;
this.isExporting = true;
this.message = string.Empty;
this.messageSeverity = Severity.Error;
try
{
var options = new ToolSettingsExportOptions
{
SelectedAreaIds = new HashSet<string>(this.selectedAreaIds, StringComparer.Ordinal),
Mode = this.mode,
IncludeSecrets = this.includeSecrets,
IncludeMinimumProviderConfidence = this.includeMinimumProviderConfidence,
};
var result = await this.ToolSettingsService.ExportAsync(this.toolDefinition, this.implementation, options);
if (this.isDisposed || !this.IsAdmin)
return;
if (!result.Success)
{
this.message = result.ErrorMessage;
return;
}
if (string.IsNullOrWhiteSpace(result.LuaCode))
{
this.messageSeverity = Severity.Info;
this.message = T("The selected areas contain no settings to export.");
return;
}
// The runtime reports clipboard success or failure. Keep the dialog open so that
// administrators can retry or export another selection from the same tool.
await this.RustService.CopyText2Clipboard(result.LuaCode);
}
catch (Exception e)
{
this.Logger.LogError(e, "Failed to export the configuration of the tool '{ToolId}'.", this.ToolId);
this.message = T("The tool configuration could not be exported. Please try again.");
}
finally
{
this.isExporting = false;
}
}
protected override void DisposeResources()
{
this.isDisposed = true;
base.DisposeResources();
}
}

View File

@ -568,7 +568,7 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::ASSISTANTBATCHPROCESSING
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::ASSISTANTBATCHPROCESSING::T3769421748"] = "fehlgeschlagen"
-- Tools used
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::ASSISTANTBATCHPROCESSING::T3809968257"] = "Verwendete Tools"
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::ASSISTANTBATCHPROCESSING::T3809968257"] = "Verwendete Werkzeuge"
-- Cancel the batch run
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::ASSISTANTBATCHPROCESSING::T3830551741"] = "Stapellauf abbrechen"
@ -3336,6 +3336,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "Die lo
-- The image at the URL is too large (>10 MB). Skipping the image.
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "Das Bild unter der URL ist zu groß (>10 MB). Das Bild wird übersprungen."
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ADMINEXPORTBUTTON::T975426229"] = "Konfiguration exportieren"
-- Open Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Einstellungen öffnen"
@ -4608,9 +4611,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T80509
-- Example text to embed
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T816748904"] = "Beispieltext zum Einbetten"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T975426229"] = "Konfiguration exportieren"
-- Cannot export the encrypted API key: No enterprise encryption secret is configured.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T1832230847"] = "Der verschlüsselte API-Schlüssel kann nicht exportiert werden: Es ist kein Geheimnis für die Verschlüsselung konfiguriert."
@ -4683,9 +4683,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T579100
-- Open Dashboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T78223861"] = "Dashboard öffnen"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T975426229"] = "Konfiguration exportieren"
-- Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1258653480"] = "Einstellungen"
@ -4708,7 +4705,7 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T266367750"
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2828607242"] = "Kein Mindestvertrauensniveau ausgewählt"
-- Minimum provider confidence
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "Minimale Anbieterzuverlässigkeit"
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "Minimales Vertrauensniveau für Anbieter"
-- Configure global settings for each tool.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3728248397"] = "Konfiguriere globale Einstellungen für jedes Werkzeug."
@ -4782,9 +4779,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T78
-- Are you sure you want to delete the transcription provider '{0}'?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T789660305"] = "Möchten Sie den Anbieter für Transkriptionen „{0}“ wirklich löschen?"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T975426229"] = "Konfiguration exportieren"
-- Copy {0} to the clipboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TEXTINFOLINE::T2206391442"] = "Kopiere {0} in die Zwischenablage"
@ -7464,9 +7458,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T782820
-- Local Directory
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T926703547"] = "Lokaler Ordner"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T975426229"] = "Konfiguration exportieren"
-- When enabled, you can preselect some ERI server options.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGERISERVER::T1280666275"] = "Wenn aktiviert, können Sie einige ERI-Serveroptionen vorauswählen."
@ -7758,9 +7749,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T55364659"
-- Are you a project manager in a research facility? You might want to create a profile for your project management activities, one for your scientific work, and a profile for when you need to write program code. In these profiles, you can record how much experience you have or which methods you like or dislike using. Later, you can choose when and where you want to use each profile.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T56359901"] = "Sind Sie Projektleiter in einer Forschungseinrichtung? Dann möchten Sie vielleicht ein Profil für ihre Projektmanagement-Aktivitäten anlegen, eines für ihre wissenschaftliche Arbeit und ein weiteres Profil, wenn Sie Programmcode schreiben müssen. In diesen Profilen können Sie festhalten, wie viel Erfahrung Sie haben oder welche Methoden Sie bevorzugen oder nicht gerne verwenden. Später können Sie dann auswählen, wann und wo Sie jedes Profil nutzen möchten."
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T975426229"] = "Konfiguration exportieren"
-- Preselect the target language
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROMPTOPTIMIZER::T1417990312"] = "Zielsprache vorwählen"
@ -8220,6 +8208,81 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T403490413"] =
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Abbrechen"
-- The tool configuration could not be exported. Please try again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1064444653"] = "Die Werkzeugkonfiguration konnte nicht exportiert werden. Bitte versuchen Sie es erneut."
-- The selected areas contain no configured API keys or other secrets.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1362677286"] = "Die ausgewählten Bereiche enthalten keine konfigurierten API-Schlüssel oder sonstigen Geheimnisse."
-- Loading tool configuration...
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1750745869"] = "Werkzeugkonfiguration wird geladen …"
-- Select all
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1794248818"] = "Alle auswählen"
-- Include minimum provider confidence
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1823629028"] = "Minimales Vertrauensniveau für Anbieter einbeziehen"
-- The selected areas contain no settings to export.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T197560416"] = "Die ausgewählten Bereiche enthalten keine Einstellungen zum Exportieren."
-- Include encrypted API keys and other secrets
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1978141571"] = "Verschlüsselte API-Schlüssel und andere Geheimnisse einbeziehen"
-- Settings to include
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2051465617"] = "Einzubeziehende Einstellungen"
-- Editable defaults
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2389486789"] = "Bearbeitbare Standardwerte"
-- {0} of the selected settings are empty and are exported as empty locked values. Users cannot change a locked setting, so an empty required one leaves the tool unusable. Deselect the areas you have not configured, or export them as editable defaults.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2555293033"] = "{0} der ausgewählten Einstellungen sind leer und werden als leere, gesperrte Werte exportiert. Benutzer können eine gesperrte Einstellung nicht ändern. Ist eine erforderliche Einstellung leer und gesperrt, kann das Tool nicht verwendet werden. Wählen Sie die Bereiche ab, die Sie nicht konfiguriert haben, oder exportieren Sie sie als bearbeitbare Standardwerte."
-- No minimum confidence level chosen
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2828607242"] = "Kein Mindestvertrauensniveau ausgewählt"
-- Secrets are always exported as locked settings. Recipients need the same enterprise encryption secret to use them.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3001812876"] = "Geheimnisse werden immer als gesperrte Einstellungen exportiert. Empfänger benötigen dasselbe Geheimnis für die Verschlüsselung, um sie verwenden zu können."
-- The tool configuration could not be loaded. Please close this dialog and try again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3388093684"] = "Die Werkzeugkonfiguration konnte nicht geladen werden. Bitte schließen Sie diesen Dialog und versuchen Sie es erneut."
-- Export tool configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3758205437"] = "Werkzeugkonfiguration exportieren"
-- Export mode
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3810275878"] = "Exportmodus"
-- The selected tool could not be loaded.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3907843187"] = "Das ausgewählte Werkzeug konnte nicht geladen werden."
-- Each area is independent. Select general settings separately if you want to include them.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3911375461"] = "Jeder Bereich ist unabhängig. Wählen Sie die allgemeinen Einstellungen separat aus, wenn Sie sie einbeziehen möchten."
-- This choice applies to settings other than secrets and the minimum provider confidence.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T4236004495"] = "Diese Auswahl gilt für Einstellungen mit Ausnahme von Geheimnissen und dem minimalen Vertrauensniveau für Anbieter."
-- Export to clipboard
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T508399334"] = "In die Zwischenablage exportieren"
-- No enterprise encryption secret is configured. API keys and other secrets cannot be exported.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T633982489"] = "Es ist kein Geheimnis für die Verschlüsselung konfiguriert. API-Schlüssel und andere Geheimnisse können nicht exportiert werden."
-- Locked settings
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T651584564"] = "Gesperrte Einstellungen"
-- Export saved settings as Lua code for your configuration plugin. You can combine exports and adapt the code before deploying it.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T744840132"] = "Exportieren Sie gespeicherte Einstellungen als Lua-Code für Ihr Konfigurations-Plugin. Sie können Exporte kombinieren und den Code vor dem Einsatz anpassen."
-- This setting is always locked and applies to the entire tool. The configuration plugin locks minimum provider confidence levels together for all tools in its confidence table.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T857922074"] = "Diese Einstellung ist immer gesperrt und gilt für das gesamte Werkzeug. Das Konfigurations-Plugin sperrt die minimalen Vertrauensniveaus für Anbieter gemeinsam für alle Werkzeuge in seiner Tabelle."
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T900713019"] = "Abbrechen"
-- Current requirement: {0}
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T982466527"] = "Aktuelle Anforderung: {0}"
-- Save
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Speichern"
@ -11418,6 +11481,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::HARNESS::TOOLCALLINGMESSAGE
-- The tool calling request failed with status code {0}. See the logs for details.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::HARNESS::TOOLCALLINGMESSAGES::T3117779001"] = "Die Anfrage zum Aufruf des Werkzeugs ist mit dem Statuscode {0} fehlgeschlagen. Weitere Details finden Sie in den Protokollen."
-- General
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T1432485131"] = "Allgemein"
-- Tool
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T3517012711"] = "Werkzeug"
@ -11676,6 +11742,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSOPTIONSOURCES::
-- Any language
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSOPTIONSOURCES::T747012729"] = "Beliebige Sprache"
-- The tool's minimum provider confidence level is invalid.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T2093219126"] = "Das minimale Vertrauensniveau für Anbieter dieses Werkzeugs ist ungültig."
-- Cannot export encrypted tool secrets: No enterprise encryption secret is configured.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T3174877792"] = "Verschlüsselte Geheimnisse des Werkzeugs können nicht exportiert werden: Es ist kein Geheimnis für die Verschlüsselung konfiguriert."
-- The tool secrets could not be encrypted. Nothing was exported.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T403101133"] = "Die Geheimnisse des Werkzeugs konnten nicht verschlüsselt werden. Es wurde nichts exportiert."
-- The file path is null or empty and the file therefore can not be loaded.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::USERFILE::T932243993"] = "Der Dateipfad ist leer, daher kann die Datei nicht geladen werden."

View File

@ -3336,6 +3336,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The lo
-- The image at the URL is too large (>10 MB). Skipping the image.
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image."
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ADMINEXPORTBUTTON::T975426229"] = "Export configuration"
-- Open Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
@ -4608,9 +4611,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T80509
-- Example text to embed
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T816748904"] = "Example text to embed"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T975426229"] = "Export configuration"
-- Cannot export the encrypted API key: No enterprise encryption secret is configured.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T1832230847"] = "Cannot export the encrypted API key: No enterprise encryption secret is configured."
@ -4683,9 +4683,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T579100
-- Open Dashboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T78223861"] = "Open Dashboard"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T975426229"] = "Export configuration"
-- Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1258653480"] = "Settings"
@ -4782,9 +4779,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T78
-- Are you sure you want to delete the transcription provider '{0}'?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T789660305"] = "Are you sure you want to delete the transcription provider '{0}'?"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T975426229"] = "Export configuration"
-- Copy {0} to the clipboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TEXTINFOLINE::T2206391442"] = "Copy {0} to the clipboard"
@ -7464,9 +7458,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T782820
-- Local Directory
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T926703547"] = "Local Directory"
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T975426229"] = "Export configuration"
-- When enabled, you can preselect some ERI server options.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGERISERVER::T1280666275"] = "When enabled, you can preselect some ERI server options."
@ -7758,9 +7749,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T55364659"
-- Are you a project manager in a research facility? You might want to create a profile for your project management activities, one for your scientific work, and a profile for when you need to write program code. In these profiles, you can record how much experience you have or which methods you like or dislike using. Later, you can choose when and where you want to use each profile.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T56359901"] = "Are you a project manager in a research facility? You might want to create a profile for your project management activities, one for your scientific work, and a profile for when you need to write program code. In these profiles, you can record how much experience you have or which methods you like or dislike using. Later, you can choose when and where you want to use each profile."
-- Export configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T975426229"] = "Export configuration"
-- Preselect the target language
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROMPTOPTIMIZER::T1417990312"] = "Preselect the target language"
@ -8220,6 +8208,81 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T403490413"] =
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Cancel"
-- The tool configuration could not be exported. Please try again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1064444653"] = "The tool configuration could not be exported. Please try again."
-- The selected areas contain no configured API keys or other secrets.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1362677286"] = "The selected areas contain no configured API keys or other secrets."
-- Loading tool configuration...
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1750745869"] = "Loading tool configuration..."
-- Select all
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1794248818"] = "Select all"
-- Include minimum provider confidence
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1823629028"] = "Include minimum provider confidence"
-- The selected areas contain no settings to export.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T197560416"] = "The selected areas contain no settings to export."
-- Include encrypted API keys and other secrets
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T1978141571"] = "Include encrypted API keys and other secrets"
-- Settings to include
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2051465617"] = "Settings to include"
-- Editable defaults
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2389486789"] = "Editable defaults"
-- {0} of the selected settings are empty and are exported as empty locked values. Users cannot change a locked setting, so an empty required one leaves the tool unusable. Deselect the areas you have not configured, or export them as editable defaults.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2555293033"] = "{0} of the selected settings are empty and are exported as empty locked values. Users cannot change a locked setting, so an empty required one leaves the tool unusable. Deselect the areas you have not configured, or export them as editable defaults."
-- No minimum confidence level chosen
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T2828607242"] = "No minimum confidence level chosen"
-- Secrets are always exported as locked settings. Recipients need the same enterprise encryption secret to use them.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3001812876"] = "Secrets are always exported as locked settings. Recipients need the same enterprise encryption secret to use them."
-- The tool configuration could not be loaded. Please close this dialog and try again.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3388093684"] = "The tool configuration could not be loaded. Please close this dialog and try again."
-- Export tool configuration
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3758205437"] = "Export tool configuration"
-- Export mode
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3810275878"] = "Export mode"
-- The selected tool could not be loaded.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3907843187"] = "The selected tool could not be loaded."
-- Each area is independent. Select general settings separately if you want to include them.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T3911375461"] = "Each area is independent. Select general settings separately if you want to include them."
-- This choice applies to settings other than secrets and the minimum provider confidence.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T4236004495"] = "This choice applies to settings other than secrets and the minimum provider confidence."
-- Export to clipboard
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T508399334"] = "Export to clipboard"
-- No enterprise encryption secret is configured. API keys and other secrets cannot be exported.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T633982489"] = "No enterprise encryption secret is configured. API keys and other secrets cannot be exported."
-- Locked settings
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T651584564"] = "Locked settings"
-- Export saved settings as Lua code for your configuration plugin. You can combine exports and adapt the code before deploying it.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T744840132"] = "Export saved settings as Lua code for your configuration plugin. You can combine exports and adapt the code before deploying it."
-- This setting is always locked and applies to the entire tool. The configuration plugin locks minimum provider confidence levels together for all tools in its confidence table.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T857922074"] = "This setting is always locked and applies to the entire tool. The configuration plugin locks minimum provider confidence levels together for all tools in its confidence table."
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T900713019"] = "Cancel"
-- Current requirement: {0}
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSEXPORTDIALOG::T982466527"] = "Current requirement: {0}"
-- Save
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Save"
@ -11418,6 +11481,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::HARNESS::TOOLCALLINGMESSAGE
-- The tool calling request failed with status code {0}. See the logs for details.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::HARNESS::TOOLCALLINGMESSAGES::T3117779001"] = "The tool calling request failed with status code {0}. See the logs for details."
-- General
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T1432485131"] = "General"
-- Tool
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T3517012711"] = "Tool"
@ -11676,6 +11742,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSOPTIONSOURCES::
-- Any language
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSOPTIONSOURCES::T747012729"] = "Any language"
-- The tool's minimum provider confidence level is invalid.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T2093219126"] = "The tool's minimum provider confidence level is invalid."
-- Cannot export encrypted tool secrets: No enterprise encryption secret is configured.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T3174877792"] = "Cannot export encrypted tool secrets: No enterprise encryption secret is configured."
-- The tool secrets could not be encrypted. Nothing was exported.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLSETTINGSSERVICE::T403101133"] = "The tool secrets could not be encrypted. Nothing was exported."
-- The file path is null or empty and the file therefore can not be loaded.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::USERFILE::T932243993"] = "The file path is null or empty and the file therefore can not be loaded."

View File

@ -0,0 +1,9 @@
namespace AIStudio.Tools.ToolCallingSystem;
/// <summary>
/// One independently selectable area of a tool's configuration export.
/// </summary>
/// <param name="Id">A stable ID, independent of the translated label. The empty ID denotes ungrouped settings.</param>
/// <param name="Label">The translated name shown to the administrator.</param>
/// <param name="FieldNames">Settings schema field names, without the tool ID prefix.</param>
public sealed record ExportableSettings(string Id, string Label, IReadOnlyList<string> FieldNames);

View File

@ -64,6 +64,29 @@ public interface IToolImplementation
/// </summary>
public IReadOnlyList<ToolSettingsGroupLink> GetSettingsGroupLinks(string groupKey) => [];
/// <summary>
/// Independently selectable areas of this tool's configuration export.
/// </summary>
/// <remarks>
/// By default, each settings group is one area, including an area for ungrouped fields.
/// Override this when the export needs a different partition. IDs must be unique and stable;
/// labels must be translated. Areas contain schema field names, never values or secrets.
/// Selecting an area does not implicitly include general settings or other areas, and a
/// field hidden in the settings dialog is still exportable.
/// </remarks>
public IReadOnlyList<ExportableSettings> GetExportableSettings(ToolDefinition definition) => definition.SettingsSchema.Properties
.GroupBy(property => property.Value.Group, StringComparer.Ordinal)
.Select(group =>
{
var label = this.GetSettingsGroupLabel(group.Key);
return new ExportableSettings(
group.Key,
string.IsNullOrEmpty(label) ? TB("General") : label,
group.Select(property => property.Key).ToList()
);
})
.ToList();
/// <summary>
/// Whether one settings field is worth showing, given what is filled in at the moment.
/// </summary>

View File

@ -0,0 +1,25 @@
namespace AIStudio.Tools.ToolCallingSystem;
/// <summary>
/// How firmly an exported tool setting applies to the people who receive the configuration plugin.
/// </summary>
/// <remarks>
/// Chosen per export, and it covers the ordinary settings only. A secret is always locked, no
/// matter which mode is picked, because a pre-filled secret is one the user may save as their
/// own — see the tool settings service for that rule. The minimum provider confidence is
/// likewise a fixed requirement.
/// </remarks>
public enum ToolSettingsExportMode
{
/// <summary>
/// The organization fixes the value: it goes into LockedToolSettings, the user cannot change
/// it, and it is reapplied on every configuration update.
/// </summary>
LOCKED,
/// <summary>
/// The organization pre-fills the value: it goes into DefaultToolSettings, and a value the
/// user saves afterwards wins over it.
/// </summary>
DEFAULT,
}

View File

@ -0,0 +1,15 @@
namespace AIStudio.Tools.ToolCallingSystem;
/// <summary>
/// The administrator's choices for one export. The dialog initially selects every available area.
/// </summary>
public sealed record ToolSettingsExportOptions
{
public IReadOnlySet<string> SelectedAreaIds { get; init; } = new HashSet<string>(StringComparer.Ordinal);
public ToolSettingsExportMode Mode { get; init; } = ToolSettingsExportMode.LOCKED;
public bool IncludeSecrets { get; init; }
public bool IncludeMinimumProviderConfidence { get; init; } = true;
}

View File

@ -0,0 +1,9 @@
namespace AIStudio.Tools.ToolCallingSystem;
/// <summary>
/// Lua to copy, or an explanation of why the export failed. An empty successful export has nothing to copy.
/// </summary>
public sealed record ToolSettingsExportResult(string LuaCode = "", string ErrorMessage = "")
{
public bool Success => string.IsNullOrEmpty(this.ErrorMessage);
}

View File

@ -0,0 +1,133 @@
using System.Text;
using AIStudio.Provider;
using AIStudio.Tools.PluginSystem;
using SharedTools;
namespace AIStudio.Tools.ToolCallingSystem;
public sealed partial class ToolSettingsService
{
private const string LOCKED_SETTINGS = "DataTools.LockedToolSettings";
private const string DEFAULT_SETTINGS = "DataTools.DefaultToolSettings";
private const string MINIMUM_CONFIDENCE = "DataTools.MinimumProviderConfidenceByToolId";
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ToolSettingsService).Namespace, nameof(ToolSettingsService));
/// <summary>
/// Reads the saved, effective configuration and exports the selected areas. Incomplete tools
/// may be exported too: administrators can finish the configuration in their Lua plugin.
/// </summary>
/// <remarks>
/// Uses the same organization overrides and keyring values as tool execution, without saving
/// settings or writing to the keyring. The caller provides the admin-only UI and copies a
/// successful, nonempty result to the clipboard.<br/><br/>
/// Only explicitly selected areas are included. Missing values stay absent, explicitly empty
/// non-secret values stay empty, and runtime defaults are not filled in. Secrets require
/// opt-in and enterprise encryption, and are always locked, even in a default-value export.
/// The optional minimum provider confidence is also always a fixed requirement.
/// </remarks>
public async Task<ToolSettingsExportResult> ExportAsync(ToolDefinition definition, IToolImplementation implementation, ToolSettingsExportOptions options)
{
var areas = implementation.GetExportableSettings(definition);
var values = await this.GetSettingsAsync(definition);
var confidence = settingsManager.GetMinimumProviderConfidenceForTool(definition.Id, definition.MinimumProviderConfidence);
return BuildConfigurationSection(definition, areas, values, options, confidence, PluginFactory.EnterpriseEncryption);
}
/// <summary>
/// Resolves selected areas to known fields in schema order. Overlapping areas include a
/// field only once; unknown field names are ignored. Form visibility does not limit exports.
/// </summary>
private static IReadOnlyList<string> GetSelectedFieldNames(ToolDefinition definition, IReadOnlyList<ExportableSettings> areas, IReadOnlySet<string> selectedAreaIds)
{
var selectedIds = new HashSet<string>(selectedAreaIds, StringComparer.Ordinal);
var selectedFields = areas.Where(area => selectedIds.Contains(area.Id))
.SelectMany(area => area.FieldNames)
.ToHashSet(StringComparer.Ordinal);
return definition.SettingsSchema.Properties.Keys.Where(selectedFields.Contains).ToList();
}
/// <summary>
/// Builds a fragment from one snapshot. A failed encryption returns no Lua, even when other
/// fields have already been processed, so the caller cannot copy a partial export by accident.
/// </summary>
private static ToolSettingsExportResult BuildConfigurationSection(ToolDefinition definition, IReadOnlyList<ExportableSettings> areas, IReadOnlyDictionary<string, string> values, ToolSettingsExportOptions options, ConfidenceLevel minimumProviderConfidence, EnterpriseEncryption? encryption)
{
var lockedValues = new Dictionary<string, string>(StringComparer.Ordinal);
var defaultValues = new Dictionary<string, string>(StringComparer.Ordinal);
foreach (var fieldName in GetSelectedFieldNames(definition, areas, options.SelectedAreaIds))
{
if (!values.TryGetValue(fieldName, out var value))
continue;
var key = ManagedSettingKey(definition.Id, fieldName);
if (definition.SettingsSchema.Properties[fieldName].Secret)
{
if (!options.IncludeSecrets || string.IsNullOrWhiteSpace(value))
continue;
if (encryption?.IsAvailable is not true)
return new(ErrorMessage: TB("Cannot export encrypted tool secrets: No enterprise encryption secret is configured."));
if (!encryption.TryEncrypt(value, out var encrypted))
return new(ErrorMessage: TB("The tool secrets could not be encrypted. Nothing was exported."));
lockedValues[key] = encrypted;
}
else if (options.Mode is ToolSettingsExportMode.LOCKED)
lockedValues[key] = value;
else
defaultValues[key] = value;
}
if (lockedValues.Count is 0 && defaultValues.Count is 0 && !options.IncludeMinimumProviderConfidence)
return new();
if (options.IncludeMinimumProviderConfidence && (!Enum.IsDefined(minimumProviderConfidence) || minimumProviderConfidence is ConfidenceLevel.UNKNOWN))
return new(ErrorMessage: TB("The tool's minimum provider confidence level is invalid."));
var lua = new StringBuilder();
AppendSettings(lua, LOCKED_SETTINGS, lockedValues);
AppendSettings(lua, DEFAULT_SETTINGS, defaultValues);
if (options.IncludeMinimumProviderConfidence)
{
AppendSettings(lua, MINIMUM_CONFIDENCE, new Dictionary<string, string>(StringComparer.Ordinal)
{
[definition.Id] = minimumProviderConfidence.ToString(),
});
//
// A managed setting without an AllowUserOverride flag is locked anyway, so writing
// "= false" here would only restate the default — and would silently undo an
// administrator's own "= true" further up in the same plugin, for the whole
// dictionary rather than this tool's entry. A comment says the same thing without
// overwriting anything:
//
lua.AppendLine($"-- The whole table is locked unless you set CONFIG[\"SETTINGS\"][\"{MINIMUM_CONFIDENCE}.AllowUserOverride\"] = true");
}
return new(LuaCode: lua.ToString());
}
/// <summary>
/// Adds entries without replacing the table, so administrators can combine export fragments
/// in one plugin. Later assignments to the same key win. This does not merge dictionaries
/// across separate configuration plugins; those still follow managed-setting precedence.
/// </summary>
private static void AppendSettings(StringBuilder lua, string settingName, IReadOnlyDictionary<string, string> values)
{
if (values.Count is 0)
return;
var table = $"CONFIG[\"SETTINGS\"][\"{settingName}\"]";
if (lua.Length > 0)
lua.AppendLine();
lua.AppendLine($"{table} = {table} or {{}}");
foreach (var (key, value) in values)
lua.AppendLine($"{table}[\"{LuaTools.EscapeLuaString(key)}\"] = \"{LuaTools.EscapeLuaString(value)}\"");
}
}

View File

@ -4,7 +4,7 @@ using AIStudio.Tools.Services;
namespace AIStudio.Tools.ToolCallingSystem;
public sealed class ToolSettingsService(SettingsManager settingsManager, RustService rustService, ILogger<ToolSettingsService> logger)
public sealed partial class ToolSettingsService(SettingsManager settingsManager, RustService rustService, ILogger<ToolSettingsService> logger)
{
/// <summary>
/// Builds the key under which an organization's configuration addresses one tool setting.
@ -202,4 +202,4 @@ public sealed class ToolSettingsService(SettingsManager settingsManager, RustSer
secret = decryptedSecret;
return true;
}
}
}

View File

@ -6,6 +6,6 @@
- Added tools to the policies of the Document Analysis assistant. A policy states which tools an analysis may use, and the AI uses exactly those — nobody has to pick them per document. AI Studio warns you beforehand when the provider you selected is not trusted enough for a tool the policy names. IT departments can roll policies out together with their tools.
- Added tools to assistant plugins and direct-chat launchers. Plugin authors name them in the new `ToolIds` field, either as the tools an assistant runs with or as the tools a launcher preselects for the chat it opens; the example assistant plugin shows both. Which tools an assistant asks for is part of what you get to see before you enable it: its security card names them, and the security audit takes them into account.
- Added tools to the Assistant Builder. For a direct-chat launcher you pick them yourself, alongside the workspace, provider, and data sources. For an assistant, the AI chooses from the tools installed here and says so in the draft, so you see the decision before the assistant is written.
- Added organization-wide management for tools. IT departments can switch tools off entirely (`DataTools.EnableTools`), disable individual ones (`DataTools.DisabledToolIds`), raise the provider trust a tool requires (`DataTools.MinimumProviderConfidenceByToolId`), and manage every tool setting by tool and field name — either fixed (`DataTools.LockedToolSettings`) or as a pre-filled value the user may still change (`DataTools.DefaultToolSettings`). None of these needs to be known to AI Studio in advance, so it covers the tools future plugins will bring just as well. Secrets such as API keys can be rolled out too, encrypted with your enterprise encryption secret, the same way you already deploy provider keys. The example configuration plugin documents every setting of both tools.
- Added organization-wide management for tools. Among other options, IT departments can switch tools off entirely, disable individual ones, or define the provider trust a tool requires. You do not have to write any of it by hand: set a tool up in the app, then export its configuration as ready-made Lua code for your plugin, with encrypted API keys if you want them.
- Added tool calling to the abilities you can state yourself in the expert provider settings. When you use a model AI Studio does not recognize as tool-capable, you can now declare that it is, the same way you already could for image input or reasoning.
- Improved loading web content in the assistants: it now uses the same reader as the Read Web Page tool, which extracts the main content of a page more reliably and skips navigation and boilerplate. Pages from your own network, including local servers, keep working as before. When a page cannot be read, AI Studio now says why instead of leaving the field empty.

View File

@ -425,6 +425,7 @@ Currently, you can configure the following things:
- Any number of transcription providers for voice-to-text functionality
- Any number of embedding providers for RAG
- Enterprise hash approvals for assistant plugins
- Tool settings, encrypted tool API keys, and minimum provider confidence requirements
- The update behavior of AI Studio
- Various UI and feature settings (see the example configuration for details)
@ -640,6 +641,50 @@ CONFIG["LLM_PROVIDERS"][#CONFIG["LLM_PROVIDERS"]+1] = {
The API key will be automatically decrypted when the configuration is loaded and stored securely in the operating system's credential store (Windows Credential Manager / macOS Keychain).
## Exporting tool configurations
Start from the [example configuration plugin](../app/MindWork%20AI%20Studio/Plugins/configuration/plugin.lua). The export produces a Lua fragment to insert into that file; it assumes `CONFIG` and `CONFIG["SETTINGS"]` already exist.
1. Enable **Show administration settings** in the app settings. In **Tool Settings**, configure the tool and save your changes, then click its **Export configuration** button next to the settings button.
2. Select the areas to export. All areas start selected. For Web Search, SearXNG, Staan, Tavily, and General are independent: selecting only Tavily does not include the search language, strategy, or preferred backend. Select General separately when you need those settings.
3. Choose **Locked settings** or **Editable defaults**. Locked settings go into `DataTools.LockedToolSettings` and cannot be changed by users. Editable defaults go into `DataTools.DefaultToolSettings`; a user's saved value takes precedence over them.
4. Optionally select **Include encrypted API keys and other secrets**, which starts off. The option is available only when the selected areas contain configured secrets and this machine has a valid enterprise encryption secret. Deploy the same secret to recipients as described in [Setting Up Encrypted API Keys](#setting-up-encrypted-api-keys). Secrets always go into `LockedToolSettings`, including when you choose editable defaults for the other fields. Managed tool secrets are used from the configuration without replacing the user's own keyring entries; removing the managed secret makes the user's own key available again.
5. Review **Include minimum provider confidence**, which starts on. The exported requirement applies to the whole tool and is locked, because a managed setting without an `AllowUserOverride` flag is locked by default. The export therefore only adds a comment about that flag instead of writing it: setting it applies to the entire confidence table, including entries for other tools, so that decision stays yours. Deselect this option if your fragment should not configure provider confidence.
6. Click **Export to clipboard**, then paste the fragment into your plugin after its `CONFIG["SETTINGS"] = {}` initialization and after any assignments that replace the tables you want to extend. Review the code and test the plugin using [Local staging and testing](#local-staging-and-testing) before rollout. The export dialog stays open so you can produce another selection.
The export reads saved, effective settings, including organization-managed values. It does not save settings or change the keyring. Missing values are omitted, explicitly empty non-secret values are preserved, and implicit runtime defaults are not added. Incomplete configurations can be exported so that you can finish them in Lua. If encryption fails, no partial fragment is copied; an empty export also leaves the clipboard unchanged.
### Complete tool export
For example, save a timeout of `30`, a content limit of `12000`, and an empty private-host list for **Read Web Page**. Select its General area, **Locked settings**, and **Include minimum provider confidence**. With its default confidence requirement of `VERY_LOW`, the export is:
```lua
CONFIG["SETTINGS"]["DataTools.LockedToolSettings"] = CONFIG["SETTINGS"]["DataTools.LockedToolSettings"] or {}
CONFIG["SETTINGS"]["DataTools.LockedToolSettings"]["read_web_page.timeoutSeconds"] = "30"
CONFIG["SETTINGS"]["DataTools.LockedToolSettings"]["read_web_page.maxContentCharacters"] = "12000"
CONFIG["SETTINGS"]["DataTools.LockedToolSettings"]["read_web_page.allowedPrivateHosts"] = ""
CONFIG["SETTINGS"]["DataTools.MinimumProviderConfidenceByToolId"] = CONFIG["SETTINGS"]["DataTools.MinimumProviderConfidenceByToolId"] or {}
CONFIG["SETTINGS"]["DataTools.MinimumProviderConfidenceByToolId"]["read_web_page"] = "VERY_LOW"
-- The whole table is locked unless you set CONFIG["SETTINGS"]["DataTools.MinimumProviderConfidenceByToolId.AllowUserOverride"] = true
```
### Exporting only one search backend
For **Web Search**, select only Tavily, choose **Editable defaults**, enable encrypted secrets, and deselect **Include minimum provider confidence**. With a saved search depth of `basic` and an API key, the fragment has this form. The ciphertext below is a placeholder; use the encrypted value generated by your export.
```lua
CONFIG["SETTINGS"]["DataTools.LockedToolSettings"] = CONFIG["SETTINGS"]["DataTools.LockedToolSettings"] or {}
CONFIG["SETTINGS"]["DataTools.LockedToolSettings"]["web_search.tavily.apiKey"] = "ENC:v1:<base64-encoded encrypted data>"
CONFIG["SETTINGS"]["DataTools.DefaultToolSettings"] = CONFIG["SETTINGS"]["DataTools.DefaultToolSettings"] or {}
CONFIG["SETTINGS"]["DataTools.DefaultToolSettings"]["web_search.tavily.searchDepth"] = "basic"
```
This does not change the SearXNG or Staan settings, the general Web Search settings, or its confidence requirement. Web Search still needs a configured `defaultLanguage`; include it through a separate General-area export, add it manually, or let the user configure it. A backend-only export does not restrict the tool to that backend.
You can combine both fragments in the same plugin: their table initializations preserve earlier entries, and only a later assignment to an identical key replaces its value. A later whole-table assignment such as `CONFIG["SETTINGS"]["DataTools.LockedToolSettings"] = { ... }` replaces those entries, so place exports after it or merge them manually. This behavior applies within one plugin; across separate configuration plugins, the winning plugin replaces the whole managed table as described in [Settings that hold a list or a table](#settings-that-hold-a-list-or-a-table).
## Letting users provide their own API key
Sometimes you want to hand out a preconfigured provider -- a fixed host, model, and instance name