diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs index 001cfd9469..7828b5c62d 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs @@ -291,6 +291,15 @@ public AgentRunResponseUpdate[] ToAgentRunResponseUpdates() return updates; } + /// + /// Deserializes the response text into the given type. + /// + /// The output type to deserialize into. + /// The result as the requested type. + /// The result is not parsable into the requested type. + public T Deserialize() => + this.Deserialize(AgentAbstractionsJsonUtilities.DefaultOptions); + /// /// Deserializes the response text into the given type using the specified serializer options. /// @@ -311,6 +320,15 @@ public T Deserialize(JsonSerializerOptions serializerOptions) }; } + /// + /// Tries to deserialize response text into the given type. + /// + /// The output type to deserialize into. + /// The parsed structured output. + /// if parsing was successful; otherwise, . + public bool TryDeserialize([NotNullWhen(true)] out T? structuredOutput) => + this.TryDeserialize(AgentAbstractionsJsonUtilities.DefaultOptions, out structuredOutput); + /// /// Tries to deserialize response text into the given type using the specified serializer options. /// diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs index 4a72d66f2d..dd1ff3b228 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs @@ -80,7 +80,7 @@ public ChatClientAgentOptions Clone() /// /// Context object passed to the to create a new instance of . /// - public class AIContextProviderFactoryContext + public sealed class AIContextProviderFactoryContext { /// /// Gets or sets the serialized state of the , if any. @@ -97,7 +97,7 @@ public class AIContextProviderFactoryContext /// /// Context object passed to the to create a new instance of . /// - public class ChatMessageStoreFactoryContext + public sealed class ChatMessageStoreFactoryContext { /// /// Gets or sets the serialized state of the chat message store, if any. diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunResponse{T}.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunResponse{T}.cs index 13b536a457..352be764eb 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunResponse{T}.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunResponse{T}.cs @@ -40,7 +40,6 @@ public ChatClientAgentRunResponse(ChatResponse response) : base(response) /// /// /// If the response did not contain JSON, or if deserialization fails, this property will throw. - /// To avoid exceptions, use instead. /// public override T Result => this._response.Result; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRunResponseTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRunResponseTests.cs index 981f1e3933..a81446d062 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRunResponseTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRunResponseTests.cs @@ -214,6 +214,12 @@ public void ToAgentRunResponseUpdatesProducesUpdates() Assert.Equal(100, usageContent.Details.TotalTokenCount); } +#if NETFRAMEWORK + /// + /// Since Json Serialization using reflection is disabled in .net core builds, and we are using a custom type here that wouldn't + /// be registered with the default source generated serializer, this test will only pass in .net framework builds where reflection-based + /// serialization is available. + /// [Fact] public void ParseAsStructuredOutputSuccess() { @@ -221,6 +227,24 @@ public void ParseAsStructuredOutputSuccess() var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger }; var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal))); + // Act. + var animal = response.Deserialize(); + + // Assert. + Assert.NotNull(animal); + Assert.Equal(expectedResult.Id, animal.Id); + Assert.Equal(expectedResult.FullName, animal.FullName); + Assert.Equal(expectedResult.Species, animal.Species); + } +#endif + + [Fact] + public void ParseAsStructuredOutputWithJSOSuccess() + { + // Arrange. + var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger }; + var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal))); + // Act. var animal = response.Deserialize(TestJsonSerializerContext.Default.Options); @@ -262,6 +286,12 @@ public void ParseAsStructuredOutputFailsWithIncorrectTypedJson() Assert.Throws(() => response.Deserialize(TestJsonSerializerContext.Default.Options)); } +#if NETFRAMEWORK + /// + /// Since Json Serialization using reflection is disabled in .net core builds, and we are using a custom type here that wouldn't + /// be registered with the default source generated serializer, this test will only pass in .net framework builds where reflection-based + /// serialization is available. + /// [Fact] public void TryParseAsStructuredOutputSuccess() { @@ -269,6 +299,24 @@ public void TryParseAsStructuredOutputSuccess() var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger }; var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal))); + // Act. + response.TryDeserialize(out Animal? animal); + + // Assert. + Assert.NotNull(animal); + Assert.Equal(expectedResult.Id, animal.Id); + Assert.Equal(expectedResult.FullName, animal.FullName); + Assert.Equal(expectedResult.Species, animal.Species); + } +#endif + + [Fact] + public void TryParseAsStructuredOutputWithJSOSuccess() + { + // Arrange. + var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger }; + var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal))); + // Act. response.TryDeserialize(TestJsonSerializerContext.Default.Options, out Animal? animal);