From 6655118d6fdf1c0b5fe584e478e2bfdf64fb7098 Mon Sep 17 00:00:00 2001 From: UniversePeak <113168673+UniversePeak@users.noreply.github.com> Date: Fri, 4 Sep 2026 08:29:16 +0800 Subject: [PATCH 1/2] docs: clarify declarative workflow input serialization --- .../Declarative/AotCheckpointing/Program.cs | 22 +++++++--- .../Declarative/AotCheckpointing/README.md | 44 +++++++++++++++++-- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/dotnet/samples/03-workflows/Declarative/AotCheckpointing/Program.cs b/dotnet/samples/03-workflows/Declarative/AotCheckpointing/Program.cs index 2bfd6d51d72..8f515b7d866 100644 --- a/dotnet/samples/03-workflows/Declarative/AotCheckpointing/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/AotCheckpointing/Program.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; +using System.Text.Json.Serialization; using Azure.AI.Projects; using Azure.AI.Projects.Agents; using Azure.Identity; @@ -15,12 +17,12 @@ namespace Demo.Workflows.Declarative.AotCheckpointing; /// /// Demonstrates JSON checkpointing of a declarative workflow under reflection-disabled -/// (the AOT / trim-aggressive constraint set +/// (the AOT / trim-aggressive constraint set /// via JsonSerializerIsReflectionEnabledByDefault=false in the csproj). /// /// -/// The key call is -/// with . Drop the options argument to observe the AOT failure. See README. +/// The key call is with +/// . Drop the options argument to observe the AOT failure. See README. /// internal sealed class Program { @@ -31,14 +33,14 @@ public static async Task Main(string[] args) await CreateGreeterAgentAsync(foundryEndpoint, configuration); - string workflowInput = Application.GetInput(args); + WorkflowInput workflowInput = new(Application.GetInput(args)); Workflow CreateWorkflow() { AzureAgentProvider agentProvider = new(foundryEndpoint, new AzureCliCredential()); DeclarativeWorkflowOptions options = new(agentProvider) { Configuration = configuration }; string workflowPath = Path.Combine(AppContext.BaseDirectory, "AotCheckpointing.yaml"); - return DeclarativeWorkflowBuilder.Build(workflowPath, options); + return DeclarativeWorkflowBuilder.Build(workflowPath, options, TransformInput); } DirectoryInfo checkpointFolder = Directory.CreateDirectory(Path.Combine(".", $"chk-{DateTime.Now:yyMMdd-HHmmss-ff}")); @@ -74,7 +76,10 @@ Workflow CreateWorkflow() } } - private static async Task> RunAndStreamAsync(Workflow workflow, string input, CheckpointManager checkpointManager) + private static ChatMessage TransformInput(WorkflowInput input) => + new(ChatRole.User, JsonSerializer.Serialize(input, AotCheckpointingJsonContext.Default.WorkflowInput)); + + private static async Task> RunAndStreamAsync(Workflow workflow, WorkflowInput input, CheckpointManager checkpointManager) { StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, input, checkpointManager).ConfigureAwait(false); return await DrainAsync(run).ConfigureAwait(false); @@ -172,3 +177,8 @@ private static void TryDelete(DirectoryInfo directory) } } } + +internal sealed record WorkflowInput(string Message); + +[JsonSerializable(typeof(WorkflowInput))] +internal sealed partial class AotCheckpointingJsonContext : JsonSerializerContext; diff --git a/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md b/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md index 9856274bfc8..421e39b91b5 100644 --- a/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md +++ b/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md @@ -25,15 +25,53 @@ reflection-disabled `System.Text.Json` -- the same constraint imposed by return is the proof JSON **reads** round-trip too. The resumed run is disposed immediately; without a pending external request it would park in `WaitForInputAsync` indefinitely. +- The initial workflow input is a `WorkflowInput` record. `TransformInput` uses the + source-generated `AotCheckpointingJsonContext` to serialize it into a + `ChatMessage` before the declarative workflow runs. `DeclarativeWorkflowJsonOptions` is marked `[Experimental("MAAI001")]`. Suppress that diagnostic in your csproj to use it. -### Registering user-defined types +### Initial input and checkpoint serialization are separate -For workflows whose inputs or custom `ActionExecutorResult.Result` -payloads are user-defined, clone `Default` and append your own resolver: +`DeclarativeWorkflowBuilder.Build` accepts an optional `inputTransform` delegate. +For a non-`ChatMessage` input, the default behavior is to call `ToString()`; the +checkpoint serializer is not involved in this conversion. Use a source-generated +context (or your own `JsonSerializerOptions`) in the delegate when the workflow +should receive a JSON representation of a typed input: + +```csharp +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Agents.AI.Workflows.Declarative; +using Microsoft.Extensions.AI; + +internal sealed record WorkflowInput(string Message); + +[JsonSerializable(typeof(WorkflowInput))] +internal sealed partial class AppJsonContext : JsonSerializerContext; + +Workflow workflow = DeclarativeWorkflowBuilder.Build( + workflowPath, + options, + input => new ChatMessage( + ChatRole.User, + JsonSerializer.Serialize(input, AppJsonContext.Default.WorkflowInput))); +``` + +This sample uses `AotCheckpointingJsonContext` for that initial-input transform. +The separate `DeclarativeWorkflowJsonOptions.Default` passed to +`CheckpointManager.CreateJson` supplies type information for declarative workflow +checkpoint state. If checkpoint state also contains application-defined payloads, +clone those options and append the application's resolver as shown below; adding +the resolver to checkpoint options does not automatically change the initial input. + +### Registering user-defined checkpoint types + +For custom `ActionExecutorResult.Result` payloads or other user-defined values +that are persisted in workflow checkpoint state, clone `Default` and append your +own resolver: ```csharp JsonSerializerOptions options = new(DeclarativeWorkflowJsonOptions.Default); From d5edf334f4e27b76ef0dc133f076bd8226dde34c Mon Sep 17 00:00:00 2001 From: UniversePeak <113168673+UniversePeak@users.noreply.github.com> Date: Sat, 5 Sep 2026 01:57:10 +0800 Subject: [PATCH 2/2] docs: add workflows namespace to serialization snippet --- .../samples/03-workflows/Declarative/AotCheckpointing/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md b/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md index 421e39b91b5..b601f40299d 100644 --- a/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md +++ b/dotnet/samples/03-workflows/Declarative/AotCheckpointing/README.md @@ -44,6 +44,7 @@ should receive a JSON representation of a typed input: ```csharp using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.Agents.AI.Workflows; using Microsoft.Agents.AI.Workflows.Declarative; using Microsoft.Extensions.AI;