diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs b/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs
index 10e01190c551..ae0c3a8411dc 100644
--- a/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs
@@ -8,7 +8,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Defines an agent.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentDefinition
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentFactory.cs b/dotnet/src/Agents/Abstractions/Definition/AgentFactory.cs
index bf1f7753cbf8..e4da560233d7 100644
--- a/dotnet/src/Agents/Abstractions/Definition/AgentFactory.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/AgentFactory.cs
@@ -12,7 +12,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Represents a factory for creating instances.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public abstract class AgentFactory
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentInput.cs b/dotnet/src/Agents/Abstractions/Definition/AgentInput.cs
index 3a71e60fea9d..04a910636a22 100644
--- a/dotnet/src/Agents/Abstractions/Definition/AgentInput.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/AgentInput.cs
@@ -7,7 +7,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Represents an input for an agent.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentInput
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs b/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs
index 69eeaea9fea3..7112602be8dc 100644
--- a/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs
@@ -9,7 +9,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Defines agent metadata.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentMetadata
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentOutput.cs b/dotnet/src/Agents/Abstractions/Definition/AgentOutput.cs
index e6131bd9acf9..95935edb9639 100644
--- a/dotnet/src/Agents/Abstractions/Definition/AgentOutput.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/AgentOutput.cs
@@ -7,7 +7,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Represents an output for an agent.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentOutput
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentToolDefinition.cs b/dotnet/src/Agents/Abstractions/Definition/AgentToolDefinition.cs
index 6d0e368b355e..5617d476ee13 100644
--- a/dotnet/src/Agents/Abstractions/Definition/AgentToolDefinition.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/AgentToolDefinition.cs
@@ -9,7 +9,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// The options for defining a tool.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class AgentToolDefinition
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/ModelConnection.cs b/dotnet/src/Agents/Abstractions/Definition/ModelConnection.cs
index f97e9d7823ef..2390e985cc45 100644
--- a/dotnet/src/Agents/Abstractions/Definition/ModelConnection.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/ModelConnection.cs
@@ -9,7 +9,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Defines the connection for a model.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class ModelConnection
{
diff --git a/dotnet/src/Agents/Abstractions/Definition/ModelDefinition.cs b/dotnet/src/Agents/Abstractions/Definition/ModelDefinition.cs
index ee5db8edd3f8..92fb7460ae25 100644
--- a/dotnet/src/Agents/Abstractions/Definition/ModelDefinition.cs
+++ b/dotnet/src/Agents/Abstractions/Definition/ModelDefinition.cs
@@ -8,7 +8,6 @@ namespace Microsoft.SemanticKernel.Agents;
///
/// Defines the model to be used by an agent.
///
-[ExcludeFromCodeCoverage]
[Experimental("SKEXP0110")]
public sealed class ModelDefinition
{
@@ -30,7 +29,7 @@ public string Api
get => this._api ?? DefaultApi;
set
{
- Verify.NotNull(value);
+ Verify.NotNullOrWhiteSpace(value);
this._api = value;
}
}
diff --git a/dotnet/src/Agents/Abstractions/Extensions/AgentDefinitionExtensions.cs b/dotnet/src/Agents/Abstractions/Extensions/AgentDefinitionExtensions.cs
index 5ef0e1b8a6b1..20bf75e32f46 100644
--- a/dotnet/src/Agents/Abstractions/Extensions/AgentDefinitionExtensions.cs
+++ b/dotnet/src/Agents/Abstractions/Extensions/AgentDefinitionExtensions.cs
@@ -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()
{
@@ -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;
diff --git a/dotnet/src/Agents/UnitTests/Definition/AgentDefinitionModelTests.cs b/dotnet/src/Agents/UnitTests/Definition/AgentDefinitionModelTests.cs
new file mode 100644
index 000000000000..d9ffc8385520
--- /dev/null
+++ b/dotnet/src/Agents/UnitTests/Definition/AgentDefinitionModelTests.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using Microsoft.SemanticKernel.Agents;
+using Xunit;
+
+namespace SemanticKernel.Agents.UnitTests.Definition;
+
+///
+/// Unit testing of and related model classes.
+///
+public class AgentDefinitionModelTests
+{
+ ///
+ /// Verify ModelDefinition.Api cannot be null or whitespace.
+ ///
+ [Fact]
+ public void VerifyModelDefinitionApiNotNullOrWhiteSpace()
+ {
+ // Arrange
+ var modelDefinition = new ModelDefinition();
+
+ // Act & Assert
+ Assert.Throws(() => modelDefinition.Api = "");
+ Assert.Throws(() => modelDefinition.Api = null!);
+ }
+}
diff --git a/dotnet/src/Agents/UnitTests/Extensions/AgentDefinitionExtensionsTests.cs b/dotnet/src/Agents/UnitTests/Extensions/AgentDefinitionExtensionsTests.cs
new file mode 100644
index 000000000000..4490589b4d6c
--- /dev/null
+++ b/dotnet/src/Agents/UnitTests/Extensions/AgentDefinitionExtensionsTests.cs
@@ -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;
+
+///
+/// Unit testing of .
+///
+public class AgentDefinitionExtensionsTests
+{
+ ///
+ /// Verify default instance of can be created.
+ ///
+ [Fact]
+ public void VerifyGetDefaultKernelArguments()
+ {
+ // Arrange
+ var agentDefinition = new AgentDefinition();
+ var kernel = new Kernel();
+
+ // Act
+ var kernelArguments = agentDefinition.GetDefaultKernelArguments(kernel);
+
+ // Assert
+ Assert.NotNull(kernelArguments);
+ }
+
+ ///
+ /// Verify default instance of has function calling enabled.
+ ///
+ [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();
+
+ // 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);
+ }
+
+ ///
+ /// Verify instance of cannot be created if function is not available.
+ ///
+ [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();
+
+ // Act & Assert
+ Assert.Throws(() => agentDefinition.GetDefaultKernelArguments(kernel));
+ }
+
+ ///
+ /// Verify GetPromptTemplate returns null if there is no template factory, template or instructions.
+ ///
+ [Fact]
+ public void VerifyGetPromptTemplateReturnsNull()
+ {
+ // Arrange
+ var agentDefinition = new AgentDefinition();
+ var kernel = new Kernel();
+
+ // Act & Assert
+ Assert.Null(agentDefinition.GetPromptTemplate(kernel, null));
+ }
+
+ ///
+ /// Verify GetPromptTemplate returns null if there is no template factory, template or instructions.
+ ///
+ [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);
+ }
+
+ ///
+ /// Verify GetFirstToolDefinition returns the correct tool.
+ ///
+ [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();
+
+ // Act
+ var toolDefinition = agentDefinition.GetFirstToolDefinition("function");
+
+ // Assert
+ Assert.NotNull(toolDefinition);
+ Assert.Equal("function", toolDefinition.Type);
+ }
+
+ ///
+ /// Verify GetToolDefinitions returns the correct tools.
+ ///
+ [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();
+
+ // Act
+ var toolDefinitions = agentDefinition.GetToolDefinitions("function");
+
+ // Assert
+ Assert.NotNull(toolDefinitions);
+ Assert.Equal(2, toolDefinitions.Count());
+ }
+
+ ///
+ /// Verify HasToolType returns the correct values.
+ ///
+ [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();
+
+ // 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
+}
diff --git a/dotnet/src/Agents/UnitTests/Extensions/AgentToolDefinitionExtensionsTests.cs b/dotnet/src/Agents/UnitTests/Extensions/AgentToolDefinitionExtensionsTests.cs
new file mode 100644
index 000000000000..98d3d9f38263
--- /dev/null
+++ b/dotnet/src/Agents/UnitTests/Extensions/AgentToolDefinitionExtensionsTests.cs
@@ -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;
+
+///
+/// Unit testing of .
+///
+public class AgentToolDefinitionExtensionsTests
+{
+ ///
+ /// Verify GetOption.
+ ///
+ [Fact]
+ public void VerifyGetOption()
+ {
+ // Arrange
+ var agentToolDefinition = new AgentToolDefinition()
+ {
+ Type = "function",
+ Id = "MyPlugin.Function1",
+ Options = new Dictionary()
+ {
+ { "null", null },
+ { "string", "string" },
+ { "int", 1 },
+ { "array", new string[] { "1", "2", "3" } },
+ }
+ };
+
+ // Act & Assert
+ Assert.Null(agentToolDefinition.GetOption("null"));
+ Assert.Equal("string", agentToolDefinition.GetOption("string"));
+ Assert.Equal(1, agentToolDefinition.GetOption("int"));
+ Assert.Equal(new string[] { "1", "2", "3" }, agentToolDefinition.GetOption("array"));
+ Assert.Throws(() => agentToolDefinition.GetOption("string"));
+ Assert.Throws(() => agentToolDefinition.GetOption(null!));
+ }
+}
diff --git a/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs b/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs
index 89067749b080..c5f25c902b74 100644
--- a/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs
+++ b/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs
@@ -21,6 +21,7 @@ public void VerifyAgentDefinitionFromYaml()
// Arrange
var text =
"""
+ id: agent_12345
version: 1.0.0
type: chat_completion_agent
name: My Agent
@@ -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
diff --git a/dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs b/dotnet/src/Agents/UnitTests/Yaml/AgentYamlTests.cs
similarity index 98%
rename from dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs
rename to dotnet/src/Agents/UnitTests/Yaml/AgentYamlTests.cs
index 4a6dcf984063..d8ba153c707c 100644
--- a/dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs
+++ b/dotnet/src/Agents/UnitTests/Yaml/AgentYamlTests.cs
@@ -21,9 +21,9 @@
namespace SemanticKernel.Agents.UnitTests.Yaml;
///
-/// Unit tests for .
+/// Unit tests for .
///
-public class KernelAgentYamlTests : IDisposable
+public class AgentYamlTests : IDisposable
{
private readonly HttpMessageHandlerStub _messageHandlerStub;
private readonly HttpClient _httpClient;
@@ -32,7 +32,7 @@ public class KernelAgentYamlTests : IDisposable
///
/// Initializes a new instance of the class.
///
- public KernelAgentYamlTests()
+ public AgentYamlTests()
{
this._messageHandlerStub = new HttpMessageHandlerStub();
this._httpClient = new HttpClient(this._messageHandlerStub, disposeHandler: false);
diff --git a/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs b/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs
index 5cd09dd92c2d..5a9ff15f6fa7 100644
--- a/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs
+++ b/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs
@@ -19,7 +19,7 @@
namespace SemanticKernel.Agents.UnitTests.Yaml;
///
-/// Unit tests for with .
+/// Unit tests for with .
///
public class AzureAIKernelAgentYamlTests : IDisposable
{
diff --git a/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs b/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs
deleted file mode 100644
index 65c337fce4cf..000000000000
--- a/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using YamlDotNet.Core;
-using YamlDotNet.Core.Events;
-using YamlDotNet.Serialization;
-using YamlDotNet.Serialization.NamingConventions;
-
-namespace Microsoft.SemanticKernel.Agents;
-
-///
-/// Type converter custom deserialization for from YAML.
-///
-///
-/// Required to correctly deserialize the from YAML.
-///
-internal sealed class AgentToolDefinitionTypeConverter : IYamlTypeConverter
-{
- ///
- public bool Accepts(Type type)
- {
- return type == typeof(AgentToolDefinition);
- }
-
- ///
- public object? ReadYaml(IParser parser, Type type)
- {
- s_deserializer ??= new DeserializerBuilder()
- .WithNamingConvention(UnderscoredNamingConvention.Instance)
- .IgnoreUnmatchedProperties() // Required to ignore the 'type' property used as type discrimination. Otherwise, the "Property 'type' not found on type '{type.FullName}'" exception is thrown.
- .Build();
-
- parser.MoveNext(); // Move to the first property
-
- var agentToolDefinition = new AgentToolDefinition();
- while (parser.Current is not MappingEnd)
- {
- var propertyName = parser.Consume().Value;
- switch (propertyName)
- {
- case "type":
- agentToolDefinition.Type = s_deserializer.Deserialize(parser);
- break;
- case "name":
- agentToolDefinition.Id = s_deserializer.Deserialize(parser);
- break;
- case "description":
- agentToolDefinition.Description = s_deserializer.Deserialize(parser);
- break;
- default:
- (agentToolDefinition.Options ??= new Dictionary()).Add(propertyName, s_deserializer.Deserialize