Web search restrictions after internal content (#1074)

Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
Peer HogeterpandThorsten Sommer authored and GitHub committed 2026-10-18 19:39:55 +02:00
1 parent f38c4aa563
commit 1f8d72aa45
42 files changed
+1654 -228

No files matched your search

@@ -81,7 +81,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
Assert.Multiple(() =>
{
Assert.That(restricted, Is.EqualTo(new[] { "wiki_search_test" }), "The configured wiki stays allowed, the web search is left out.");
Assert.That(unrestricted, Is.EquivalentTo(new[] { "web_search_test", "wiki_search_test" }), "A chat which read no mailbox gets every tool it selected.");
Assert.That(unrestricted, Is.EquivalentTo(new[] { "web_search_test", "wiki_search_test" }), "A chat which holds no internal content gets every tool it selected.");
});
}
@@ -146,8 +146,8 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
[Test]
public void AMailboxRemovedSinceIsNotNamed()
{
var requirement = new OutboundDataRequirement(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, MAILBOX_ID);
var message = requirement.GetToolBlockedMessage([new DataSourceMailbox { Id = "another-mailbox", Name = "Private mailbox" }]);
var requirement = new OutboundDataRequirement(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, OutboundDataOrigin.MAILBOX, MAILBOX_ID);
var message = requirement.GetToolBlockedMessage(new Data { Mailboxes = [new DataSourceMailbox { Id = "another-mailbox", Name = "Private mailbox" }] });
Assert.Multiple(() =>
{
@@ -157,10 +157,61 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
});
}
[Test]
public void AConfluenceSearchIsNamedAsTheWiki()
{
var requirement = new OutboundDataRequirement(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT, OutboundDataOrigin.CONFLUENCE_WIKI, string.Empty);
var message = requirement.GetToolBlockedMessage(new Data { Mailboxes = [new DataSourceMailbox { Id = MAILBOX_ID, Name = "Work mailbox" }] });
Assert.Multiple(() =>
{
Assert.That(message, Does.Contain("Confluence wiki"), "The user learns that the wiki stands in the way.");
Assert.That(message, Does.Not.Contain("mailbox"), "No mailbox restricted the chat.");
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.GetName()));
});
}
[Test]
public void AnInternalWebPageIsNamedByItsHost()
{
var requirement = new OutboundDataRequirement(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, OutboundDataOrigin.INTERNAL_WEB_PAGE, "intranet.example.org");
var message = requirement.GetToolBlockedMessage(new Data());
Assert.Multiple(() =>
{
Assert.That(message, Does.Contain("intranet.example.org"), "The user learns which page stands in the way.");
Assert.That(message, Does.Not.Contain("mailbox"), "No mailbox restricted the chat.");
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.GetName()));
});
}
[Test]
public async Task ACallTurnedDownByTheWikiNamesTheWiki()
{
var hasRun = false;
var webSearch = new TestTool(Definition(), execute: _ =>
{
hasRun = true;
return new ToolExecutionResult { TextContent = "Results about the budget." };
}) { OutboundData = ToolOutboundData.THIRD_PARTY_QUERIES };
var thread = new ChatThread();
thread.RequireOutboundDataRestriction(new(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT, OutboundDataOrigin.CONFLUENCE_WIKI, string.Empty));
var executor = new ToolExecutor(this.CreateToolSettingsService(), NullLogger<ToolExecutor>.Instance);
var outcome = await executor.ExecuteAsync("call-1", TOOL_ID, "{}", [(webSearch.GetDefinition(), webSearch)], new NoProvider(), thread, order: 1);
Assert.Multiple(() =>
{
Assert.That(hasRun, Is.False, "The wiki content would already have left AI Studio.");
Assert.That(outcome.Trace.Status, Is.EqualTo(ToolInvocationTraceStatus.BLOCKED));
Assert.That(outcome.Content, Does.Contain("Confluence wiki"));
});
}
private static ChatThread RestrictedChat(OutboundDataRestriction restriction)
{
var thread = new ChatThread();
thread.RequireOutboundDataRestriction(new(restriction, MAILBOX_ID));
thread.RequireOutboundDataRestriction(new(restriction, OutboundDataOrigin.MAILBOX, MAILBOX_ID));
return thread;
}