From c41a0cf74c2ac67ba7e196643e7d28a7363b37ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peer=20Sch=C3=BCtt?= <20603780+peerschuett@users.noreply.github.com> Date: Mon, 22 Jun 2026 14:39:42 +0200 Subject: [PATCH] Added the option to create an assistants plugin that just opens a chat in a specific workspace --- .../Assistants/I18N/allTexts.lua | 12 ++++ .../Components/AssistantBlock.razor | 15 ++++- .../Components/AssistantBlock.razor.cs | 5 ++ app/MindWork AI Studio/Pages/Assistants.razor | 4 +- .../Pages/Assistants.razor.cs | 51 +++++++++++++++++ .../Plugins/assistants/README.md | 28 ++++++++++ .../Plugins/assistants/plugin.lua | 2 + .../plugin.lua | 12 ++++ .../plugin.lua | 12 ++++ .../DataModel/DataAssistantPluginAudit.cs | 2 +- .../AssistantPluginLaunchBehavior.cs | 7 +++ .../Assistants/PluginAssistants.cs | 56 +++++++++++++++++++ .../Tools/WorkspaceBehaviour.cs | 42 +++++++++++++- 13 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantPluginLaunchBehavior.cs diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index bd5d8535..0bfe6563 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -7516,6 +7516,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANT -- Button UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPEEXTENSIONS::T864557713"] = "Button" +-- The ASSISTANT table contains an invalid LaunchBehavior value. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T109828905"] = "The ASSISTANT table contains an invalid LaunchBehavior value." + +-- The ASSISTANT table contains an unsupported LaunchBehavior value. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1194373781"] = "The ASSISTANT table contains an unsupported LaunchBehavior value." + -- Failed to parse the UI render tree from the ASSISTANT lua table. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Failed to parse the UI render tree from the ASSISTANT lua table." @@ -7531,12 +7537,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2 -- The ASSISTANT lua table does not exist or is not a valid table. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3017816936"] = "The ASSISTANT lua table does not exist or is not a valid table." +-- The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3233001282"] = "The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'." + -- The provided ASSISTANT lua table does not contain a valid system prompt. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3402798667"] = "The provided ASSISTANT lua table does not contain a valid system prompt." -- The ASSISTANT table does not contain a valid system prompt. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3723171842"] = "The ASSISTANT table does not contain a valid system prompt." +-- The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T4215554842"] = "The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName." + -- ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T683382975"] = "ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax." diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor b/app/MindWork AI Studio/Components/AssistantBlock.razor index 973af871..3b959819 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor @@ -24,9 +24,18 @@ - - @this.ButtonText - + @if (this.HasStartAction) + { + + @this.ButtonText + + } + else + { + + @this.ButtonText + + } @if (this.HasSettingsPanel) { diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs index dde37267..e880fb4e 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs @@ -22,6 +22,9 @@ public partial class AssistantBlock : MSGComponentBase where TSetting [Parameter] public string Link { get; set; } = string.Empty; + [Parameter] + public EventCallback OnClick { get; set; } + [Parameter] public bool Disabled { get; set; } @@ -61,4 +64,6 @@ public partial class AssistantBlock : MSGComponentBase where TSetting private bool IsVisible => this.SettingsManager.IsAssistantVisible(this.Component, assistantName: this.Name, requiredPreviewFeature: this.RequiredPreviewFeature); private bool HasSettingsPanel => typeof(TSettings) != typeof(NoSettingsPanel); + + private bool HasStartAction => this.OnClick.HasDelegate; } diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor index cec6c561..7963886c 100644 --- a/app/MindWork AI Studio/Pages/Assistants.razor +++ b/app/MindWork AI Studio/Pages/Assistants.razor @@ -42,12 +42,14 @@ @foreach (var assistantPlugin in this.AssistantPlugins) { var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin); + var launchLink = assistantPlugin.StartsChatDirectly ? string.Empty : $"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}"; + Link="@launchLink" + OnClick="@(() => this.StartAssistantPluginAsync(assistantPlugin))"> diff --git a/app/MindWork AI Studio/Pages/Assistants.razor.cs b/app/MindWork AI Studio/Pages/Assistants.razor.cs index f7668a1d..1d67cecb 100644 --- a/app/MindWork AI Studio/Pages/Assistants.razor.cs +++ b/app/MindWork AI Studio/Pages/Assistants.razor.cs @@ -1,3 +1,4 @@ +using AIStudio.Chat; using AIStudio.Components; using AIStudio.Agents.AssistantAudit; using AIStudio.Tools.PluginSystem; @@ -12,6 +13,12 @@ public partial class Assistants : MSGComponentBase [Inject] private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!; + + [Inject] + private NavigationManager NavigationManager { get; init; } = null!; + + [Inject] + private ILogger Logger { get; init; } = null!; protected override async Task OnInitializedAsync() { @@ -81,6 +88,50 @@ public partial class Assistants : MSGComponentBase audits.Add(audit); } + private async Task StartAssistantPluginAsync(PluginAssistants assistantPlugin) + { + var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin); + if (!securityState.CanStartAssistant) + return; + + if (!assistantPlugin.StartsChatDirectly) + { + this.NavigationManager.NavigateTo($"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}"); + return; + } + + var chatThread = await this.TryCreateDirectChatThreadAsync(assistantPlugin); + if (chatThread is null) + return; + + MessageBus.INSTANCE.DeferMessage(this, Event.SEND_TO_CHAT, chatThread); + this.NavigationManager.NavigateTo(Routes.CHAT); + } + + private async Task TryCreateDirectChatThreadAsync(PluginAssistants assistantPlugin) + { + var workspaceId = await WorkspaceBehaviour.ResolveOrCreateWorkspaceIdByNameAsync(assistantPlugin.LaunchWorkspaceName); + if (workspaceId == Guid.Empty) + { + this.Logger.LogWarning("Assistant plugin '{PluginName}' could not resolve or create workspace '{WorkspaceName}'.", assistantPlugin.Name, assistantPlugin.LaunchWorkspaceName); + return null; + } + + return new ChatThread + { + IncludeDateTime = true, + SelectedProvider = string.Empty, + SelectedProfile = string.Empty, + SelectedChatTemplate = string.Empty, + SystemPrompt = SystemPrompts.DEFAULT, + WorkspaceId = workspaceId, + ChatId = Guid.NewGuid(), + Name = assistantPlugin.AssistantTitle, + DataSourceOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy(), + Blocks = [], + }; + } + protected override async Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default { if (triggeredEvent is Event.PLUGINS_RELOADED) diff --git a/app/MindWork AI Studio/Plugins/assistants/README.md b/app/MindWork AI Studio/Plugins/assistants/README.md index 2ce0a9c7..dfef8c10 100644 --- a/app/MindWork AI Studio/Plugins/assistants/README.md +++ b/app/MindWork AI Studio/Plugins/assistants/README.md @@ -81,6 +81,9 @@ Each assistant plugin lives in its own directory under the assistants plugin roo ## Structure - `ASSISTANT` is the root table. It must contain `Title`, `Description`, `SystemPrompt`, `SubmitText`, `AllowProfiles`, and the nested `UI` definition. +- `ASSISTANT` may optionally define direct-launch metadata for assistant tiles: + - `LaunchBehavior = "OPEN_WORKSPACE_CHAT_BY_NAME"` + - `WorkspaceName = ""` - `UI.Type` is always `"FORM"` and `UI.Children` is a list of component tables. - Each component table declares `Type`, an optional `Children` array, and a `Props` table that feeds the component’s parameters. @@ -92,6 +95,8 @@ ASSISTANT = { ["SystemPrompt"] = "", ["SubmitText"] = "", ["AllowProfiles"] = true, + ["LaunchBehavior"] = "OPEN_WORKSPACE_CHAT_BY_NAME", + ["WorkspaceName"] = "", ["UI"] = { ["Type"] = "FORM", ["Children"] = { @@ -101,6 +106,29 @@ ASSISTANT = { } ``` +## Direct Launch to Workspace Chat +Assistant plugins can optionally skip the normal assistant page and open a chat directly from the tile. + +```lua +ASSISTANT = { + ["Title"] = "Open Chat", + ["Description"] = "Open a new chat in the XXX workspace.", + ["SystemPrompt"] = "", + ["SubmitText"] = "Start", + ["AllowProfiles"] = true, + ["LaunchBehavior"] = "OPEN_WORKSPACE_CHAT_BY_NAME", + ["WorkspaceName"] = "XXX", + ["UI"] = { + ["Type"] = "FORM", + ["Children"] = {} + } +} +``` + +- `WorkspaceName` is resolved case-insensitively after trimming. +- If the workspace does not exist yet, AI Studio creates it automatically. +- The opened chat uses the normal default chat settings of AI Studio. + #### Supported types (matching the Blazor UI components): diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index 36d22016..e3610bc2 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -62,6 +62,8 @@ ASSISTANT = { ["SystemPrompt"] = "", -- required ["SubmitText"] = "