using Elsa.AI.Abstractions.Contracts; using Elsa.AI.Abstractions.Models; using Elsa.AI.Copilot.Adapters; using GitHub.Copilot; using Microsoft.Extensions.AI; using System.Text.Json.Nodes; namespace Elsa.AI.Copilot.UnitTests; public class ElsaCopilotToolFunctionTests { [Fact(DisplayName = "Tool function preserves Elsa metadata and schema")] public void ToolFunctionPreservesElsaMetadataAndSchema() { var definition = CreateDefinition(); var function = new ElsaCopilotToolFunction(definition, new CapturingToolInvoker()); Assert.Equal("workflow.inspect", function.Name); Assert.Equal("Inspect workflow", function.Description); Assert.Equal("workflowId", function.JsonSchema.GetProperty("required")[0].GetString()); var additionalProperties = function.AdditionalProperties!; Assert.Equal("Proposal", additionalProperties["elsa_mutability"]); Assert.Equal("Medium", additionalProperties["elsa_danger_level"]); Assert.False((bool)additionalProperties["skip_permission"]!); } [Fact(DisplayName = "Tool function invokes Elsa provider tool invoker")] public async Task ToolFunctionInvokesElsaProviderToolInvoker() { var invoker = new CapturingToolInvoker(); var function = new ElsaCopilotToolFunction(CreateDefinition(), invoker); var arguments = new AIFunctionArguments(new Dictionary { ["workflowId"] = "workflow-1" }) { Context = new Dictionary { ["copilotInvocation"] = new ToolInvocation { ToolCallId = "sdk-tool-call-1" } } }; var result = await function.InvokeAsync(arguments); var toolResult = Assert.IsType(result); Assert.Equal("Workflow inspected", toolResult.Result.TextResultForLlm); var invocation = Assert.Single(invoker.Invocations); Assert.Equal("sdk-tool-call-1", invocation.Id); Assert.Equal("workflow.inspect", invocation.ToolName); Assert.Equal("workflow-1", invocation.Arguments["workflowId"]!.GetValue()); } private static AIToolDefinition CreateDefinition() => new() { Name = "workflow.inspect", Description = "Inspect workflow", Mutability = AIToolMutability.Proposal, DangerLevel = AIToolDangerLevel.Medium, Schema = new JsonObject { ["type"] = "object", ["required"] = new JsonArray("workflowId"), ["properties"] = new JsonObject { ["workflowId"] = new JsonObject { ["type"] = "string" } } } }; private class CapturingToolInvoker : IAIProviderToolInvoker { public List Invocations { get; } = []; public ValueTask InvokeAsync(AIProviderToolInvocation invocation, CancellationToken cancellationToken = default) { Invocations.Add(invocation); return ValueTask.FromResult(new AIToolResult { Summary = "Workflow inspected" }); } } }