From e30bb3d00fc2d31dd0da17956d55408846472269 Mon Sep 17 00:00:00 2001 From: nilsk Date: Mon, 13 Jul 2026 11:10:26 +0200 Subject: [PATCH] wireing revision dialog and logic to a footer button in dynamic assistants --- .../Assistants/Dynamic/AssistantDynamic.razor | 6 + .../Dynamic/AssistantDynamic.razor.cs | 109 ++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index 5f8ed63f..897e67e7 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -42,6 +42,12 @@ else } @code { + private protected override RenderFragment? HeaderActions => this.CanReviseCurrentAssistant + ? @ + + + : null; + private RenderFragment RenderSwitch(AssistantSwitch assistantSwitch) => @ { + [Inject] + private IDialogService DialogService { get; init; } = null!; + [Parameter] public AssistantForm? RootComponent { get; set; } @@ -33,6 +40,18 @@ public partial class AssistantDynamic : AssistantBaseCore /// protected override string AssistantSessionInstanceId => this.assistantPlugin is null ? base.AssistantSessionInstanceId : this.assistantPlugin.Id.ToString(); + protected override IReadOnlyList FooterButtons => + [ + new ButtonData + { + Text = T("Revise"), + Icon = Icons.Material.Filled.AutoMode, + Color = Color.Default, + AsyncAction = this.OpenRevisionDialogAsync, + DisabledActionParam = () => !this.CanReviseCurrentAssistant, + }, + ]; + private string title = string.Empty; private string description = string.Empty; private string systemPrompt = string.Empty; @@ -67,6 +86,9 @@ public partial class AssistantDynamic : AssistantBaseCore private static readonly AssistantSessionStateKey SECURITY_MESSAGE_STATE_KEY = new(nameof(securityMessage)); private static readonly AssistantSessionStateKey IS_SECURITY_BLOCKED_STATE_KEY = new(nameof(isSecurityBlocked)); + private bool CanReviseCurrentAssistant => this.assistantPlugin is { IsInternal: false, IsAssistantBuilderGenerated: true } && + !string.IsNullOrWhiteSpace(this.assistantPlugin.PluginPath); + /// protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state) { @@ -210,6 +232,93 @@ public partial class AssistantDynamic : AssistantBaseCore return null; } + private async Task OpenRevisionDialogAsync() + { + if (this.assistantPlugin is null || !this.CanReviseCurrentAssistant) + return; + + var testContext = await this.BuildRevisionTestContextAsync(); + var parameters = new DialogParameters + { + { x => x.PluginId, this.assistantPlugin.Id }, + { x => x.PluginLocalPath, this.assistantPlugin.PluginPath }, + { x => x.TestContext, testContext }, + }; + + var dialog = await this.DialogService.ShowAsync(this.T("Revise Assistant"), parameters, DialogOptions.BLOCKING_FULLSCREEN); + var result = await dialog.Result; + if (result is null || result.Canceled) + return; + + if (result.Data is not AssistantPluginRevisionDialogResult revisionResult) + return; + + this.Logger.LogInformation($"AssistantDynamic of plugin '{revisionResult.PluginName}' ({revisionResult.PluginName}) was successfully revised with audit result {revisionResult.Audit?.Level ?? AssistantAuditLevel.UNKNOWN}."); + var updatedPlugin = PluginFactory.RunningPlugins.OfType().FirstOrDefault(x => x.Id == revisionResult.PluginId); + if (updatedPlugin is not null) + this.ApplyUpdatedAssistantPlugin(updatedPlugin); + + await this.MessageBus.SendSuccess(new(Icons.Material.Filled.AutoFixHigh, string.Format(this.T("The assistant '{0}' has been updated."), revisionResult.PluginName))); + await this.MessageBus.SendMessage(this, Event.PLUGINS_RELOADED); + await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); + await this.InvokeAsync(this.StateHasChanged); + } + + private async Task BuildRevisionTestContextAsync() + { + var builder = new StringBuilder(); + + if (this.assistantPlugin is not null) + { + var componentSummary = this.assistantPlugin.CreateAuditComponentSummary(); + if (!string.IsNullOrWhiteSpace(componentSummary)) + { + builder.AppendLine("Current component overview:"); + builder.AppendLine(componentSummary); + builder.AppendLine(); + } + } + + var promptPreview = await this.CollectUserPromptAsync(); + if (!string.IsNullOrWhiteSpace(promptPreview)) + { + builder.AppendLine("Current prompt preview from the assistant form:"); + builder.AppendLine(promptPreview); + builder.AppendLine(); + } + + if (this.ResultingContentBlock?.Content is ContentText text && !string.IsNullOrWhiteSpace(text.Text)) + { + builder.AppendLine("Last assistant response visible in this session:"); + builder.AppendLine(text.Text); + } + + return builder.ToString().Trim(); + } + + private void ApplyUpdatedAssistantPlugin(PluginAssistants updatedPlugin) + { + this.assistantPlugin = updatedPlugin; + this.RootComponent = updatedPlugin.RootComponent; + this.title = updatedPlugin.AssistantTitle; + this.description = updatedPlugin.AssistantDescription; + this.systemPrompt = updatedPlugin.SystemPrompt; + this.submitText = updatedPlugin.SubmitText; + this.allowProfiles = updatedPlugin.AllowProfiles; + this.showFooterProfileSelection = !updatedPlugin.HasEmbeddedProfileSelection; + this.pluginPath = updatedPlugin.PluginPath; + var pluginHash = updatedPlugin.ComputeAuditHash(); + this.audit = this.SettingsManager.ConfigurationData.AssistantPluginAudits.FirstOrDefault(x => x.PluginId == updatedPlugin.Id && x.PluginHash == pluginHash); + + var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, updatedPlugin); + this.securityMessage = securityState.CanStartAssistant ? string.Empty : securityState.Description; + this.isSecurityBlocked = !securityState.CanStartAssistant; + + this.assistantState.Clear(); + if (this.RootComponent is not null) + this.InitializeComponentState(this.RootComponent.Children); + } + #endregion private string ResolveImageSource(AssistantImage image)