Fixed Semantic Search with external data sources (ERI servers) (#1090)

Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
Peer HogeterpandThorsten Sommer authored and GitHub committed 2026-10-20 16:24:55 +02:00
1 parent 0f5b041729
commit f6e48e95f3
30 files changed
+1370 -141

No files matched your search

@@ -30,7 +30,7 @@ public sealed class SemanticSearchToolDescriptionTests
Assert.Multiple(() =>
{
Assert.That(function.DescriptionForLLM, Does.Contain($"id={HANDBOOK.Id}, name='Handbook', type=local folder, results per page=10, last page=9, description='Our processes.'"));
Assert.That(function.DescriptionForLLM, Does.Contain($"id={INTRANET.Id}, name='Intranet', type=external data source, results per page=10, last page=9"));
Assert.That(function.DescriptionForLLM, Does.Contain($"id={INTRANET.Id}, name='Intranet', type=external data source, results per page=10, last page=1"), "ERI v1 knows no offset, so an ERI data source has a single page.");
Assert.That(function.DescriptionForLLM, Does.Not.Contain(MINUTES.Id).And.Not.Contain("Minutes"), "A data source which is not offered must not even be named.");
Assert.That(OfferedIds(function), Is.EqualTo(new[] { HANDBOOK.Id, INTRANET.Id }), "The schema lets the model name exactly the data sources the description lists.");
});
@@ -86,11 +86,49 @@ public sealed class SemanticSearchToolDescriptionTests
Assert.That(function.DescriptionForLLM, Does.Contain($"description='{new string('x', 499)}...'"));
}
private static ToolFunctionDefinition Describe(params (IDataSource DataSource, string Description)[] dataSources)
[Test]
public void AnUnreachableDataSourceIsNamedButCannotBeSearched()
{
var function = DescribeWithUnreachable([(HANDBOOK, "Our processes.")], [INTRANET]);
Assert.Multiple(() =>
{
Assert.That(function.DescriptionForLLM, Does.Contain("could not be reached or signed in to right now"), "A wrong password keeps the data source away just as well as a server which is down.");
Assert.That(function.DescriptionForLLM, Does.Contain("- name='Intranet'"), "The model has to know what is missing, so that it can tell the user.");
Assert.That(function.DescriptionForLLM, Does.Not.Contain(INTRANET.Id), "A data source which cannot be searched gets no ID to pass.");
Assert.That(OfferedIds(function), Is.EqualTo(new[] { HANDBOOK.Id }), "The schema still lets the model name only what it may search.");
});
}
[Test]
public void OnlyUnreachableDataSourcesStillMakeAFunction()
{
var function = DescribeWithUnreachable([], [INTRANET]);
Assert.Multiple(() =>
{
Assert.That(function.DescriptionForLLM, Does.Contain("None of the data sources of this chat can be searched right now."));
Assert.That(function.DescriptionForLLM, Does.Contain("- name='Intranet'"));
Assert.That(function.DescriptionForLLM, Does.Not.Contain("The data sources you may search"));
});
}
[Test]
public void WithoutUnreachableDataSourcesTheDescriptionStaysAsBefore()
{
var withoutList = Describe((HANDBOOK, "Our processes."));
var withEmptyList = DescribeWithUnreachable([(HANDBOOK, "Our processes.")], []);
Assert.That(withEmptyList.DescriptionForLLM, Is.EqualTo(withoutList.DescriptionForLLM), "The providers cache a request from its beginning, so nothing may change while every server answers.");
}
private static ToolFunctionDefinition Describe(params (IDataSource DataSource, string Description)[] dataSources) => DescribeWithUnreachable(dataSources, []);
private static ToolFunctionDefinition DescribeWithUnreachable((IDataSource DataSource, string Description)[] dataSources, IDataSource[] unreachableDataSources)
{
// Stating its definition needs none of the services the tool searches with:
var registered = new SemanticSearchTool(null!, null!, null!, null!, null!).GetDefinition().Function;
return SemanticSearchTool.DescribeDataSources(registered, dataSources);
return SemanticSearchTool.DescribeDataSources(registered, dataSources, unreachableDataSources);
}
private static IReadOnlyList<string?> OfferedIds(ToolFunctionDefinition function) => function.Parameters