// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.DurableTask.State;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.DurableTask;
/// Provides JSON serialization utilities and source-generated contracts for Durable Agent types.
///
///
/// This mirrors the pattern used by other libraries (e.g. WorkflowsJsonUtilities) to enable Native AOT and trimming
/// friendly serialization without relying on runtime reflection. It establishes a singleton
/// instance that is preconfigured with:
///
///
/// - baseline defaults.
/// - for default null-value suppression.
/// - to tolerate numbers encoded as strings.
/// - Chained type info resolvers from shared agent abstractions to cover cross-package types (e.g. , ).
///
///
/// Keep the list of [JsonSerializable] types in sync with the Durable Agent data model anytime new state or request/response
/// containers are introduced that must round-trip via JSON.
///
///
internal static partial class DurableAgentJsonUtilities
{
///
/// Gets the singleton used for Durable Agent serialization.
///
public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions();
///
/// Serializes a sequence of chat messages using the durable agent default options.
///
/// The messages to serialize.
/// A representing the serialized messages.
public static JsonElement Serialize(this IEnumerable messages) =>
JsonSerializer.SerializeToElement(messages, DefaultOptions.GetTypeInfo(typeof(IEnumerable)));
///
/// Deserializes chat messages from a using durable agent options.
///
/// The JSON element containing the messages.
/// The deserialized list of chat messages.
public static List DeserializeMessages(this JsonElement element) =>
(List?)element.Deserialize(DefaultOptions.GetTypeInfo(typeof(List))) ?? [];
///
/// Creates the configured instance for durable agents.
///
/// The configured options.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
private static JsonSerializerOptions CreateDefaultOptions()
{
// Base configuration from the source-generated context below.
JsonSerializerOptions options = new(JsonContext.Default.Options)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as AgentAbstractionsJsonUtilities and AIJsonUtilities
};
// Chain in shared abstractions resolver (Microsoft.Extensions.AI + Agent abstractions) so dependent types are covered.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(JsonContext.Default.Options.TypeInfoResolver!);
if (JsonSerializer.IsReflectionEnabledByDefault)
{
options.Converters.Add(new JsonStringEnumConverter());
}
options.MakeReadOnly();
return options;
}
// Keep in sync with CreateDefaultOptions above.
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
NumberHandling = JsonNumberHandling.AllowReadingFromString)]
// Durable Agent State Types
[JsonSerializable(typeof(DurableAgentState))]
[JsonSerializable(typeof(DurableAgentSession))]
// Request Types
[JsonSerializable(typeof(RunRequest))]
// Primitive / Supporting Types
[JsonSerializable(typeof(ChatMessage))]
[JsonSerializable(typeof(JsonElement))]
[ExcludeFromCodeCoverage]
internal sealed partial class JsonContext : JsonSerializerContext;
}