Skip to content

.Net: Unit tests for declarative agents #11399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Defines an agent.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentDefinition
{
Expand Down
1 change: 0 additions & 1 deletion dotnet/src/Agents/Abstractions/Definition/AgentFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Represents a factory for creating <see cref="Agent"/> instances.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public abstract class AgentFactory
{
Expand Down
1 change: 0 additions & 1 deletion dotnet/src/Agents/Abstractions/Definition/AgentInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Represents an input for an agent.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentInput
{
Expand Down
1 change: 0 additions & 1 deletion dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Defines agent metadata.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentMetadata
{
Expand Down
1 change: 0 additions & 1 deletion dotnet/src/Agents/Abstractions/Definition/AgentOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Represents an output for an agent.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentOutput
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// The options for defining a tool.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentToolDefinition
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Defines the connection for a model.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class ModelConnection
{
Expand Down
3 changes: 1 addition & 2 deletions dotnet/src/Agents/Abstractions/Definition/ModelDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ namespace Microsoft.SemanticKernel.Agents;
/// <summary>
/// Defines the model to be used by an agent.
/// </summary>
[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class ModelDefinition
{
Expand All @@ -30,7 +29,7 @@ public string Api
get => this._api ?? DefaultApi;
set
{
Verify.NotNull(value);
Verify.NotNullOrWhiteSpace(value);
this._api = value;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static class AgentDefinitionExtensions
public static KernelArguments GetDefaultKernelArguments(this AgentDefinition agentDefinition, Kernel kernel)
{
Verify.NotNull(agentDefinition);
Verify.NotNull(kernel);

PromptExecutionSettings executionSettings = new()
{
Expand All @@ -39,7 +40,7 @@ public static KernelArguments GetDefaultKernelArguments(this AgentDefinition age
var nameParts = FunctionName.Parse(function.Id!, FunctionNameSeparator);

// Look up the function in the kernel.
if (kernel is not null && kernel.Plugins.TryGetFunction(nameParts.PluginName, nameParts.Name, out var kernelFunction))
if (kernel.Plugins.TryGetFunction(nameParts.PluginName, nameParts.Name, out var kernelFunction))
{
kernelFunctions.Add(kernelFunction);
continue;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using Microsoft.SemanticKernel.Agents;
using Xunit;

namespace SemanticKernel.Agents.UnitTests.Definition;

/// <summary>
/// Unit testing of <see cref="AgentDefinition"/> and related model classes.
/// </summary>
public class AgentDefinitionModelTests
{
/// <summary>
/// Verify ModelDefinition.Api cannot be null or whitespace.
/// </summary>
[Fact]
public void VerifyModelDefinitionApiNotNullOrWhiteSpace()
{
// Arrange
var modelDefinition = new ModelDefinition();

// Act & Assert
Assert.Throws<ArgumentException>(() => modelDefinition.Api = "");
Assert.Throws<ArgumentNullException>(() => modelDefinition.Api = null!);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.ComponentModel;
using System.Linq;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Xunit;

namespace SemanticKernel.Agents.UnitTests.Extensions;

/// <summary>
/// Unit testing of <see cref="AgentDefinitionExtensions"/>.
/// </summary>
public class AgentDefinitionExtensionsTests
{
/// <summary>
/// Verify default instance of <see cref="KernelArguments"/> can be created.
/// </summary>
[Fact]
public void VerifyGetDefaultKernelArguments()
{
// Arrange
var agentDefinition = new AgentDefinition();
var kernel = new Kernel();

// Act
var kernelArguments = agentDefinition.GetDefaultKernelArguments(kernel);

// Assert
Assert.NotNull(kernelArguments);
}

/// <summary>
/// Verify default instance of <see cref="KernelArguments"/> has function calling enabled.
/// </summary>
[Fact]
public void VerifyGetDefaultKernelArgumentsEnablesFunctionCalling()
{
// Arrange
var agentDefinition = new AgentDefinition
{
Tools = [new() { Type = "function", Id = "MyPlugin.Function1" }]
};
var kernel = new Kernel();
var kernelPlugin = kernel.Plugins.AddFromType<MyPlugin>();

// Act
var kernelArguments = agentDefinition.GetDefaultKernelArguments(kernel);

// Assert
Assert.NotNull(kernelArguments);
Assert.NotNull(kernelArguments.ExecutionSettings);
Assert.Single(kernelArguments.ExecutionSettings);
Assert.NotNull(kernelArguments.ExecutionSettings["default"].FunctionChoiceBehavior);
var autoFunctionChoiceBehavior = kernelArguments.ExecutionSettings["default"].FunctionChoiceBehavior as AutoFunctionChoiceBehavior;
Assert.NotNull(autoFunctionChoiceBehavior);
Assert.NotNull(autoFunctionChoiceBehavior.Functions);
Assert.Single(autoFunctionChoiceBehavior.Functions);
}

/// <summary>
/// Verify instance of <see cref="KernelArguments"/> cannot be created if function is not available.
/// </summary>
[Fact]
public void VerifyGetDefaultKernelArgumentsThrowsForInvalidFunction()
{
// Arrange
var agentDefinition = new AgentDefinition
{
Tools = [new() { Type = "function", Id = "MyPlugin.Function2" }]
};
var kernel = new Kernel();
var kernelPlugin = kernel.Plugins.AddFromType<MyPlugin>();

// Act & Assert
Assert.Throws<KernelException>(() => agentDefinition.GetDefaultKernelArguments(kernel));
}

/// <summary>
/// Verify GetPromptTemplate returns null if there is no template factory, template or instructions.
/// </summary>
[Fact]
public void VerifyGetPromptTemplateReturnsNull()
{
// Arrange
var agentDefinition = new AgentDefinition();
var kernel = new Kernel();

// Act & Assert
Assert.Null(agentDefinition.GetPromptTemplate(kernel, null));
}

/// <summary>
/// Verify GetPromptTemplate returns null if there is no template factory, template or instructions.
/// </summary>
[Fact]
public void VerifyGetPromptTemplate()
{
// Arrange
var agentDefinition = new AgentDefinition()
{
Instructions = "instructions",
Template = new() { Format = "semantic-kernel" }
};
var kernel = new Kernel();
var templateFactory = new KernelPromptTemplateFactory();

// Act
var promptTemplate = agentDefinition.GetPromptTemplate(kernel, templateFactory);

// Assert
Assert.NotNull(promptTemplate);
}

/// <summary>
/// Verify GetFirstToolDefinition returns the correct tool.
/// </summary>
[Fact]
public void VerifyGetFirstToolDefinition()
{
// Arrange
var agentDefinition = new AgentDefinition
{
Tools =
[
new() { Type = "function", Id = "MyPlugin.Function1" },
new() { Type = "code_interpreter" }
]
};
var kernel = new Kernel();
var kernelPlugin = kernel.Plugins.AddFromType<MyPlugin>();

// Act
var toolDefinition = agentDefinition.GetFirstToolDefinition("function");

// Assert
Assert.NotNull(toolDefinition);
Assert.Equal("function", toolDefinition.Type);
}

/// <summary>
/// Verify GetToolDefinitions returns the correct tools.
/// </summary>
[Fact]
public void VerifyGetToolDefinitions()
{
// Arrange
var agentDefinition = new AgentDefinition
{
Tools =
[
new() { Type = "function", Id = "MyPlugin.Function1" },
new() { Type = "function", Id = "MyPlugin.Function2" },
new() { Type = "code_interpreter" }
]
};
var kernel = new Kernel();
var kernelPlugin = kernel.Plugins.AddFromType<MyPlugin>();

// Act
var toolDefinitions = agentDefinition.GetToolDefinitions("function");

// Assert
Assert.NotNull(toolDefinitions);
Assert.Equal(2, toolDefinitions.Count());
}

/// <summary>
/// Verify HasToolType returns the correct values.
/// </summary>
[Fact]
public void VerifyHasToolType()
{
// Arrange
var agentDefinition = new AgentDefinition
{
Tools =
[
new() { Type = "function", Id = "MyPlugin.Function1" },
new() { Type = "code_interpreter" }
]
};
var kernel = new Kernel();
var kernelPlugin = kernel.Plugins.AddFromType<MyPlugin>();

// Act & Assert
Assert.True(agentDefinition.HasToolType("function"));
Assert.True(agentDefinition.HasToolType("code_interpreter"));
Assert.False(agentDefinition.HasToolType("file_search"));
}

#region private
private sealed class MyPlugin
{
[KernelFunction("Function1")]
[Description("Description for function 1.")]
public string Function1([Description("Description for parameter 1")] string param1) => $"Function1: {param1}";
}
#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using Microsoft.SemanticKernel.Agents;
using Xunit;

namespace SemanticKernel.Agents.UnitTests.Extensions;

/// <summary>
/// Unit testing of <see cref="AgentToolDefinitionExtensions"/>.
/// </summary>
public class AgentToolDefinitionExtensionsTests
{
/// <summary>
/// Verify GetOption.
/// </summary>
[Fact]
public void VerifyGetOption()
{
// Arrange
var agentToolDefinition = new AgentToolDefinition()
{
Type = "function",
Id = "MyPlugin.Function1",
Options = new Dictionary<string, object?>()
{
{ "null", null },
{ "string", "string" },
{ "int", 1 },
{ "array", new string[] { "1", "2", "3" } },
}
};

// Act & Assert
Assert.Null(agentToolDefinition.GetOption<string>("null"));
Assert.Equal("string", agentToolDefinition.GetOption<string>("string"));
Assert.Equal(1, agentToolDefinition.GetOption<int>("int"));
Assert.Equal(new string[] { "1", "2", "3" }, agentToolDefinition.GetOption<string[]>("array"));
Assert.Throws<InvalidCastException>(() => agentToolDefinition.GetOption<string[]>("string"));
Assert.Throws<ArgumentNullException>(() => agentToolDefinition.GetOption<string>(null!));
}
}
3 changes: 3 additions & 0 deletions dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public void VerifyAgentDefinitionFromYaml()
// Arrange
var text =
"""
id: agent_12345
version: 1.0.0
type: chat_completion_agent
name: My Agent
Expand Down Expand Up @@ -63,8 +64,10 @@ public void VerifyAgentDefinitionFromYaml()
tools:
- id: tool1
type: code_interpreter
description: Code interpreter tool
- id: tool2
type: file_search
description: File search tool
""";

// Act
Expand Down
Loading
Loading