Skip to content
Open
Show file tree
Hide file tree
Changes from all 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>
<TargetFramework>net10.0</TargetFramework>
<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);

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")
}
};

// Create initial checkpoint
Console.WriteLine("--- Creating Initial Checkpoint ---");
var initialCheckpoint = await checkpointStore.CreateCheckpointAsync(
runId,
JsonSerializer.SerializeToElement(workflowState));

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);

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);

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);
var checkpointList = allCheckpoints.ToList();
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);
var childList = children.ToList();
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);
Console.WriteLine($"Current step: {retrievedData.GetProperty("CurrentStep").GetString()}");

var variables = retrievedData.GetProperty("Variables");
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