Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.1.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Mcp" Version="1.0.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" />
<!-- Redis -->
<PackageVersion Include="StackExchange.Redis" Version="2.8.16" />
<!-- Test -->
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.22" />
Expand Down
3 changes: 3 additions & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@
<Project Path="samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj" />
<Project Path="samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj" />
<Project Path="samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj" />
<Project Path="samples/GettingStarted/Workflows/Checkpoint/CheckpointWithRedis/CheckpointWithRedis.csproj" />
</Folder>
<Folder Name="/Samples/GettingStarted/Workflows/HumanInTheLoop/">
<Project Path="samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj" />
Expand Down Expand Up @@ -392,6 +393,7 @@
<Project Path="src/Microsoft.Agents.AI.Mem0/Microsoft.Agents.AI.Mem0.csproj" />
<Project Path="src/Microsoft.Agents.AI.OpenAI/Microsoft.Agents.AI.OpenAI.csproj" />
<Project Path="src/Microsoft.Agents.AI.Purview/Microsoft.Agents.AI.Purview.csproj" />
<Project Path="src/Microsoft.Agents.AI.Redis/Microsoft.Agents.AI.Redis.csproj" />
<Project Path="src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
<Project Path="src/Microsoft.Agents.AI.Workflows.Declarative/Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<Project Path="src/Microsoft.Agents.AI.Workflows/Microsoft.Agents.AI.Workflows.csproj" />
Expand Down Expand Up @@ -432,6 +434,7 @@
<Project Path="tests/Microsoft.Agents.AI.Mem0.UnitTests/Microsoft.Agents.AI.Mem0.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.OpenAI.UnitTests/Microsoft.Agents.AI.OpenAI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Purview.UnitTests/Microsoft.Agents.AI.Purview.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Redis.UnitTests/Microsoft.Agents.AI.Redis.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.UnitTests/Microsoft.Agents.AI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Workflows.UnitTests/Microsoft.Agents.AI.Workflows.UnitTests.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
Comment thread
ysya marked this conversation as resolved.
Resolved
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);MEAI001</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Redis\Microsoft.Agents.AI.Redis.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows\Microsoft.Agents.AI.Workflows.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text.Json;
using Microsoft.Agents.AI.Workflows;

namespace CheckpointWithRedis;

/// <summary>
/// This sample demonstrates how to use Redis-backed checkpoint storage for workflows.
/// Key concepts:
/// - RedisCheckpointStore: A distributed, durable checkpoint store using Redis
/// - TTL (Time-To-Live): Automatic expiration of checkpoints
/// - Parent-child relationships: Linking checkpoints to track workflow history
/// </summary>
/// <remarks>
/// Pre-requisites:
/// - Redis must be running. Start it with: docker run --name redis -p 6379:6379 -d redis:7-alpine
/// - Or set REDIS_CONNECTION_STRING environment variable to your Redis instance.
/// </remarks>
public static class Program
{
public static async Task<int> Main()
{
// Configuration
var redisConnectionString = Environment.GetEnvironmentVariable("REDIS_CONNECTION_STRING") ?? "localhost:6379";
var ttl = TimeSpan.FromHours(24);
Comment thread
ysya marked this conversation as resolved.
Resolved

Console.WriteLine("=== Redis Checkpoint Storage Demo ===\n");
Console.WriteLine($"Connecting to Redis: {redisConnectionString}");

try
{
// Create checkpoint store with TTL
using var checkpointStore = RedisWorkflowExtensions.CreateRedisCheckpointStoreWithTtl(
redisConnectionString,
ttl,
keyPrefix: "workflow_checkpoints");

Console.WriteLine($"Key prefix: {checkpointStore.KeyPrefix}");
Console.WriteLine($"TTL: {checkpointStore.TimeToLive}\n");

// Sample workflow data
var runId = $"run_{Guid.NewGuid():N}";
var workflowState = new WorkflowState
{
CurrentStep = "initialize",
Variables = new Dictionary<string, object>
{
["user_input"] = "Hello, Agent!",
["timestamp"] = DateTimeOffset.UtcNow.ToString("o")
}
};
Comment thread
ysya marked this conversation as resolved.
Resolved

// Create initial checkpoint
Console.WriteLine("--- Creating Initial Checkpoint ---");
var initialCheckpoint = await checkpointStore.CreateCheckpointAsync(
runId,
JsonSerializer.SerializeToElement(workflowState));
Comment thread
ysya marked this conversation as resolved.
Resolved

Console.WriteLine($"Run ID: {runId}");
Console.WriteLine($"Checkpoint ID: {initialCheckpoint.CheckpointId}");
Console.WriteLine($"State: {workflowState.CurrentStep}\n");

// Simulate workflow progress
workflowState.CurrentStep = "processing";
workflowState.Variables["processed"] = true;
workflowState.Variables["processing_time"] = DateTimeOffset.UtcNow.ToString("o");

// Create child checkpoint (linked to parent)
Console.WriteLine("--- Creating Child Checkpoint ---");
var processingCheckpoint = await checkpointStore.CreateCheckpointAsync(
runId,
JsonSerializer.SerializeToElement(workflowState),
parent: initialCheckpoint);
Comment thread
ysya marked this conversation as resolved.
Resolved

Console.WriteLine($"Checkpoint ID: {processingCheckpoint.CheckpointId}");
Console.WriteLine($"Parent ID: {initialCheckpoint.CheckpointId}");
Console.WriteLine($"State: {workflowState.CurrentStep}\n");

// Simulate more progress
workflowState.CurrentStep = "completed";
workflowState.Variables["result"] = "Success!";
workflowState.Variables["completion_time"] = DateTimeOffset.UtcNow.ToString("o");

// Create final checkpoint
Console.WriteLine("--- Creating Final Checkpoint ---");
var finalCheckpoint = await checkpointStore.CreateCheckpointAsync(
runId,
JsonSerializer.SerializeToElement(workflowState),
parent: processingCheckpoint);
Comment thread
ysya marked this conversation as resolved.
Resolved

Console.WriteLine($"Checkpoint ID: {finalCheckpoint.CheckpointId}");
Console.WriteLine($"State: {workflowState.CurrentStep}\n");

// List all checkpoints for the run
Console.WriteLine("--- All Checkpoints for Run ---");
var allCheckpoints = await checkpointStore.RetrieveIndexAsync(runId);
Comment thread
ysya marked this conversation as resolved.
Resolved
var checkpointList = allCheckpoints.ToList();
Comment thread
ysya marked this conversation as resolved.
Resolved
Console.WriteLine($"Total checkpoints: {checkpointList.Count}");
foreach (var cp in checkpointList)
{
Console.WriteLine($" - {cp.CheckpointId}");
}

Console.WriteLine();

// List children of the initial checkpoint
Console.WriteLine("--- Children of Initial Checkpoint ---");
var children = await checkpointStore.RetrieveIndexAsync(runId, initialCheckpoint);
Comment thread
ysya marked this conversation as resolved.
Resolved
var childList = children.ToList();
Comment thread
ysya marked this conversation as resolved.
Resolved
Console.WriteLine($"Child checkpoints: {childList.Count}");
foreach (var child in childList)
{
Console.WriteLine($" - {child.CheckpointId}");
}

Console.WriteLine();

// Retrieve and display checkpoint data
Console.WriteLine("--- Retrieving Final Checkpoint Data ---");
var retrievedData = await checkpointStore.RetrieveCheckpointAsync(runId, finalCheckpoint);
Comment thread
ysya marked this conversation as resolved.
Resolved
Console.WriteLine($"Current step: {retrievedData.GetProperty("CurrentStep").GetString()}");

var variables = retrievedData.GetProperty("Variables");
Comment thread
ysya marked this conversation as resolved.
Resolved
Console.WriteLine($"User input: {variables.GetProperty("user_input").GetString()}");
Console.WriteLine($"Result: {variables.GetProperty("result").GetString()}");
Console.WriteLine($"Processed: {variables.GetProperty("processed").GetBoolean()}");

Console.WriteLine("\n=== Demo Complete ===");
}
catch (Exception ex)
{
Console.WriteLine($"\nError: {ex.Message}");
Console.WriteLine("\nMake sure Redis is running. You can start it with:");
Console.WriteLine(" docker run --name redis -p 6379:6379 -d redis:7-alpine");
return 1;
}

return 0;
}
}

/// <summary>
/// Sample workflow state class.
/// </summary>
public class WorkflowState
{
public string CurrentStep { get; set; } = string.Empty;
public Dictionary<string, object> Variables { get; set; } = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
<RootNamespace>Microsoft.Agents.AI</RootNamespace>
<NoWarn>$(NoWarn);MEAI001</NoWarn>
<VersionSuffix>preview</VersionSuffix>
</PropertyGroup>

<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectDiagnosticClassesOnLegacy>true</InjectDiagnosticClassesOnLegacy>
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
<InjectRequiredMemberOnLegacy>true</InjectRequiredMemberOnLegacy>
<InjectCompilerFeatureRequiredOnLegacy>true</InjectCompilerFeatureRequiredOnLegacy>
</PropertyGroup>

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
<Title>Microsoft Agent Framework Redis Integration</Title>
<Description>Provides Redis implementations for Microsoft Agent Framework storage abstractions including CheckpointStore.</Description>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
<ProjectReference Include="..\Microsoft.Agents.AI.Workflows\Microsoft.Agents.AI.Workflows.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="StackExchange.Redis" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.Agents.AI.Redis.UnitTests" />
</ItemGroup>

</Project>
Loading