using AIStudio.Provider; using AIStudio.Tools.ToolCallingSystem; namespace AIStudio.Tests.Tools.ToolCalling; /// /// Checks that the configuration of a tool collection exports as one, with the settings of each of its tools. /// /// /// An administrator exports what they configured for one entry of the tool settings. The tools of a /// collection keep their settings to themselves, so the export has to keep them apart, while the /// confidence belongs to the collection and goes under its ID. /// [TestFixture] [NonParallelizable] public sealed class ToolSettingsExportTests : ToolRegistryTestBase { private const string COLLECTION_ID = "test_collection"; private const string SEARCH_TOOL_ID = "test_search"; private const string READ_TOOL_ID = "test_read"; [Test] public async Task EachToolOfACollectionBringsItsOwnAreas() { var item = await this.CreateCollectionItemAsync(); var areas = ToolSettingsService.GetExportAreas(item.Tools); Assert.Multiple(() => { Assert.That(areas.Select(area => area.Id), Is.EqualTo(new[] { $"{SEARCH_TOOL_ID}/", $"{READ_TOOL_ID}/" }), "Both tools put their settings into the nameless area, which only the tool tells apart; the order is the collection's."); Assert.That(areas[0].Label, Does.StartWith($"{SEARCH_TOOL_ID}: "), "With two tools to choose from, each area names its tool."); Assert.That(areas[1].Label, Does.StartWith($"{READ_TOOL_ID}: ")); }); } [Test] public async Task ASingleToolNamesNoTool() { var registry = this.CreateRegistry(new TestTool(Definition(requiresSetting: true))); var item = await registry.GetCatalogItemAsync(TOOL_ID); Assert.That(ToolSettingsService.GetExportAreas(item!.Tools).Single().Label, Does.Not.StartWith(TOOL_ID), "The dialog names the tool already."); } [Test] public async Task TheExportHoldsTheSettingsOfEveryToolAndTheConfidenceOfTheCollection() { var item = await this.CreateCollectionItemAsync(); var areas = ToolSettingsService.GetExportAreas(item.Tools); var result = await this.CreateToolSettingsService().ExportAsync(item.Tools, new() { SelectedAreaIds = areas.Select(area => area.Id).ToHashSet() }, COLLECTION_ID, ConfidenceLevel.HIGH); Assert.Multiple(() => { Assert.That(result.Success, Is.True, result.ErrorMessage); Assert.That(result.LuaCode, Does.Contain($"[\"{SEARCH_TOOL_ID}.{REQUIRED_SETTING}\"] = \"https://search.example.org\"")); Assert.That(result.LuaCode, Does.Contain($"[\"{READ_TOOL_ID}.{REQUIRED_SETTING}\"] = \"https://read.example.org\"")); Assert.That(result.LuaCode, Does.Contain($"[\"{COLLECTION_ID}\"] = \"HIGH\""), "The confidence belongs to the collection."); }); } [Test] public async Task OnlyTheSelectedAreasAreExported() { var item = await this.CreateCollectionItemAsync(); var result = await this.CreateToolSettingsService().ExportAsync(item.Tools, new() { SelectedAreaIds = new HashSet { $"{READ_TOOL_ID}/" }, IncludeMinimumProviderConfidence = false }, COLLECTION_ID, ConfidenceLevel.HIGH); Assert.Multiple(() => { Assert.That(result.LuaCode, Does.Contain(READ_TOOL_ID)); Assert.That(result.LuaCode, Does.Not.Contain(SEARCH_TOOL_ID), "The area of the search was not selected."); }); } /// /// A search and a reader in one collection, each with an address saved for it. /// private async Task CreateCollectionItemAsync() { var registry = this.CreateRegistry([new TestCollection(COLLECTION_ID, ConfidenceLevel.NONE, SEARCH_TOOL_ID, READ_TOOL_ID)], new TestTool(Definition(READ_TOOL_ID, requiresSetting: true)), new TestTool(Definition(SEARCH_TOOL_ID, requiresSetting: true))); this.SettingsManager.ConfigurationData.Tools.Settings[SEARCH_TOOL_ID] = new() { [REQUIRED_SETTING] = "https://search.example.org" }; this.SettingsManager.ConfigurationData.Tools.Settings[READ_TOOL_ID] = new() { [REQUIRED_SETTING] = "https://read.example.org" }; var item = await registry.GetCatalogItemAsync(READ_TOOL_ID); Assert.That(item, Is.Not.Null); return item!; } }