Assistant Builder improvements (#851)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,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,updater, appimage) (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,app,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,updater, appimage) (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

Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
nilskruthoffandThorsten Sommer authored and GitHub committed 2026-07-15 21:00:25 +02:00
1 parent b1459523d9
commit 88aab302a2
37 files changed
+4098 -482

No files matched your search

@@ -1,6 +1,7 @@
@attribute [Route(Routes.ASSISTANTS)]
@using AIStudio.Dialogs.Settings
@using AIStudio.Settings.DataModel
@using AIStudio.Tools.PluginSystem
@using AIStudio.Tools.PluginSystem.Assistants
@inherits MSGComponentBase
@@ -45,6 +46,7 @@
{
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
var launchLink = assistantPlugin.StartsChatDirectly ? string.Empty : $"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}";
var availablePlugin = PluginFactory.AvailablePlugins.OfType<IAvailablePlugin>().FirstOrDefault(plugin => plugin.Id == assistantPlugin.Id);
<AssistantBlock TSettings="NoSettingsPanel"
Name="@T(assistantPlugin.AssistantTitle)"
Description="@T(assistantPlugin.Description)"
@@ -53,6 +55,12 @@
AssistantSessionInstanceId="@assistantPlugin.Id.ToString()"
Link="@launchLink"
OnClick="@(() => this.StartAssistantPluginAsync(assistantPlugin))">
<AdditionalActions>
@if (availablePlugin is not null)
{
<AssistantPluginDeleteAction Plugin="@availablePlugin" />
}
</AdditionalActions>
<SecurityBadge>
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true" />
</SecurityBadge>
@@ -324,6 +324,7 @@
<ThirdPartyComponent Name="HtmlAgilityPack" Developer="ZZZ Projects & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE" RepositoryUrl="https://github.com/zzzprojects/html-agility-pack" UseCase="@T("We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant.")"/>
<ThirdPartyComponent Name="ReverseMarkdown" Developer="Babu Annamalai & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mysticmind/reversemarkdown-net/blob/master/LICENSE" RepositoryUrl="https://github.com/mysticmind/reversemarkdown-net" UseCase="@T("This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant.")"/>
<ThirdPartyComponent Name="wikEd diff" Developer="Cacycle & Open Source Community" LicenseName="None (public domain)" LicenseUrl="https://en.wikipedia.org/wiki/User:Cacycle/diff#License" RepositoryUrl="https://en.wikipedia.org/wiki/User:Cacycle/diff" UseCase="@T("This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant.")"/>
<ThirdPartyComponent Name="CodeJar" Developer="Anton Medvedev" LicenseName="MIT" LicenseUrl="https://github.com/antonmedv/codejar/blob/master/LICENSE" RepositoryUrl="https://github.com/antonmedv/codejar/" UseCase="@T("CodeJar is a lightweight embeddable code editor for the browser.")"/>
</MudGrid>
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Verified" HeaderText="License: FSL-1.1-MIT">
+37 -13
View File
@@ -1,5 +1,6 @@
@using AIStudio.Tools.PluginSystem
@using AIStudio.Tools.PluginSystem.Assistants
@using AIStudio.Tools.Services
@inherits MSGComponentBase
@attribute [Route(Routes.PLUGINS)]
@@ -64,11 +65,11 @@
</MudStack>
</MudTd>
<MudTd>
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
<MudStack Row="true" Spacing="0" 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" />
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true"/>
}
@if (context is { IsInternal: false, Type: not PluginType.CONFIGURATION })
{
@@ -79,23 +80,46 @@
</MudTooltip>
}
@if (context is { IsInternal: false } && !string.IsNullOrWhiteSpace(context.SourceURL))
{
var sourceUrl = context.SourceURL;
var isSendingMail = IsSendingMail(sourceUrl);
if (isSendingMail)
<MudButtonGroup Class="ms-3">
@if (context is { IsInternal: false } && !string.IsNullOrWhiteSpace(context.SourceURL))
{
<MudTooltip Text="@T("Send a mail")">
<MudIconButton Icon="@Icons.Material.Filled.Email" Href="@sourceUrl" Target="_blank" Size="Size.Medium"/>
var sourceUrl = context.SourceURL;
var isSendingMail = IsSendingMail(sourceUrl);
if (isSendingMail)
{
var isDefaultSupportContact = string.Equals(sourceUrl, AssistantPluginGenerationService.DEFAULT_SUPPORT_CONTACT, StringComparison.Ordinal);
<MudTooltip Text="@(isDefaultSupportContact ? string.Empty : T("Send a mail"))">
<MudIconButton Icon="@Icons.Material.Filled.Email" Href="@sourceUrl" Target="_blank" Size="Size.Medium" Disabled="@isDefaultSupportContact"/>
</MudTooltip>
}
else
{
var isDefaultSourceUrl = string.Equals(sourceUrl, AssistantPluginGenerationService.DEFAULT_SOURCE_URL, StringComparison.Ordinal);
<MudTooltip Text="@(isDefaultSourceUrl ? T("No source url available") : T("Open website"))">
<MudIconButton Icon="@Icons.Material.Filled.OpenInBrowser" Href="@sourceUrl" Target="_blank" Size="Size.Medium" Disabled="@isDefaultSourceUrl"/>
</MudTooltip>
}
}
@if (context is IAvailablePlugin editablePlugin && CanEditAssistantPlugin(editablePlugin))
{
<MudTooltip Text="@T("Edit assistant plugin")">
<MudIconButton Icon="@Icons.Material.Filled.Code" Size="Size.Medium" OnClick="@(() => this.OpenAssistantPluginEditorDialogAsync(editablePlugin))"/>
</MudTooltip>
}
else
@if (context is IAvailablePlugin revisionPlugin && CanReviseAssistantPlugin(revisionPlugin))
{
<MudTooltip Text="@T("Open website")">
<MudIconButton Icon="@Icons.Material.Filled.OpenInBrowser" Href="@sourceUrl" Target="_blank" Size="Size.Medium"/>
<MudTooltip Text="@T("Revise assistant plugin with AI")">
<MudIconButton Icon="@Icons.Material.Filled.AutoMode" Size="Size.Medium" OnClick="@(() => this.OpenAssistantPluginRevisionDialogAsync(revisionPlugin))"/>
</MudTooltip>
}
}
@if (context is IAvailablePlugin availablePlugin)
{
<AssistantPluginDeleteAction Plugin="@availablePlugin" />
}
</MudButtonGroup>
</MudStack>
</MudTd>
</RowTemplate>
+56 -4
View File
@@ -4,6 +4,7 @@ using AIStudio.Dialogs;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.PluginSystem.Assistants;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Dialogs.DialogOptions;
@@ -27,6 +28,8 @@ public partial class Plugins : MSGComponentBase
[Inject]
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger(nameof(Plugins));
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
@@ -88,7 +91,7 @@ public partial class Plugins : MSGComponentBase
return;
}
if (securityState.IsBelowMinimum && securityState.IsBlocked)
if (securityState is { IsBelowMinimum: true, IsBlocked: true })
{
var blockedAudit = securityState.Audit;
if (blockedAudit is not null)
@@ -96,7 +99,7 @@ public partial class Plugins : MSGComponentBase
return;
}
if (securityState.IsBelowMinimum && securityState.CanOverride &&
if (securityState is { IsBelowMinimum: true, CanOverride: true } &&
!await this.ConfirmActivationBelowMinimumAsync(pluginMeta.Name, securityState.Audit!.Level))
{
return;
@@ -135,7 +138,7 @@ public partial class Plugins : MSGComponentBase
{
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?"),
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())
@@ -158,7 +161,7 @@ public partial class Plugins : MSGComponentBase
return false;
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
return securityState.IsBlocked && !securityState.RequiresAudit;
return securityState is { IsBlocked: true, RequiresAudit: false };
}
private string GetActivationTooltip(IPluginMetadata pluginMeta, bool isEnabled)
@@ -182,6 +185,55 @@ public partial class Plugins : MSGComponentBase
: this.T("Enable plugin");
}
private static bool CanEditAssistantPlugin(IAvailablePlugin plugin) => plugin is { IsInternal: false, Type: PluginType.ASSISTANT } && !string.IsNullOrWhiteSpace(plugin.LocalPath);
private static bool CanReviseAssistantPlugin(IAvailablePlugin plugin)
{
var assistantPlugin = PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == plugin.Id);
return plugin is { IsInternal: false, IsManagedByConfigServer: false, Type: PluginType.ASSISTANT } &&
!string.IsNullOrWhiteSpace(plugin.LocalPath) &&
assistantPlugin?.IsManagedByConfigServer is false;
}
private async Task OpenAssistantPluginEditorDialogAsync(IAvailablePlugin plugin)
{
var parameters = new DialogParameters<AssistantPluginEditorDialog>
{
{ x => x.PluginId, plugin.Id },
{ x => x.PluginLocalPath, plugin.LocalPath },
};
var dialogReference = await this.DialogService.ShowAsync<AssistantPluginEditorDialog>(this.T("Edit Assistant Plugin"), parameters, DialogOptions.BLOCKING_FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is not AssistantPluginEditorDialogResult result)
return;
await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Save, string.Format(this.T("The assistant plugin '{0}' has been successfully saved."), result.PluginName)));
LOG.LogInformation($"The assistant plugin '{result.PluginName}' ({result.PluginId}) has been successfully updated.");
await this.MessageBus.SendMessage<bool>(this, Event.PLUGINS_RELOADED);
await this.InvokeAsync(this.StateHasChanged);
}
private async Task OpenAssistantPluginRevisionDialogAsync(IAvailablePlugin plugin)
{
var parameters = new DialogParameters<AssistantPluginRevisionDialog>
{
{ x => x.PluginId, plugin.Id },
{ x => x.PluginLocalPath, plugin.LocalPath },
};
var dialogReference = await this.DialogService.ShowAsync<AssistantPluginRevisionDialog>(this.T("Revise Assistant Plugin"), parameters, DialogOptions.BLOCKING_FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is not AssistantPluginRevisionDialogResult result)
return;
await this.MessageBus.SendSuccess(new(Icons.Material.Filled.AutoFixHigh, string.Format(this.T("The assistant plugin '{0}' has been successfully revised."), result.PluginName)));
LOG.LogInformation($"The assistant plugin '{result.PluginName}' ({result.PluginId}) has been successfully revised.");
await this.MessageBus.SendMessage<bool>(this, Event.PLUGINS_RELOADED);
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
await this.InvokeAsync(this.StateHasChanged);
}
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);