Merge branch 'main' into feature-foundry-agents

This commit is contained in:
Chris
2025-11-06 18:48:17 -08:00
committed by GitHub
Unverified
6 changed files with 46 additions and 6 deletions
@@ -50,12 +50,16 @@ public static partial class AgentAbstractionsJsonUtilities
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as AIJsonUtilities
};
// Chain with all supported types from Microsoft.Extensions.AI.Abstractions.
// Chain in the resolvers from both AIJsonUtilities and our source generated context.
// We want AIJsonUtilities first to ensure any M.E.AI types are handled via its resolver.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AIJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(JsonContext.Default.Options.TypeInfoResolver!);
// If reflection-based serialization is enabled by default, this includes
// the default type info resolver that utilizes reflection, but we need to manually
// apply the same converter AIJsonUtilities adds for string-based enum serialization,
// as that's not propagated as part of the resolver.
options.TypeInfoResolverChain.Add(AIJsonUtilities.DefaultOptions.TypeInfoResolver!);
if (JsonSerializer.IsReflectionEnabledByDefault)
{
options.Converters.Add(new JsonStringEnumConverter());
@@ -17,7 +17,13 @@ internal static class ChatCompletionsJsonSerializerOptions
private static JsonSerializerOptions Create()
{
JsonSerializerOptions options = new(ChatCompletionsJsonContext.Default.Options);
// Chain in the resolvers from both AgentAbstractionsJsonUtilities and our source generated context.
// We want AgentAbstractionsJsonUtilities first to ensure any M.E.AI types are handled via its resolver.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(ChatCompletionsJsonContext.Default.Options.TypeInfoResolver!);
options.MakeReadOnly();
return options;
}
@@ -24,7 +24,13 @@ internal static class OpenAIHostingJsonUtilities
private static JsonSerializerOptions CreateDefaultOptions()
{
JsonSerializerOptions options = new(OpenAIHostingJsonContext.Default.Options);
// Chain in the resolvers from both AgentAbstractionsJsonUtilities and our source generated context.
// We want AgentAbstractionsJsonUtilities first to ensure any M.E.AI types are handled via its resolver.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(OpenAIHostingJsonContext.Default.Options.TypeInfoResolver!);
options.MakeReadOnly();
return options;
}
@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Mem0;
@@ -44,8 +43,12 @@ public static partial class Mem0JsonUtilities
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as in AIJsonUtilities
};
// Chain with all supported types from Microsoft.Extensions.AI.Abstractions.
options.TypeInfoResolverChain.Add(AIJsonUtilities.DefaultOptions.TypeInfoResolver!);
// Chain in the resolvers from both AgentAbstractionsJsonUtilities and our source generated context.
// We want AgentAbstractionsJsonUtilities first to ensure any M.E.AI types are handled via its resolver.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(JsonContext.Default.Options.TypeInfoResolver!);
if (JsonSerializer.IsReflectionEnabledByDefault)
{
options.Converters.Add(new JsonStringEnumConverter());
@@ -44,8 +44,12 @@ internal static partial class AgentJsonUtilities
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as in AgentAbstractionsJsonUtilities and AIJsonUtilities
};
// Chain with all supported types from Microsoft.Agents.AI.Abstractions.
// Chain in the resolvers from both AgentAbstractionsJsonUtilities and our source generated context.
// We want AgentAbstractionsJsonUtilities first to ensure any M.E.AI types are handled via its resolver.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(JsonContext.Default.Options.TypeInfoResolver!);
if (JsonSerializer.IsReflectionEnabledByDefault)
{
options.Converters.Add(new JsonStringEnumConverter());
@@ -114,6 +114,23 @@ public class InMemoryChatMessageStoreTests
Assert.Equal("B", newStore[1].Text);
}
[Fact]
public async Task SerializeAndDeserializeWorksWithExperimentalContentTypesAsync()
{
var store = new InMemoryChatMessageStore
{
new ChatMessage(ChatRole.User, [new FunctionApprovalRequestContent("call123", new FunctionCallContent("call123", "some_func"))]),
new ChatMessage(ChatRole.Assistant, [new FunctionApprovalResponseContent("call123", true, new FunctionCallContent("call123", "some_func"))])
};
var jsonElement = store.Serialize();
var newStore = new InMemoryChatMessageStore(jsonElement);
Assert.Equal(2, newStore.Count);
Assert.IsType<FunctionApprovalRequestContent>(newStore[0].Contents[0]);
Assert.IsType<FunctionApprovalResponseContent>(newStore[1].Contents[0]);
}
[Fact]
public async Task AddMessagesAsyncWithEmptyMessagesDoesNotChangeStoreAsync()
{