diff --git a/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj b/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj index 5deea28529ca..313f00fa6bad 100644 --- a/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj +++ b/dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj @@ -5,6 +5,7 @@ Microsoft.SemanticKernel.Agents.Abstractions Microsoft.SemanticKernel.Agents net8.0;netstandard2.0 + $(NoWarn);SKEXP0110;SKEXP0001 false rc diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs b/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs index 2d0fcc21ccad..f81decfd16be 100644 --- a/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs +++ b/dotnet/src/Agents/Abstractions/Definition/AgentDefinition.cs @@ -45,7 +45,7 @@ public sealed class AgentDefinition /// /// Gets or sets the metadata associated with the agent. /// - public IDictionary? Metadata { get; set; } + public AgentMetadata? Metadata { get; set; } /// /// Gets or sets the model used by the agent. diff --git a/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs b/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs new file mode 100644 index 000000000000..69eeaea9fea3 --- /dev/null +++ b/dotnet/src/Agents/Abstractions/Definition/AgentMetadata.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Agents; + +/// +/// Defines agent metadata. +/// +[ExcludeFromCodeCoverage] +[Experimental("SKEXP0110")] +public sealed class AgentMetadata +{ + /// + /// Gets or sets the collection of authors associated with the agent. + /// + public IList? Authors { get; set; } + + /// + /// Gets or sets the collection of tags associated with the agent. + /// + public IList? Tags { get; set; } + + /// + /// Extra properties that may be included in the serialized agent metadata. + /// + /// + /// Used to store agent specific metadata. + /// + [JsonExtensionData] + public IDictionary ExtensionData + { + get => this._extensionData ??= new Dictionary(); + set + { + Verify.NotNull(value); + this._extensionData = value; + } + } + + #region private + private IDictionary? _extensionData; + #endregion +} diff --git a/dotnet/src/Agents/AzureAI/Definition/AzureAIAgentFactory.cs b/dotnet/src/Agents/AzureAI/Definition/AzureAIAgentFactory.cs index d61757b3d884..24268fc01153 100644 --- a/dotnet/src/Agents/AzureAI/Definition/AzureAIAgentFactory.cs +++ b/dotnet/src/Agents/AzureAI/Definition/AzureAIAgentFactory.cs @@ -16,7 +16,7 @@ public sealed class AzureAIAgentFactory : KernelAgentFactory /// /// The type of the Azure AI agent. /// - public const string AzureAIAgentType = "azureai_agent"; + public const string AzureAIAgentType = "foundry_agent"; /// /// Initializes a new instance of the class. diff --git a/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs b/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs new file mode 100644 index 000000000000..8e301aa1caed --- /dev/null +++ b/dotnet/src/Agents/UnitTests/Yaml/AgentDefinitionYamlTests.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Microsoft.SemanticKernel.Agents; +using Xunit; + +namespace SemanticKernel.Agents.UnitTests.Yaml; + +/// +/// Unit tests for . +/// +public class AgentDefinitionYamlTests +{ + /// + /// Verify can create an instance of from YAML text. + /// + [Fact] + public void VerifyAgentDefinitionFromYaml() + { + // Arrange + var text = + """ + version: 1.0.0 + type: chat_completion_agent + name: My Agent + description: Description of My Agent + instructions: Instructions for how My Agent works + metadata: + authors: + - Bob + - Ted + - Alice + tags: + - red + - green + - blue + created: 2025-02-21 + model: + id: gpt-4o-mini + options: + temperature: 0.4 + function_choice_behavior: + type: auto + configuration: + type: azureai + inputs: + - name: input1 + description: input1 description + - name: input2 + description: input2 description + outputs: + - description: output1 description + template: + format: liquid + parser: semantic-kernel + tools: + - name: tool1 + type: code_interpreter + - name: tool2 + type: file_search + """; + + // Act + var agentDefinition = AgentDefinitionYaml.FromYaml(text); + + // Assert + Assert.NotNull(agentDefinition); + } + + /// + /// Verify can create an instance of from YAML text. + /// + [Fact] + public void VerifyAgentDefinitionMetadataPropertiesFromYaml() + { + // Arrange + var text = + """ + version: 1.0.0 + type: chat_completion_agent + name: My Agent + description: Description of My Agent + instructions: Instructions for how My Agent works + metadata: + authors: + - Bob + - Ted + - Alice + tags: + - red + - green + - blue + created: 2025-02-21 + """; + + // Act + var agentDefinition = AgentDefinitionYaml.FromYaml(text); + + // Assert + Assert.NotNull(agentDefinition); + Assert.Equal("1.0.0", agentDefinition.Version); + Assert.Equal("chat_completion_agent", agentDefinition.Type); + Assert.Equal("My Agent", agentDefinition.Name); + Assert.Equal("Description of My Agent", agentDefinition.Description); + Assert.Equal("Instructions for how My Agent works", agentDefinition.Instructions); + Assert.NotNull(agentDefinition.Metadata); + Assert.Equal(3, agentDefinition.Metadata.Authors?.Count); + Assert.Equal(3, agentDefinition.Metadata.Tags?.Count); + Assert.Equal("2025-02-21", agentDefinition.Metadata.ExtensionData["created"]); + } +} diff --git a/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs b/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs index 01e23ae743ff..6ffb219b1468 100644 --- a/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs +++ b/dotnet/src/Agents/UnitTests/Yaml/AzureAIKernelAgentYamlTests.cs @@ -66,7 +66,7 @@ public async Task VerifyRequestIncludesToolAsync(string type) // Arrange var text = $""" - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions @@ -99,7 +99,7 @@ public async Task VerifyRequestIncludesAzureFunctionAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions @@ -147,7 +147,7 @@ public async Task VerifyRequestIncludesFunctionAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions @@ -189,7 +189,7 @@ public async Task VerifyRequestIncludesBingGroundingAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions @@ -224,7 +224,7 @@ public async Task VerifyRequestIncludesMicrosoftFabricAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions @@ -259,7 +259,7 @@ public async Task VerifyRequestIncludesOpenAPIAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions @@ -309,7 +309,7 @@ public async Task VerifyRequestIncludesSharepointGroundingAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions diff --git a/dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs b/dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs index 3bd38539957a..755d5619b968 100644 --- a/dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs +++ b/dotnet/src/Agents/UnitTests/Yaml/KernelAgentYamlTests.cs @@ -63,55 +63,6 @@ public void Dispose() this._httpClient.Dispose(); } - /// - /// Verify can create an instance of from YAML text. - /// - [Fact] - public void VerifyAgentDefinitionFromYaml() - { - // Arrange - var text = - """ - version: 1.0.0 - type: chat_completion_agent - name: ChatCompletionAgent - description: ChatCompletionAgent Description - instructions: ChatCompletionAgent Instructions - metadata: - author: Microsoft - created: 2025-02-21 - model: - id: gpt-4o-mini - options: - temperature: 0.4 - function_choice_behavior: - type: auto - configuration: - type: azureai - inputs: - - name: input1 - description: input1 description - - name: input2 - description: input2 description - outputs: - - description: output1 description - template: - format: liquid - parser: semantic-kernel - tools: - - name: tool1 - type: code_interpreter - - name: tool2 - type: file_search - """; - - // Act - var agentDefinition = AgentDefinitionYaml.FromYaml(text); - - // Assert - Assert.NotNull(agentDefinition); - } - /// /// Verify can create an instance of using /// @@ -189,7 +140,7 @@ public async Task VerifyCanCreateAzureAIAgentAsync() // Arrange var text = """ - type: azureai_agent + type: foundry_agent name: AzureAIAgent description: AzureAIAgent Description instructions: AzureAIAgent Instructions diff --git a/dotnet/src/Agents/Yaml/AgentDefinitionYaml.cs b/dotnet/src/Agents/Yaml/AgentDefinitionYaml.cs index 81316bc827bb..ad006c4c445e 100644 --- a/dotnet/src/Agents/Yaml/AgentDefinitionYaml.cs +++ b/dotnet/src/Agents/Yaml/AgentDefinitionYaml.cs @@ -23,6 +23,7 @@ public static AgentDefinition FromYaml(string text) .WithTypeConverter(new PromptExecutionSettingsTypeConverter()) .WithTypeConverter(new ModelConfigurationTypeConverter()) .WithTypeConverter(new AgentToolDefinitionTypeConverter()) + .WithTypeConverter(new AgentMetadataTypeConverter()) .Build(); return deserializer.Deserialize(text); diff --git a/dotnet/src/Agents/Yaml/AgentMetadataTypeConverter.cs b/dotnet/src/Agents/Yaml/AgentMetadataTypeConverter.cs new file mode 100644 index 000000000000..5ae1f58839cc --- /dev/null +++ b/dotnet/src/Agents/Yaml/AgentMetadataTypeConverter.cs @@ -0,0 +1,70 @@ +// 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.Name = s_deserializer.Deserialize(parser); + break; + case "description": + agentToolDefinition.Description = s_deserializer.Deserialize(parser); + break; + default: + (agentToolDefinition.Configuration ??= new Dictionary()).Add(propertyName, s_deserializer.Deserialize(parser)); + break; + } + } + parser.MoveNext(); // Move past the MappingEnd event + return agentToolDefinition; + } + + /// + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + throw new NotImplementedException(); + } + + /// + /// The YamlDotNet deserializer instance. + /// + private static IDeserializer? s_deserializer; +} diff --git a/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs b/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs index 5ae1f58839cc..2ba910a1c6fe 100644 --- a/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs +++ b/dotnet/src/Agents/Yaml/AgentToolDefinitionTypeConverter.cs @@ -10,17 +10,17 @@ namespace Microsoft.SemanticKernel.Agents; /// -/// Type converter custom deserialization for from YAML. +/// Type converter custom deserialization for from YAML. /// /// -/// Required to correctly deserialize the from YAML. +/// Required to correctly deserialize the from YAML. /// -internal sealed class AgentToolDefinitionTypeConverter : IYamlTypeConverter +internal sealed class AgentMetadataTypeConverter : IYamlTypeConverter { /// public bool Accepts(Type type) { - return type == typeof(AgentToolDefinition); + return type == typeof(AgentMetadata); } /// @@ -33,28 +33,25 @@ public bool Accepts(Type type) parser.MoveNext(); // Move to the first property - var agentToolDefinition = new AgentToolDefinition(); + var agentMetadata = new AgentMetadata(); while (parser.Current is not MappingEnd) { var propertyName = parser.Consume().Value; switch (propertyName) { - case "type": - agentToolDefinition.Type = s_deserializer.Deserialize(parser); + case "authors": + agentMetadata.Authors = s_deserializer.Deserialize>(parser); break; - case "name": - agentToolDefinition.Name = s_deserializer.Deserialize(parser); - break; - case "description": - agentToolDefinition.Description = s_deserializer.Deserialize(parser); + case "tags": + agentMetadata.Tags = s_deserializer.Deserialize>(parser); break; default: - (agentToolDefinition.Configuration ??= new Dictionary()).Add(propertyName, s_deserializer.Deserialize(parser)); + (agentMetadata.ExtensionData ??= new Dictionary()).Add(propertyName, s_deserializer.Deserialize(parser)); break; } } parser.MoveNext(); // Move past the MappingEnd event - return agentToolDefinition; + return agentMetadata; } ///