mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 18:53:48 +00:00
Added assistant plugins (#659)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
1 parent
a573409a24
commit
f6a128f2e4
100 files changed
+10146
-85
No files matched your search
@@ -1,6 +1,7 @@
|
||||
@attribute [Route(Routes.ASSISTANTS)]
|
||||
@using AIStudio.Dialogs.Settings
|
||||
@using AIStudio.Settings.DataModel
|
||||
@attribute [Route(Routes.ASSISTANTS)]
|
||||
@using AIStudio.Tools.PluginSystem.Assistants
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<div class="inner-scrolling-context">
|
||||
@@ -30,6 +31,29 @@
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@if (this.AssistantPlugins.Count > 0)
|
||||
{
|
||||
<MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6">
|
||||
@T("Installed Assistants")
|
||||
</MudText>
|
||||
<MudStack Row="@true" Wrap="@Wrap.Wrap" Class="mb-3">
|
||||
@foreach (var assistantPlugin in this.AssistantPlugins)
|
||||
{
|
||||
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
|
||||
<AssistantBlock TSettings="NoSettingsPanel"
|
||||
Name="@T(assistantPlugin.AssistantTitle)"
|
||||
Description="@T(assistantPlugin.Description)"
|
||||
Icon="@Icons.Material.Filled.FindInPage"
|
||||
Disabled="@(!securityState.CanStartAssistant)"
|
||||
Link="@($"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}")">
|
||||
<SecurityBadge>
|
||||
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true" />
|
||||
</SecurityBadge>
|
||||
</AssistantBlock>
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@if (this.SettingsManager.IsAnyCategoryAssistantVisible("Business",
|
||||
(Components.EMAIL_ASSISTANT, PreviewFeatures.NONE),
|
||||
(Components.DOCUMENT_ANALYSIS_ASSISTANT, PreviewFeatures.NONE),
|
||||
|
||||
@@ -1,5 +1,92 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Agents.AssistantAudit;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Pages;
|
||||
|
||||
public partial class Assistants : MSGComponentBase;
|
||||
public partial class Assistants : MSGComponentBase
|
||||
{
|
||||
private bool isAutoAuditing;
|
||||
|
||||
[Inject]
|
||||
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
await this.TryAutoAuditAssistantsAsync();
|
||||
}
|
||||
|
||||
private IReadOnlyCollection<PluginAssistants> AssistantPlugins =>
|
||||
PluginFactory.RunningPlugins.OfType<PluginAssistants>()
|
||||
.Where(plugin => this.SettingsManager.IsPluginEnabled(plugin))
|
||||
.ToList();
|
||||
|
||||
private async Task TryAutoAuditAssistantsAsync()
|
||||
{
|
||||
if (this.isAutoAuditing || !this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants)
|
||||
return;
|
||||
|
||||
this.isAutoAuditing = true;
|
||||
|
||||
try
|
||||
{
|
||||
var wasConfigurationChanged = false;
|
||||
var assistantPlugins = PluginFactory.RunningPlugins.OfType<PluginAssistants>().ToList();
|
||||
foreach (var assistantPlugin in assistantPlugins)
|
||||
{
|
||||
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
|
||||
if (!securityState.RequiresAudit)
|
||||
continue;
|
||||
|
||||
var audit = await this.AssistantPluginAuditService.RunAuditAsync(assistantPlugin);
|
||||
if (audit.Level is AssistantAuditLevel.UNKNOWN)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(this.T("The automatic security audit for the assistant plugin '{0}' failed. Please run it manually from the plugins page."), assistantPlugin.Name)));
|
||||
continue;
|
||||
}
|
||||
|
||||
this.UpsertAuditCard(audit);
|
||||
wasConfigurationChanged = true;
|
||||
}
|
||||
|
||||
if (!wasConfigurationChanged)
|
||||
return;
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isAutoAuditing = false;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpsertAuditCard(PluginAssistantAudit audit)
|
||||
{
|
||||
var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits;
|
||||
var existingIndex = audits.FindIndex(x => x.PluginId == audit.PluginId);
|
||||
if (existingIndex >= 0)
|
||||
audits[existingIndex] = audit;
|
||||
else
|
||||
audits.Add(audit);
|
||||
}
|
||||
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
if (triggeredEvent is Event.PLUGINS_RELOADED)
|
||||
await this.TryAutoAuditAssistantsAsync();
|
||||
|
||||
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@using AIStudio.Tools.PluginSystem
|
||||
@using AIStudio.Tools.PluginSystem.Assistants
|
||||
@inherits MSGComponentBase
|
||||
@attribute [Route(Routes.PLUGINS)]
|
||||
|
||||
@@ -64,19 +65,25 @@
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||
@if (context.Type is PluginType.ASSISTANT)
|
||||
{
|
||||
var assistantPlugin = PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == context.Id);
|
||||
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true" />
|
||||
}
|
||||
@if (context is { IsInternal: false, Type: not PluginType.CONFIGURATION })
|
||||
{
|
||||
var isEnabled = this.SettingsManager.IsPluginEnabled(context);
|
||||
<MudTooltip Text="@(isEnabled ? T("Disable plugin") : T("Enable plugin"))">
|
||||
<MudSwitch T="bool" Value="@isEnabled" ValueChanged="@(_ => this.PluginActivationStateChanged(context))"/>
|
||||
var activationSwitchDisabled = this.IsActivationSwitchDisabled(context, isEnabled);
|
||||
<MudTooltip Text="@this.GetActivationTooltip(context, isEnabled)">
|
||||
<MudSwitch T="bool" Value="@isEnabled" ValueChanged="@(_ => this.PluginActivationStateChanged(context))" Disabled="@activationSwitchDisabled"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
|
||||
|
||||
@if (context is { IsInternal: false } && !string.IsNullOrWhiteSpace(context.SourceURL))
|
||||
{
|
||||
var sourceUrl = context.SourceURL;
|
||||
var isSendingMail = IsSendingMail(sourceUrl);
|
||||
if(isSendingMail)
|
||||
if (isSendingMail)
|
||||
{
|
||||
<MudTooltip Text="@T("Send a mail")">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Email" Href="@sourceUrl" Target="_blank" Size="Size.Medium"/>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Agents.AssistantAudit;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Pages;
|
||||
|
||||
@@ -10,9 +15,18 @@ public partial class Plugins : MSGComponentBase
|
||||
private const string GROUP_ENABLED = "Enabled";
|
||||
private const string GROUP_DISABLED = "Disabled";
|
||||
private const string GROUP_INTERNAL = "Internal";
|
||||
private bool isAutoAuditing;
|
||||
|
||||
private DataAssistantPluginAudit AssistantPluginAuditSettings => this.SettingsManager.ConfigurationData.AssistantPluginAudit;
|
||||
|
||||
private TableGroupDefinition<IPluginMetadata> groupConfig = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -37,21 +51,192 @@ public partial class Plugins : MSGComponentBase
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
await this.TryAutoAuditAssistantsAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task PluginActivationStateChanged(IPluginMetadata pluginMeta)
|
||||
{
|
||||
if (this.SettingsManager.IsPluginEnabled(pluginMeta))
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.EnabledPlugins.Remove(pluginMeta.Id);
|
||||
else
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pluginMeta.Type is not PluginType.ASSISTANT)
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id);
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
return;
|
||||
}
|
||||
|
||||
var assistantPlugin = PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == pluginMeta.Id);
|
||||
if (assistantPlugin is null)
|
||||
return;
|
||||
|
||||
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
|
||||
if (securityState.RequiresAudit)
|
||||
{
|
||||
await this.OpenAssistantAuditDialogAsync(pluginMeta.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (securityState.IsBelowMinimum && securityState.IsBlocked)
|
||||
{
|
||||
var blockedAudit = securityState.Audit;
|
||||
if (blockedAudit is not null)
|
||||
await this.DialogService.ShowMessageBox(this.T("Assistant Audit"), $"{blockedAudit.Level.GetName()}: {blockedAudit.Summary}", this.T("Close"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (securityState.IsBelowMinimum && securityState.CanOverride &&
|
||||
!await this.ConfirmActivationBelowMinimumAsync(pluginMeta.Name, securityState.Audit!.Level))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task OpenAssistantAuditDialogAsync(Guid pluginId)
|
||||
{
|
||||
var parameters = new DialogParameters<AssistantPluginAuditDialog>
|
||||
{
|
||||
{ x => x.PluginId, pluginId },
|
||||
};
|
||||
var dialog = await this.DialogService.ShowAsync<AssistantPluginAuditDialog>(this.T("Assistant Audit"), parameters, DialogOptions.FULLSCREEN);
|
||||
var result = await dialog.Result;
|
||||
if (result is null || result.Canceled || result.Data is not AssistantPluginAuditDialogResult auditResult)
|
||||
return;
|
||||
|
||||
if (auditResult.Audit is not null)
|
||||
this.UpsertAuditCard(auditResult.Audit);
|
||||
|
||||
if (auditResult.ActivatePlugin)
|
||||
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginId);
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task<bool> ConfirmActivationBelowMinimumAsync(string pluginName, AssistantAuditLevel actualLevel)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{
|
||||
x => x.Message,
|
||||
string.Format(
|
||||
this.T("The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"),
|
||||
pluginName,
|
||||
actualLevel.GetName(),
|
||||
this.AssistantPluginAuditSettings.MinimumLevel.GetName())
|
||||
},
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(this.T("Potentially Dangerous Plugin"), dialogParameters,
|
||||
DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
return dialogResult is not null && !dialogResult.Canceled;
|
||||
}
|
||||
|
||||
private bool IsActivationSwitchDisabled(IPluginMetadata pluginMeta, bool isEnabled)
|
||||
{
|
||||
if (isEnabled || pluginMeta.Type is not PluginType.ASSISTANT)
|
||||
return false;
|
||||
|
||||
var assistantPlugin = this.TryGetAssistantPlugin(pluginMeta.Id);
|
||||
if (assistantPlugin is null)
|
||||
return false;
|
||||
|
||||
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
|
||||
return securityState.IsBlocked && !securityState.RequiresAudit;
|
||||
}
|
||||
|
||||
private string GetActivationTooltip(IPluginMetadata pluginMeta, bool isEnabled)
|
||||
{
|
||||
if (isEnabled)
|
||||
return this.T("Disable plugin");
|
||||
|
||||
if (pluginMeta.Type is not PluginType.ASSISTANT)
|
||||
return this.T("Enable plugin");
|
||||
|
||||
var assistantPlugin = this.TryGetAssistantPlugin(pluginMeta.Id);
|
||||
if (assistantPlugin is null)
|
||||
return this.T("Enable plugin");
|
||||
|
||||
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
|
||||
if (securityState.RequiresAudit)
|
||||
return securityState.ActionLabel;
|
||||
|
||||
return securityState.IsBlocked
|
||||
? securityState.Description
|
||||
: this.T("Enable plugin");
|
||||
}
|
||||
|
||||
private static bool IsSendingMail(string sourceUrl) => sourceUrl.TrimStart().StartsWith("mailto:", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private PluginAssistants? TryGetAssistantPlugin(Guid pluginId) => PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == pluginId);
|
||||
|
||||
private async Task TryAutoAuditAssistantsAsync()
|
||||
{
|
||||
if (this.isAutoAuditing || !this.AssistantPluginAuditSettings.AutomaticallyAuditAssistants)
|
||||
return;
|
||||
|
||||
this.isAutoAuditing = true;
|
||||
|
||||
try
|
||||
{
|
||||
var wasConfigurationChanged = false;
|
||||
var assistantPlugins = PluginFactory.RunningPlugins.OfType<PluginAssistants>().ToList();
|
||||
foreach (var assistantPlugin in assistantPlugins)
|
||||
{
|
||||
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
|
||||
if (!securityState.RequiresAudit)
|
||||
continue;
|
||||
|
||||
var audit = await this.AssistantPluginAuditService.RunAuditAsync(assistantPlugin);
|
||||
if (audit.Level is AssistantAuditLevel.UNKNOWN)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(this.T("The automatic security audit for the assistant plugin '{0}' failed. Please run it manually."), assistantPlugin.Name)));
|
||||
continue;
|
||||
}
|
||||
|
||||
this.UpsertAuditCard(audit);
|
||||
wasConfigurationChanged = true;
|
||||
}
|
||||
|
||||
if (!wasConfigurationChanged)
|
||||
return;
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isAutoAuditing = false;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpsertAuditCard(PluginAssistantAudit audit)
|
||||
{
|
||||
var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits;
|
||||
var existingIndex = audits.FindIndex(x => x.PluginId == audit.PluginId);
|
||||
if (existingIndex >= 0)
|
||||
audits[existingIndex] = audit;
|
||||
else
|
||||
audits.Add(audit);
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
@@ -59,6 +244,11 @@ public partial class Plugins : MSGComponentBase
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.PLUGINS_RELOADED:
|
||||
await this.TryAutoAuditAssistantsAsync();
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
|
||||
case Event.CONFIGURATION_CHANGED:
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
}
|
||||
|
||||
<SettingsPanelAgentContentCleaner AvailableLLMProvidersFunc="@(() => this.availableLLMProviders)"/>
|
||||
<SettingsPanelAgentAssistantAudit AvailableLLMProvidersFunc="@(() => this.availableLLMProviders)"/>
|
||||
</MudExpansionPanels>
|
||||
</InnerScrolling>
|
||||
</div>
|
||||
</div>
|
||||
Reference in new issue
Block a user