diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolActivation.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolActivation.cs
new file mode 100644
index 00000000..1a58a487
--- /dev/null
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolActivation.cs
@@ -0,0 +1,23 @@
+namespace AIStudio.Tools.ToolCallingSystem;
+
+///
+/// How a tool comes to be offered to a model.
+///
+public enum ToolActivation
+{
+ ///
+ /// Offered when it was selected: by the user, a chat template, a policy, or an assistant.
+ ///
+ SELECTION,
+
+ ///
+ /// Offered whenever the chat calls for it, without anybody selecting it.
+ ///
+ ///
+ /// For a tool whose use is already decided somewhere else. Semantic Search is such a tool: the
+ /// user picks the data sources of a chat, and a second switch for searching them would only be
+ /// a way to contradict the first one. Such a tool never appears in a selection, and it decides
+ /// on each request whether it has anything to offer.
+ ///
+ CONTEXT,
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolDefinition.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolDefinition.cs
index bee42325..c539fd4d 100644
--- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolDefinition.cs
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolDefinition.cs
@@ -12,6 +12,11 @@ public sealed class ToolDefinition
public ToolVisibilityDefinition VisibleIn { get; init; } = new();
+ ///
+ /// Whether the tool waits to be selected, or offers itself whenever the chat calls for it.
+ ///
+ public ToolActivation Activation { get; init; } = ToolActivation.SELECTION;
+
public ToolSettingsSchema SettingsSchema { get; init; } = new();
public string SystemPromptInstructions { get; init; } = string.Empty;
diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
index c671508b..b1aab738 100644
--- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
@@ -269,9 +269,19 @@ public sealed class ToolRegistry
return filtered;
}
+ ///
+ /// The tools somebody can select in this component.
+ ///
+ ///
+ /// Every selection in the app is built from this list: the one below the message field, the
+ /// defaults, the templates, and the tools the AI picks for a new assistant. A tool which offers
+ /// itself from the context of a chat is left out, because selecting it would change nothing.
+ /// The tool list of the app settings asks for all definitions instead, so an organization can
+ /// still switch such a tool off or set the trust it requires.
+ ///
public async Task> GetCatalogAsync(Components component)
{
- var definitions = this.GetDefinitionsForComponent(component);
+ var definitions = this.GetDefinitionsForComponent(component).Where(x => x.Activation is ToolActivation.SELECTION);
return await this.GetCatalogAsync(definitions);
}
diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs
index 79022924..47ebee72 100644
--- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs
+++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs
@@ -9,6 +9,7 @@ public static class ToolSelectionRules
public const string WEB_SEARCH_TOOL_ID = "web_search";
public const string READ_WEB_PAGE_TOOL_ID = "read_web_page";
public const string SEARCH_CONFLUENCE_TOOL_ID = "search_confluence";
+ public const string SEMANTIC_SEARCH_TOOL_ID = "semantic_search";
///
/// Turns a set of selected tool IDs into the set which actually runs.
@@ -19,6 +20,10 @@ public static class ToolSelectionRules
/// ToolRegistry still drops it when it is switched off or the provider's confidence is too
/// low, and Read Web Page reaches a wiki on a private or VPN address only when its host is
/// allowed there.
+ /// It also removes the tools nobody selects. Semantic Search offers itself whenever the data
+ /// sources of a chat call for it, see ToolActivation.CONTEXT; kept in a selection, it would
+ /// appear on the security card of a plugin and in its audit without the selection having any
+ /// say in whether it runs.
/// Every place which shows or stores a selection normalizes it, the tool selection fields
/// included. That way a chat, a template, a policy, or an assistant plugin shows the tools
/// which will actually run, and the audit of a plugin judges exactly those.
@@ -29,6 +34,7 @@ public static class ToolSelectionRules
if (normalized.Contains(SEARCH_CONFLUENCE_TOOL_ID))
normalized.Add(READ_WEB_PAGE_TOOL_ID);
+ normalized.Remove(SEMANTIC_SEARCH_TOOL_ID);
return normalized;
}
diff --git a/app/Tests/Tools/ToolCalling/ToolSelectionRulesTests.cs b/app/Tests/Tools/ToolCalling/ToolSelectionRulesTests.cs
index 5f5209f5..4bea5b4f 100644
--- a/app/Tests/Tools/ToolCalling/ToolSelectionRulesTests.cs
+++ b/app/Tests/Tools/ToolCalling/ToolSelectionRulesTests.cs
@@ -16,6 +16,7 @@ public sealed class ToolSelectionRulesTests
private const string SEARCH_CONFLUENCE = ToolSelectionRules.SEARCH_CONFLUENCE_TOOL_ID;
private const string READ_WEB_PAGE = ToolSelectionRules.READ_WEB_PAGE_TOOL_ID;
private const string WEB_SEARCH = ToolSelectionRules.WEB_SEARCH_TOOL_ID;
+ private const string SEMANTIC_SEARCH = ToolSelectionRules.SEMANTIC_SEARCH_TOOL_ID;
[Test]
public void SearchConfluenceBringsReadWebPageAlong()
@@ -30,6 +31,12 @@ public sealed class ToolSelectionRulesTests
Assert.That(ToolSelectionRules.NormalizeSelection([toolId]), Is.EquivalentTo(new[] { toolId }), "Only Search Confluence depends on another tool. Read Web Page in particular does not pull the search in.");
}
+ [Test]
+ public void SemanticSearchIsNeverPartOfASelection()
+ {
+ Assert.That(ToolSelectionRules.NormalizeSelection([SEMANTIC_SEARCH, WEB_SEARCH]), Is.EquivalentTo(new[] { WEB_SEARCH }), "Semantic Search offers itself from the data sources of a chat. A template or a plugin naming it would put a tool on the security card that the selection has no say over.");
+ }
+
[Test]
public void NormalizingTwiceChangesNothing()
{