mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-08 23:49:39 +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
@@ -0,0 +1,10 @@
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public sealed class AssistantAuditTreeItem : ITreeItem
|
||||
{
|
||||
public string Text { get; init; } = string.Empty;
|
||||
public string Icon { get; init; } = string.Empty;
|
||||
public string Caption { get; init; } = string.Empty;
|
||||
public bool Expandable { get; init; }
|
||||
public bool IsComponent { get; init; } = true;
|
||||
}
|
||||
@@ -22,15 +22,23 @@
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudButtonGroup Variant="Variant.Outlined">
|
||||
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link">
|
||||
@this.ButtonText
|
||||
</MudButton>
|
||||
@if (this.HasSettingsPanel)
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Style="width: 100%;">
|
||||
<MudButtonGroup Variant="Variant.Outlined">
|
||||
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link" Disabled="@this.Disabled">
|
||||
@this.ButtonText
|
||||
</MudButton>
|
||||
@if (this.HasSettingsPanel)
|
||||
{
|
||||
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" Color="Color.Default" OnClick="@this.OpenSettingsDialog"/>
|
||||
}
|
||||
</MudButtonGroup>
|
||||
@if (this.SecurityBadge is not null)
|
||||
{
|
||||
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" Color="Color.Default" OnClick="@this.OpenSettingsDialog"/>
|
||||
<MudElement>
|
||||
@this.SecurityBadge
|
||||
</MudElement>
|
||||
}
|
||||
</MudButtonGroup>
|
||||
</MudStack>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
using AIStudio.Settings.DataModel;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
@@ -24,6 +22,12 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
|
||||
[Parameter]
|
||||
public string Link { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? SecurityBadge { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Tools.Components Component { get; set; } = Tools.Components.NONE;
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
@using AIStudio.Agents.AssistantAudit
|
||||
@inherits MSGComponentBase
|
||||
|
||||
@if (this.Plugin is not null)
|
||||
{
|
||||
var state = this.SecurityState;
|
||||
|
||||
<div class="d-flex">
|
||||
<MudTooltip Text="@state.ActionLabel" Placement="Placement.Top">
|
||||
<MudIconButton Icon="@state.BadgeIcon"
|
||||
Color="@state.AuditColor"
|
||||
Size="@(this.Compact ? Size.Small : Size.Medium)"
|
||||
OnClick="@this.ToggleSecurityCard" />
|
||||
</MudTooltip>
|
||||
|
||||
<MudPopover Open="@this.showSecurityCard"
|
||||
AnchorOrigin="Origin.BottomRight"
|
||||
TransformOrigin="Origin.BottomLeft"
|
||||
OverflowBehavior="OverflowBehavior.FlipAlways"
|
||||
DropShadow="@true"
|
||||
Class="border-solid border-4 rounded-lg"
|
||||
Style="@this.GetPopoverStyle()">
|
||||
<MudCard Elevation="2" Outlined Style="max-width: min(42rem, 90vw);">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
<MudAvatar Color="@state.AuditColor" Variant="Variant.Filled" Size="Size.Large">
|
||||
<MudIcon Icon="@state.AuditIcon" Size="Size.Medium" />
|
||||
</MudAvatar>
|
||||
</CardHeaderAvatar>
|
||||
<CardHeaderContent>
|
||||
<div class="d-flex align-center gap-2">
|
||||
<MudText Typo="Typo.h6">@T("Assistant Security")</MudText>
|
||||
<MudChip T="string" Size="Size.Small" Variant="Variant.Filled" Color="@state.AuditColor">
|
||||
@state.AuditLabel
|
||||
</MudChip>
|
||||
@if (!string.IsNullOrWhiteSpace(state.AvailabilityLabel))
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined" Color="@state.AvailabilityColor" Icon="@state.AvailabilityIcon">
|
||||
@state.AvailabilityLabel
|
||||
</MudChip>
|
||||
}
|
||||
</div>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
@state.Headline
|
||||
</MudText>
|
||||
</CardHeaderContent>
|
||||
<CardHeaderActions>
|
||||
<MudTooltip Text="@T("Show or hide the detailed security information.")">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ExpandMore" OnClick="@this.ToggleDetails" />
|
||||
</MudTooltip>
|
||||
</CardHeaderActions>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent Class="pt-0 pb-2">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="4" Class="flex-wrap">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Speed" Size="Size.Small" />
|
||||
<MudText Typo="Typo.body2">@T("Confidence"):</MudText>
|
||||
<MudProgressLinear Color="@state.AuditColor"
|
||||
Value="@this.GetConfidencePercentage()"
|
||||
Rounded="@true"
|
||||
Size="Size.Medium"
|
||||
Style="width: 80px; min-width: 80px;" />
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">
|
||||
@this.GetConfidenceLabel()
|
||||
</MudText>
|
||||
</MudStack>
|
||||
<MudDivider Vertical="@true" FlexItem="@true" />
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.BugReport" Size="Size.Small" Color="@state.AuditColor" />
|
||||
<MudText Typo="Typo.body2">@this.GetFindingSummary()</MudText>
|
||||
</MudStack>
|
||||
<MudDivider Vertical="@true" FlexItem="@true" />
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Schedule" Size="Size.Small" />
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
@this.GetAuditTimestampLabel()
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
|
||||
<MudCollapse Expanded="@this.showDetails">
|
||||
<MudDivider />
|
||||
<MudCardContent>
|
||||
<MudStack Spacing="3">
|
||||
<MudAlert Severity="@this.GetStatusSeverity()" Variant="Variant.Outlined" Dense="@true">
|
||||
@state.Description
|
||||
</MudAlert>
|
||||
|
||||
<MudPaper Outlined="true" Class="pa-2">
|
||||
<div class="d-flex align-center justify-space-between gap-2">
|
||||
<MudText Typo="Typo.subtitle2">@T("Technical Details")</MudText>
|
||||
<MudIconButton Icon="@(this.showMetadata ? Icons.Material.Filled.ExpandLess : Icons.Material.Filled.ExpandMore)"
|
||||
Size="Size.Small"
|
||||
OnClick="@this.ToggleMetadata" />
|
||||
</div>
|
||||
<MudCollapse Expanded="@this.showMetadata">
|
||||
<MudSimpleTable Dense="@true" Hover="@true" Bordered="@true" Striped="@true" Style="overflow-x: auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 180px;">
|
||||
<MudText Typo="Typo.body2"><b>@T("Plugin ID")</b></MudText>
|
||||
</td>
|
||||
<td><code style="font-size: 0.8rem;">@this.Plugin.Id</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Current hash")</b></MudText>
|
||||
</td>
|
||||
<td><code style="font-size: 0.8rem;">@GetShortHash(state.CurrentHash)</code></td>
|
||||
</tr>
|
||||
@if (state.Audit is not null)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Audit hash")</b></MudText>
|
||||
</td>
|
||||
<td><code style="font-size: 0.8rem;">@GetShortHash(state.Audit.PluginHash)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Audit provider")</b></MudText>
|
||||
</td>
|
||||
<td><MudText Typo="Typo.body2">@this.GetAuditProviderLabel()</MudText></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Audited at")</b></MudText>
|
||||
</td>
|
||||
<td><MudText Typo="Typo.body2">@this.FormatFileTimestamp(state.Audit.AuditedAtUtc.ToLocalTime().DateTime)</MudText></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Audit level")</b></MudText>
|
||||
</td>
|
||||
<td><MudText Typo="Typo.body2">@state.AuditLabel</MudText></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Availability")</b></MudText>
|
||||
</td>
|
||||
<td><MudText Typo="Typo.body2">@state.AvailabilityLabel</MudText></td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2"><b>@T("Required minimum")</b></MudText>
|
||||
</td>
|
||||
<td><MudText Typo="Typo.body2">@state.Settings.MinimumLevel.GetName()</MudText></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</MudSimpleTable>
|
||||
</MudCollapse>
|
||||
</MudPaper>
|
||||
|
||||
@if (state.Audit is null)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="@true">
|
||||
@T("No stored audit details are available yet.")
|
||||
</MudAlert>
|
||||
}
|
||||
else if (state.Audit.Findings.Count == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Success" Variant="Variant.Text" Dense="@true">
|
||||
@T("No security findings were stored for this assistant plugin.")
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div style="max-height: min(22rem, 45vh); overflow-y: auto; padding-right: 0.25rem;">
|
||||
<MudStack Spacing="2">
|
||||
@foreach (var finding in state.Audit.Findings)
|
||||
{
|
||||
<MudAlert Severity="@finding.Severity.GetSeverity()" Variant="Variant.Text" Dense="@true">
|
||||
<strong>@finding.Category</strong><span>: @finding.Description</span>
|
||||
@if (!string.IsNullOrWhiteSpace(finding.Location))
|
||||
{
|
||||
<div>
|
||||
<MudText Typo="Typo.caption">@finding.Location</MudText>
|
||||
</div>
|
||||
}
|
||||
</MudAlert>
|
||||
}
|
||||
</MudStack>
|
||||
</div>
|
||||
}
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
</MudCollapse>
|
||||
|
||||
<MudCardActions>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Primary" OnClick="@this.OpenAuditDialogAsync">
|
||||
@state.ActionLabel
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Text" OnClick="@this.HideSecurityCard">
|
||||
@T("Close")
|
||||
</MudButton>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
</MudPopover>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System.Globalization;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class AssistantPluginSecurityCard : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public PluginAssistants? Plugin { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool Compact { get; set; }
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
private PluginAssistantSecurityState SecurityState => this.Plugin is null
|
||||
? new PluginAssistantSecurityState()
|
||||
: PluginAssistantSecurityResolver.Resolve(this.SettingsManager, this.Plugin);
|
||||
|
||||
private CultureInfo currentCultureInfo = CultureInfo.InvariantCulture;
|
||||
private bool showSecurityCard;
|
||||
private bool showDetails;
|
||||
private bool showMetadata;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var activeLanguagePlugin = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
this.currentCultureInfo = CommonTools.DeriveActiveCultureOrInvariant(activeLanguagePlugin.IETFTag);
|
||||
this.showDetails = !this.Compact;
|
||||
this.showMetadata = false;
|
||||
|
||||
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
private async Task OpenAuditDialogAsync()
|
||||
{
|
||||
if (this.Plugin is null)
|
||||
return;
|
||||
|
||||
var parameters = new DialogParameters<AssistantPluginAuditDialog>
|
||||
{
|
||||
{ x => x.PluginId, this.Plugin.Id },
|
||||
};
|
||||
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)
|
||||
UpsertAudit(this.SettingsManager.ConfigurationData.AssistantPluginAudits, auditResult.Audit);
|
||||
|
||||
if (auditResult.ActivatePlugin && !this.SettingsManager.ConfigurationData.EnabledPlugins.Contains(this.Plugin.Id))
|
||||
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(this.Plugin.Id);
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.SendMessage(Event.CONFIGURATION_CHANGED, true);
|
||||
}
|
||||
|
||||
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
|
||||
return this.InvokeAsync(this.StateHasChanged);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ToggleSecurityCard() => this.showSecurityCard = !this.showSecurityCard;
|
||||
|
||||
private void HideSecurityCard() => this.showSecurityCard = false;
|
||||
|
||||
private void ToggleDetails() => this.showDetails = !this.showDetails;
|
||||
|
||||
private void ToggleMetadata() => this.showMetadata = !this.showMetadata;
|
||||
|
||||
private static void UpsertAudit(List<PluginAssistantAudit> audits, PluginAssistantAudit audit)
|
||||
{
|
||||
var existingIndex = audits.FindIndex(x => x.PluginId == audit.PluginId);
|
||||
if (existingIndex >= 0)
|
||||
audits[existingIndex] = audit;
|
||||
else
|
||||
audits.Add(audit);
|
||||
}
|
||||
|
||||
private string FormatFileTimestamp(DateTime timestamp) => CommonTools.FormatTimestampToGeneral(timestamp, this.currentCultureInfo);
|
||||
|
||||
private string GetPopoverStyle() => $"border-color: {this.GetStatusBorderColor()};";
|
||||
|
||||
private double GetConfidencePercentage()
|
||||
{
|
||||
var confidence = this.SecurityState.Audit?.Confidence ?? 0f;
|
||||
if (confidence <= 1)
|
||||
confidence *= 100;
|
||||
|
||||
return Math.Clamp(confidence, 0, 100);
|
||||
}
|
||||
|
||||
private string GetConfidenceLabel() => $"{this.GetConfidencePercentage():0}%";
|
||||
|
||||
private string GetFindingSummary()
|
||||
{
|
||||
var count = this.SecurityState.Audit?.Findings.Count ?? 0;
|
||||
return string.Format(this.T("{0} Finding(s)"), count);
|
||||
}
|
||||
|
||||
private string GetAuditTimestampLabel()
|
||||
{
|
||||
var auditedAt = this.SecurityState.Audit?.AuditedAtUtc;
|
||||
return auditedAt is null
|
||||
? this.T("No audit yet")
|
||||
: this.FormatFileTimestamp(auditedAt.Value.ToLocalTime().DateTime);
|
||||
}
|
||||
|
||||
private string GetAuditProviderLabel()
|
||||
{
|
||||
var providerName = this.SecurityState.Audit?.AuditProviderName;
|
||||
return string.IsNullOrWhiteSpace(providerName) ? this.T("Unknown") : providerName;
|
||||
}
|
||||
|
||||
private static string GetShortHash(string hash)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(hash) || hash.Length <= 16)
|
||||
return hash;
|
||||
|
||||
return $"{hash[..8]}...{hash[^8..]}";
|
||||
}
|
||||
|
||||
private Severity GetStatusSeverity() => this.SecurityState.AuditColor switch
|
||||
{
|
||||
Color.Success => Severity.Success,
|
||||
Color.Warning => Severity.Warning,
|
||||
Color.Error => Severity.Error,
|
||||
_ => Severity.Info,
|
||||
};
|
||||
|
||||
private string GetStatusBorderColor() => this.SecurityState.AuditColor switch
|
||||
{
|
||||
Color.Success => "var(--mud-palette-success)",
|
||||
Color.Warning => "var(--mud-palette-warning)",
|
||||
Color.Error => "var(--mud-palette-error)",
|
||||
_ => "var(--mud-palette-info)",
|
||||
};
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
var block = blocks[i];
|
||||
var isLastBlock = i == blocks.Count - 1;
|
||||
var isSecondLastBlock = i == blocks.Count - 2;
|
||||
@if (!block.HideFromUser)
|
||||
@if (block is { HideFromUser: false, Content: not null })
|
||||
{
|
||||
<ContentBlockComponent
|
||||
@key="@block"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<MudStack Row="true" Class='@MergeClasses(this.Class, "mb-3")' Style="@this.Style">
|
||||
@if (this.IsMultiselect)
|
||||
{
|
||||
<MudSelect
|
||||
T="string"
|
||||
SelectedValues="@this.SelectedValues"
|
||||
SelectedValuesChanged="@this.OnSelectedValuesChanged"
|
||||
MultiSelectionTextFunc="@this.GetMultiSelectionText"
|
||||
Label="@this.Label"
|
||||
HelperText="@this.HelperText"
|
||||
Placeholder="@this.Default.Display"
|
||||
OpenIcon="@this.OpenIcon"
|
||||
CloseIcon="@this.CloseIcon"
|
||||
Adornment="@this.IconPosition"
|
||||
AdornmentColor="@this.IconColor"
|
||||
Variant="@this.Variant"
|
||||
Margin="Margin.Normal"
|
||||
MultiSelection="@true"
|
||||
SelectAll="@this.HasSelectAll"
|
||||
SelectAllText="@this.SelectAllText">
|
||||
@foreach (var item in this.GetRenderedItems())
|
||||
{
|
||||
<MudSelectItem Value="@item.Value">
|
||||
@item.Display
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSelect
|
||||
T="string"
|
||||
Value="@this.Value"
|
||||
ValueChanged="@(val => this.OnValueChanged(val))"
|
||||
Label="@this.Label"
|
||||
HelperText="@this.HelperText"
|
||||
Placeholder="@this.Default.Display"
|
||||
OpenIcon="@this.OpenIcon"
|
||||
CloseIcon="@this.CloseIcon"
|
||||
Adornment="@this.IconPosition"
|
||||
AdornmentColor="@this.IconColor"
|
||||
Variant="@this.Variant"
|
||||
Margin="Margin.Normal">
|
||||
@foreach (var item in this.GetRenderedItems())
|
||||
{
|
||||
<MudSelectItem Value="@item.Value">
|
||||
@item.Display
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
}
|
||||
</MudStack>
|
||||
@@ -0,0 +1,130 @@
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components
|
||||
{
|
||||
public partial class DynamicAssistantDropdown : ComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public List<AssistantDropdownItem> Items { get; set; } = new();
|
||||
|
||||
[Parameter]
|
||||
public AssistantDropdownItem Default { get; set; } = new();
|
||||
|
||||
[Parameter]
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> ValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public HashSet<string> SelectedValues { get; set; } = [];
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<HashSet<string>> SelectedValuesChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string HelperText { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public Func<string, string?> ValidateSelection { get; set; } = _ => null;
|
||||
|
||||
[Parameter]
|
||||
public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
|
||||
|
||||
[Parameter]
|
||||
public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
|
||||
|
||||
[Parameter]
|
||||
public Color IconColor { get; set; } = Color.Default;
|
||||
|
||||
[Parameter]
|
||||
public Adornment IconPosition { get; set; } = Adornment.End;
|
||||
|
||||
[Parameter]
|
||||
public Variant Variant { get; set; } = Variant.Outlined;
|
||||
|
||||
[Parameter]
|
||||
public bool IsMultiselect { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool HasSelectAll { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string SelectAllText { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Style { get; set; } = string.Empty;
|
||||
|
||||
private async Task OnValueChanged(string newValue)
|
||||
{
|
||||
if (this.Value != newValue)
|
||||
{
|
||||
this.Value = newValue;
|
||||
await this.ValueChanged.InvokeAsync(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnSelectedValuesChanged(IEnumerable<string?>? newValues)
|
||||
{
|
||||
var updatedValues = newValues?
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Select(value => value!)
|
||||
.ToHashSet(StringComparer.Ordinal) ?? [];
|
||||
|
||||
if (this.SelectedValues.SetEquals(updatedValues))
|
||||
return;
|
||||
|
||||
this.SelectedValues = updatedValues;
|
||||
await this.SelectedValuesChanged.InvokeAsync(updatedValues);
|
||||
}
|
||||
|
||||
private List<AssistantDropdownItem> GetRenderedItems()
|
||||
{
|
||||
var items = this.Items;
|
||||
if (string.IsNullOrWhiteSpace(this.Default.Value))
|
||||
return items;
|
||||
|
||||
if (items.Any(item => string.Equals(item.Value, this.Default.Value, StringComparison.Ordinal)))
|
||||
return items;
|
||||
|
||||
return [this.Default, .. items];
|
||||
}
|
||||
|
||||
private string GetMultiSelectionText(List<string?>? selectedValues)
|
||||
{
|
||||
if (selectedValues is null || selectedValues.Count == 0)
|
||||
return this.Default.Display;
|
||||
|
||||
var labels = selectedValues
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Select(value => this.ResolveDisplayText(value!))
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.ToList();
|
||||
|
||||
return labels.Count == 0 ? this.Default.Display : string.Join(", ", labels);
|
||||
}
|
||||
|
||||
private string ResolveDisplayText(string value)
|
||||
{
|
||||
var item = this.GetRenderedItems().FirstOrDefault(item => string.Equals(item.Value, value, StringComparison.Ordinal));
|
||||
return item?.Display ?? value;
|
||||
}
|
||||
|
||||
private static string MergeClasses(string custom, string fallback)
|
||||
{
|
||||
var trimmedCustom = custom.Trim();
|
||||
var trimmedFallback = fallback.Trim();
|
||||
if (string.IsNullOrEmpty(trimmedCustom))
|
||||
return trimmedFallback;
|
||||
|
||||
return string.IsNullOrEmpty(trimmedFallback) ? trimmedCustom : $"{trimmedCustom} {trimmedFallback}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Policy" HeaderText="@T("Agent: Security Audit for external Assistants")">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
@T("This Agent audits newly installed or updated external Plugin-Assistant for security risks before they are activated and stores the latest audit card until the plugin manifest changes.")
|
||||
</MudText>
|
||||
<ConfigurationOption OptionDescription="@T("Require a security audit before activating external Assistants?")" LabelOn="@T("External Assistants must be audited before activation")" LabelOff="@T("External Assistant can be activated without an audit")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.RequireAuditBeforeActivation)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.RequireAuditBeforeActivation = updatedState)" />
|
||||
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider = selectedValue)" HelpText="@(() => T("Optionally choose a dedicated provider for assistant plugin audits. When left empty, AI Studio falls back to the app-wide default provider."))" />
|
||||
<ConfigurationSelect OptionDescription="@T("Minimum required audit level")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel)" Data="@ConfigurationSelectDataFactory.GetAssistantAuditLevelsData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel = selectedValue)" OptionHelp="@T("External Assistants rated below this audit level are treated as insufficiently reviewed.")" />
|
||||
<ConfigurationOption OptionDescription="@T("Block activation below the minimum Audit-Level?")" LabelOn="@T("Activation is blocked below the minimum Audit-Level")" LabelOff="@T("Users may still activate plugins below the minimum Audit-Level")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum = updatedState)"
|
||||
OptionHelp="@T("The audit shows you all security risks and information, if you consider this rating false at your own discretion, you can decide to install it anyway (not recommended).")"/>
|
||||
<ConfigurationOption OptionDescription="@T("Automatically audit new or updated plugins in the background?")" LabelOn="@T("Security audit is automatically done in the background")" LabelOff="@T("Security audit is done manually by the user")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants = updatedState)" />
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelAgentAssistantAudit : SettingsPanelBase;
|
||||
@@ -24,7 +24,7 @@ else
|
||||
case TreeItemData treeItem:
|
||||
@if (treeItem.Type is TreeItemType.LOADING)
|
||||
{
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@false" Items="@treeItem.Children">
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@false" Items="@treeItem.Children!">
|
||||
<BodyContent>
|
||||
<MudSkeleton Width="85%" Height="22px"/>
|
||||
</BodyContent>
|
||||
@@ -32,7 +32,7 @@ else
|
||||
}
|
||||
else if (treeItem.Type is TreeItemType.CHAT)
|
||||
{
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="@(() => this.LoadChatAsync(treeItem.Path, true))">
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children!" OnClick="@(() => this.LoadChatAsync(treeItem.Path, true))">
|
||||
<BodyContent>
|
||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||
<MudText Style="justify-self: start;">
|
||||
@@ -65,7 +65,7 @@ else
|
||||
}
|
||||
else if (treeItem.Type is TreeItemType.WORKSPACE)
|
||||
{
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="@(() => this.OnWorkspaceClicked(treeItem))">
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children!" OnClick="@(() => this.OnWorkspaceClicked(treeItem))">
|
||||
<BodyContent>
|
||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||
<MudText Style="justify-self: start;">
|
||||
@@ -86,7 +86,7 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
|
||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children!">
|
||||
<BodyContent>
|
||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||
<MudText Style="justify-self: start;">
|
||||
|
||||
Reference in new issue
Block a user