// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Workflows.Checkpointing;
///
/// JSON converter for that supports both the new
/// map shape ({ "id": ["intermediate"] }) and the legacy array shape
/// (["id1", "id2"]). Legacy-shaped payloads are read as if every id had been registered
/// as a regular (untagged) output source; output is always written in the new map shape.
///
internal sealed class WorkflowInfoOutputExecutorsConverter : JsonConverter>>
{
public override Dictionary> Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Dictionary> result = new(StringComparer.Ordinal);
if (reader.TokenType == JsonTokenType.Null)
{
return result;
}
if (reader.TokenType == JsonTokenType.StartArray)
{
// Legacy shape: a flat array of executor ids. Treat each as a registered
// (untagged) output executor.
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
{
return result;
}
if (reader.TokenType != JsonTokenType.String)
{
throw new JsonException($"Expected a string in legacy outputExecutorIds array, got {reader.TokenType}.");
}
string id = reader.GetString()!;
result[id] = [];
}
throw new JsonException("Unexpected end of legacy outputExecutorIds array.");
}
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException($"Expected object or array for outputExecutorIds, got {reader.TokenType}.");
}
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return result;
}
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException($"Expected property name in outputExecutorIds object, got {reader.TokenType}.");
}
string id = reader.GetString()!;
reader.Read();
HashSet tags = [];
if (reader.TokenType == JsonTokenType.StartArray)
{
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{
if (reader.TokenType != JsonTokenType.String)
{
throw new JsonException($"Expected a string tag, got {reader.TokenType}.");
}
tags.Add(ReadTag(reader.GetString()!));
}
}
else
{
throw new JsonException($"Expected array of tags for outputExecutorIds[{id}], got {reader.TokenType}.");
}
result[id] = tags;
}
throw new JsonException("Unexpected end of outputExecutorIds object.");
}
private static OutputTag ReadTag(string value)
{
if (string.Equals(value, OutputTag.Intermediate.Value, StringComparison.Ordinal))
{
return OutputTag.Intermediate;
}
return new OutputTag(value);
}
public override void Write(
Utf8JsonWriter writer,
Dictionary> value,
JsonSerializerOptions options)
{
writer.WriteStartObject();
foreach (KeyValuePair> kvp in value)
{
writer.WritePropertyName(kvp.Key);
writer.WriteStartArray();
foreach (OutputTag tag in kvp.Value)
{
writer.WriteStringValue(tag.Value);
}
writer.WriteEndArray();
}
writer.WriteEndObject();
}
}