From 243dc34a34a1d37e188da6bfe1b327a8eb40c410 Mon Sep 17 00:00:00 2001 From: Peer Hogeterp <20603780+peerschuett@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:32:25 +0200 Subject: [PATCH] Read Web Page is now opened automatically when the confluence tool is selected --- .../Agents/AssistantAudit/AssistantAuditAgent.cs | 4 ++-- .../Components/AssistantPluginSecurityCard.razor.cs | 5 ++++- .../Components/ToolSelectionField.razor.cs | 8 +++++++- app/MindWork AI Studio/Plugins/configuration/plugin.lua | 2 ++ .../de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua | 6 ++++++ .../en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua | 6 ++++++ .../Tools/ToolCallingSystem/ToolSelectionRules.cs | 8 +++++++- documentation/Tools.md | 2 +- 8 files changed, 35 insertions(+), 6 deletions(-) diff --git a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs index aa1e600f..56530ef8 100644 --- a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs +++ b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs @@ -348,12 +348,12 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo /// private string FormatRequestedTools(PluginAssistants plugin) { - var toolIds = plugin.AssistantToolIds ?? plugin.ChatLaunchConfiguration?.ToolIds ?? []; + var toolIds = ToolSelectionRules.NormalizeSelection(plugin.AssistantToolIds ?? plugin.ChatLaunchConfiguration?.ToolIds ?? []); if (toolIds.Count == 0) return "None. This plugin does not request any tools."; var builder = new StringBuilder(); - foreach (var toolId in toolIds) + foreach (var toolId in toolIds.OrderBy(x => x, StringComparer.Ordinal)) { var definition = toolRegistry.GetDefinition(toolId); if (definition is null) diff --git a/app/MindWork AI Studio/Components/AssistantPluginSecurityCard.razor.cs b/app/MindWork AI Studio/Components/AssistantPluginSecurityCard.razor.cs index e1ffde7a..06e25a30 100644 --- a/app/MindWork AI Studio/Components/AssistantPluginSecurityCard.razor.cs +++ b/app/MindWork AI Studio/Components/AssistantPluginSecurityCard.razor.cs @@ -1,6 +1,7 @@ using System.Globalization; using AIStudio.Dialogs; using AIStudio.Tools.PluginSystem.Assistants; +using AIStudio.Tools.ToolCallingSystem; using Microsoft.AspNetCore.Components; using DialogOptions = AIStudio.Dialogs.DialogOptions; @@ -30,7 +31,9 @@ public partial class AssistantPluginSecurityCard : MSGComponentBase /// should see that beforehand, which is why the count sits in the header next to the audit /// level and the tools themselves are named in the details. /// - private IReadOnlyList PluginToolIds => this.Plugin?.AssistantToolIds ?? this.Plugin?.ChatLaunchConfiguration?.ToolIds ?? []; + private IReadOnlyList PluginToolIds => ToolSelectionRules.NormalizeSelection(this.Plugin?.AssistantToolIds ?? this.Plugin?.ChatLaunchConfiguration?.ToolIds ?? []) + .OrderBy(x => x, StringComparer.Ordinal) + .ToList(); private CultureInfo currentCultureInfo = CultureInfo.InvariantCulture; private bool showSecurityCard; diff --git a/app/MindWork AI Studio/Components/ToolSelectionField.razor.cs b/app/MindWork AI Studio/Components/ToolSelectionField.razor.cs index 73186d41..45c82150 100644 --- a/app/MindWork AI Studio/Components/ToolSelectionField.razor.cs +++ b/app/MindWork AI Studio/Components/ToolSelectionField.razor.cs @@ -49,6 +49,12 @@ public partial class ToolSelectionField : MSGComponentBase private List> availableTools = []; + protected override void OnParametersSet() + { + this.SelectedToolIds = ToolSelectionRules.NormalizeSelection(this.SelectedToolIds); + base.OnParametersSet(); + } + protected override async Task OnInitializedAsync() { this.availableTools = (await this.ToolRegistry.GetCatalogAsync(this.Component)) @@ -80,4 +86,4 @@ public partial class ToolSelectionField : MSGComponentBase break; } } -} \ No newline at end of file +} diff --git a/app/MindWork AI Studio/Plugins/configuration/plugin.lua b/app/MindWork AI Studio/Plugins/configuration/plugin.lua index 97796c8b..10033120 100644 --- a/app/MindWork AI Studio/Plugins/configuration/plugin.lua +++ b/app/MindWork AI Studio/Plugins/configuration/plugin.lua @@ -1071,6 +1071,7 @@ CONFIG["CHAT_TEMPLATES"] = {} -- -- organization switched off. A tool has to meet the confidence requirements of the -- -- provider in use, so it may stay unavailable even though this template names it. -- -- Tool IDs include: web_search, read_web_page, search_confluence +-- -- Selecting search_confluence also selects read_web_page. -- ["ToolIds"] = { -- "read_web_page", -- }, @@ -1176,6 +1177,7 @@ CONFIG["DOCUMENT_ANALYSIS_POLICIES"] = {} -- -- A listed tool must still meet the confidence requirements of the provider in -- -- use, so a tool may stay unavailable even though this policy permits it. -- -- Tool IDs include: web_search, read_web_page, search_confluence +-- -- Allowing search_confluence also allows read_web_page. -- ["AllowedToolIds"] = { "web_search" }, -- -- -- Optional: preselect a provider or profile by ID. diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua index 665bd5bf..128eff13 100644 --- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua @@ -12564,6 +12564,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- The setting '{0}' must not exceed {1}. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T3109652601"] = "Die Einstellung „{0}“ darf den Wert {1} nicht überschreiten." +-- Confluence asked for a sign-in instead of showing search results. Your operating system's sign-in was not accepted by the wiki; open it in your browser to check your access. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T3122873647"] = "Confluence hat eine Anmeldung angefordert, statt Suchergebnisse anzuzeigen. Ihre Betriebssystemanmeldung wurde vom Wiki nicht akzeptiert; öffnen Sie es in Ihrem Browser, um Ihren Zugriff zu überprüfen." + -- Confluence Base URL UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T3278636117"] = "Confluence-Basis-URL" @@ -12585,6 +12588,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- Search Confluence UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T665149329"] = "Confluence durchsuchen" +-- Confluence search for “{0}” +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T718586991"] = "Confluence-Suche nach „{0}“" + -- The HTTPS address of your Confluence site, including its path if present, such as https://wiki.example.org/confluence/. AI Studio searches through the same page reader used by Read Web Page. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T859229067"] = "Die HTTPS-Adresse Ihrer Confluence-Site, einschließlich des Pfads, falls vorhanden, wie https://wiki.example.org/confluence/. AI Studio verwendet zum Durchsuchen denselben Seitenleser wie „Webseite lesen“." diff --git a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua index 99c9980a..658fb10b 100644 --- a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua @@ -12564,6 +12564,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- The setting '{0}' must not exceed {1}. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T3109652601"] = "The setting '{0}' must not exceed {1}." +-- Confluence asked for a sign-in instead of showing search results. Your operating system's sign-in was not accepted by the wiki; open it in your browser to check your access. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T3122873647"] = "Confluence asked for a sign-in instead of showing search results. Your operating system's sign-in was not accepted by the wiki; open it in your browser to check your access." + -- Confluence Base URL UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T3278636117"] = "Confluence Base URL" @@ -12585,6 +12588,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- Search Confluence UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T665149329"] = "Search Confluence" +-- Confluence search for “{0}” +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T718586991"] = "Confluence search for “{0}”" + -- The HTTPS address of your Confluence site, including its path if present, such as https://wiki.example.org/confluence/. AI Studio searches through the same page reader used by Read Web Page. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::CONFLUENCESEARCHTOOL::T859229067"] = "The HTTPS address of your Confluence site, including its path if present, such as https://wiki.example.org/confluence/. AI Studio searches through the same page reader used by Read Web Page." diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs index 86af6833..5aaada34 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs @@ -11,7 +11,13 @@ public static class ToolSelectionRules public const string SEARCH_CONFLUENCE_TOOL_ID = "search_confluence"; public static HashSet NormalizeSelection(IEnumerable selectedToolIds) - => selectedToolIds.ToHashSet(StringComparer.Ordinal); + { + var normalized = selectedToolIds.ToHashSet(StringComparer.Ordinal); + if (normalized.Contains(SEARCH_CONFLUENCE_TOOL_ID)) + normalized.Add(READ_WEB_PAGE_TOOL_ID); + + return normalized; + } public static string GetMaxToolCallsFinalResponseInstruction() => $"The maximum of {MAX_TOOL_CALLS} tool calls has been reached. No more tools are available. Provide the best possible final answer to the user based on the tool results already available."; diff --git a/documentation/Tools.md b/documentation/Tools.md index 103cd6b9..b89e3209 100644 --- a/documentation/Tools.md +++ b/documentation/Tools.md @@ -92,7 +92,7 @@ What differs between callers is which targets are acceptable, and that follows f `read_web_page` remains the independent single-URL tool and may use its configured private-host allowlist and operating-system sign-in behavior for allowed HTTPS targets. An allowed private host can only be read by a High-confidence provider or a provider instance listed in `DataSourceSecuritySettings.TrustedProviderIds`. -`search_confluence` builds a CQL query for the configured HTTPS Confluence Data Center site's `dosearchsite.action` page and loads it through `WebPageRetrievalService`, the same reader used by `read_web_page`. The model supplies a search phrase and optionally a space key, never a URL or CQL expression. The tool returns the extracted search page as Markdown with links, after truncation and prompt-injection filtering, and lists the search page as its source. Every request, redirects included, must stay within the configured base URL; `WebPageRetrievalOptions.IsTargetAllowed` refuses a redirect before it is followed, so the query never reaches another host. The operating-system sign-in goes to the configured host even when it has public addresses (`IsOsSsoAllowedForPublicHost`), and a redirect to Confluence's login page is reported as a failed sign-in instead of an empty search. The tool treats a provider trusted by the organization the same as a High-confidence one, and the result raises the chat's continuing confidence requirement to High. `ChatThreadExtensions.IsLLMProviderAllowed` lets a trusted provider meet that requirement as well, so it can continue the chat after a search. Select `read_web_page` as well to load a result's full content; its private-host allowlist still applies to that page. +`search_confluence` builds a CQL query for the configured HTTPS Confluence Data Center site's `dosearchsite.action` page and loads it through `WebPageRetrievalService`, the same reader used by `read_web_page`. The model supplies a search phrase and optionally a space key, never a URL or CQL expression. The tool returns the extracted search page as Markdown with links, after truncation and prompt-injection filtering, and lists the search page as its source. Every request, redirects included, must stay within the configured base URL; `WebPageRetrievalOptions.IsTargetAllowed` refuses a redirect before it is followed, so the query never reaches another host. The operating-system sign-in goes to the configured host even when it has public addresses (`IsOsSsoAllowedForPublicHost`), and a redirect to Confluence's login page is reported as a failed sign-in instead of an empty search. The tool treats a provider trusted by the organization the same as a High-confidence one, and the result raises the chat's continuing confidence requirement to High. `ChatThreadExtensions.IsLLMProviderAllowed` lets a trusted provider meet that requirement as well, so it can continue the chat after a search. Selecting `search_confluence` also selects `read_web_page` so the model can load a result's full content; the latter tool's private-host allowlist and other availability rules still apply. Every successfully retrieved page with readable content is also returned as a structured tool source, using the final URL after redirects and the extracted page title. The provider collects these sources across local tool calls and attaches them to the final response under the separate “Sources used by tools” heading. Failed, blocked, empty, and duplicate retrievals do not add sources — a pattern worth copying for any tool that returns material the user may want to check.