Skip to content

Commit 41d4414

Browse files
authored
Initial draft of actor runtime abstractions (#197)
* Initial draft of actor runtime abstractions
1 parent 8f2d3da commit 41d4414

108 files changed

Lines changed: 7445 additions & 58 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/dotnet-build-and-test.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
run: |
6464
export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | tr '\n' ' ')
6565
for solution in $SOLUTIONS; do
66-
dotnet build $solution -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --warnaserror
66+
dotnet build $solution -c ${{ matrix.configuration }} --warnaserror
6767
done
6868
- name: Package install check
6969
shell: bash
@@ -78,7 +78,7 @@ jobs:
7878
dotnet pack $solution /property:TargetFrameworks=${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build --no-restore --output "$TEMP_DIR/artifacts"
7979
done
8080
81-
cd "$TEMP_DIR"
81+
pushd "$TEMP_DIR"
8282
8383
# Create a new console app to test the package installation
8484
dotnet new console -f ${{ matrix.targetFramework }} --name packcheck --output consoleapp
@@ -91,13 +91,13 @@ jobs:
9191
dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org --configfile consoleapp/nuget.config
9292
9393
# Change to project directory to ensure local nuget.config is used
94-
cd consoleapp
94+
pushd consoleapp
9595
dotnet add packcheck.csproj package Microsoft.Extensions.AI.Agents --prerelease
9696
dotnet build -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} packcheck.csproj
9797
9898
# Clean up
99-
cd ..
100-
cd ..
99+
popd
100+
popd
101101
rm -rf "$TEMP_DIR"
102102
103103
- name: Run Unit Tests Windows

dotnet/Directory.Packages.props

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,59 @@
33
<!-- Enable central package management -->
44
<!-- https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
55
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
6+
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
67
</PropertyGroup>
78
<ItemGroup>
89
<!-- Azure.* -->
10+
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="9.3.1-preview.1.25305.6" />
11+
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.3.1" />
12+
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="9.3.1" />
913
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.1.0-beta.4" />
1014
<PackageVersion Include="Azure.AI.OpenAI" Version="2.2.0-beta.5" />
11-
<PackageVersion Include="Azure.Identity" Version="1.14.0" />
15+
<PackageVersion Include="Azure.Identity" Version="1.14.2" />
16+
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="9.6.0" />
17+
<PackageVersion Include="Microsoft.Extensions.AI.AzureAIInference" Version="9.7.1-preview.1.25365.4" />
18+
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.7.0" />
19+
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="9.3.1" />
20+
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
21+
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
22+
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
23+
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
1224
<!-- System.* -->
13-
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
14-
<PackageVersion Include="System.Text.Json" Version="8.0.6" />
25+
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
26+
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
1527
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.7" />
1628
<PackageVersion Include="System.Threading.Channels" Version="9.0.7" />
1729
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
1830
<!-- OpenTelemetry -->
19-
<PackageVersion Include="OpenTelemetry" Version="1.9.0" />
20-
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
21-
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.9.0" />
22-
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
31+
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
32+
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
33+
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.12.0" />
34+
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
2335
<!-- Microsoft.Extensions.* -->
2436
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
25-
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.7.0" />
26-
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.7.0-preview.1.25356.2" />
27-
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.7.0" />
37+
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.7.1" />
38+
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.7.1-preview.1.25365.4" />
39+
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.7.1" />
2840
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.7" />
2941
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.7" />
3042
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.7" />
3143
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.7" />
3244
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.7" />
3345
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.7" />
34-
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
46+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
3547
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
36-
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
37-
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
48+
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
49+
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.7" />
3850
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
3951
<PackageVersion Include="Microsoft.Extensions.Logging.Testing" Version="9.0.7" />
4052
<!-- Agent SDKs -->
4153
<PackageVersion Include="Microsoft.Agents.CopilotStudio.Client" Version="1.1.125-beta" />
4254
<!-- Identity -->
43-
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.67.2" />
55+
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.74.1" />
4456
<!-- Test -->
45-
<PackageVersion Include="FluentAssertions" Version="8.2.0" />
46-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
57+
<PackageVersion Include="FluentAssertions" Version="8.5.0" />
58+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
4759
<PackageVersion Include="Moq" Version="[4.18.4]" />
4860
<PackageVersion Include="xunit" Version="2.9.2" />
4961
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
@@ -59,12 +71,12 @@
5971
<PrivateAssets>all</PrivateAssets>
6072
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6173
</PackageReference>
62-
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.12.19" />
74+
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15" />
6375
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers">
6476
<PrivateAssets>all</PrivateAssets>
6577
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6678
</PackageReference>
67-
<PackageVersion Include="xunit.analyzers" Version="1.17.0" />
79+
<PackageVersion Include="xunit.analyzers" Version="1.23.0" />
6880
<PackageReference Include="xunit.analyzers">
6981
<PrivateAssets>all</PrivateAssets>
7082
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

dotnet/agent-framework-dotnet.slnx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@
3434
<BuildType Solution="Publish|*" Project="Debug" />
3535
</Project>
3636
</Folder>
37+
<Folder Name="/Samples/HelloHttpApi/">
38+
<Project Path="samples/HelloHttpApi/HelloHttpApi.ApiService/HelloHttpApi.ApiService.csproj">
39+
<BuildType Solution="Publish|*" Project="Release" />
40+
</Project>
41+
<Project Path="samples/HelloHttpApi/HelloHttpApi.AppHost/HelloHttpApi.AppHost.csproj" Id="17db642e-bf19-413c-bdb1-ccbccc753d7b">
42+
<BuildType Solution="Publish|*" Project="Release" />
43+
</Project>
44+
<Project Path="samples/HelloHttpApi/HelloHttpApi.ServiceDefaults/HelloHttpApi.ServiceDefaults.csproj">
45+
<BuildType Solution="Publish|*" Project="Release" />
46+
</Project>
47+
<Project Path="samples/HelloHttpApi/HelloHttpApi.Web/HelloHttpApi.Web.csproj">
48+
<BuildType Solution="Publish|*" Project="Release" />
49+
</Project>
50+
</Folder>
3751
<Folder Name="/Solution Items/">
3852
<File Path=".editorconfig" />
3953
<File Path=".gitignore" />
@@ -130,6 +144,9 @@
130144
<Project Path="src/Microsoft.Extensions.AI.Agents.AzureAI/Microsoft.Extensions.AI.Agents.AzureAI.csproj" />
131145
<Project Path="src/Microsoft.Extensions.AI.Agents.CopilotStudio/Microsoft.Extensions.AI.Agents.CopilotStudio.csproj" />
132146
<Project Path="src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.csproj" />
147+
<Project Path="src/Microsoft.Extensions.AI.Agents.Runtime/Microsoft.Extensions.AI.Agents.Runtime.csproj" Id="35d72ad5-61e1-45cc-a9ad-fa8490dbd146">
148+
<BuildType Solution="Publish|*" Project="Release" />
149+
</Project>
133150
<Project Path="src/Microsoft.Extensions.AI.Agents/Microsoft.Extensions.AI.Agents.csproj" />
134151
</Folder>
135152
<Folder Name="/UnitTests/">

dotnet/samples/Directory.Build.props

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,8 @@
44

55
<PropertyGroup>
66
<IsPackable>false</IsPackable>
7-
<IsTestProject>true</IsTestProject>
87
<IsAotCompatible>false</IsAotCompatible>
98
<ProjectsTargetFrameworks>net472;net9.0</ProjectsTargetFrameworks>
109
</PropertyGroup>
1110

12-
<ItemGroup>
13-
<PackageReference Include="coverlet.collector" />
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" />
15-
<PackageReference Include="Moq" />
16-
<PackageReference Include="xunit" />
17-
<PackageReference Include="xunit.runner.visualstudio" />
18-
<PackageReference Include="System.Linq.Async" />
19-
</ItemGroup>
20-
21-
<ItemGroup>
22-
<Using Include="Xunit" />
23-
<Using Include="Xunit.Abstractions" />
24-
</ItemGroup>
25-
2611
</Project>

dotnet/samples/GettingStarted/External/MEAI.OpenAI/NewOpenAIAssistantChatClient.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,15 @@
66
// Licensed to the .NET Foundation under one or more agreements.
77
// The .NET Foundation licenses this file to you under the MIT license.
88

9-
using System;
10-
using System.Collections.Generic;
11-
using System.Diagnostics.CodeAnalysis;
12-
using System.Linq;
139
using System.Reflection;
1410
using System.Runtime.CompilerServices;
1511
using System.Text;
1612
using System.Text.Json;
1713
using System.Text.Json.Nodes;
1814
using System.Text.Json.Serialization;
19-
using System.Threading;
20-
using System.Threading.Tasks;
2115
using Microsoft.Shared.Diagnostics;
2216
using OpenAI;
2317
using OpenAI.Assistants;
24-
using OpenAI.Audio;
25-
using OpenAI.Chat;
26-
using OpenAI.Embeddings;
2718

2819
#pragma warning disable CA1031 // Do not catch general exception types
2920
#pragma warning disable SA1005 // Single line comments should begin with single space

dotnet/samples/GettingStarted/GettingStarted.csproj

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<PropertyGroup>
4-
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
5-
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
6-
</PropertyGroup>
7-
83
<PropertyGroup>
94
<RootNamespace>GettingStarted</RootNamespace>
105
<OutputType>Library</OutputType>
116
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
127
<NoWarn>$(NoWarn);CA1707;CA1716;IDE0009;IDE1006;OPENAI001;</NoWarn>
138
<ImplicitUsings>enable</ImplicitUsings>
149
<InjectSharedSamples>true</InjectSharedSamples>
10+
<InjectSharedThrow>true</InjectSharedThrow>
11+
<IsTestProject>true</IsTestProject>
12+
</PropertyGroup>
13+
14+
<PropertyGroup>
15+
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
16+
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
1517
</PropertyGroup>
1618

1719
<ItemGroup>
@@ -29,6 +31,12 @@
2931
<PackageReference Include="OpenTelemetry" />
3032
<PackageReference Include="OpenTelemetry.Exporter.Console" />
3133
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
34+
<PackageReference Include="coverlet.collector" />
35+
<PackageReference Include="Microsoft.NET.Test.Sdk" />
36+
<PackageReference Include="Moq" />
37+
<PackageReference Include="xunit" />
38+
<PackageReference Include="xunit.runner.visualstudio" />
39+
<PackageReference Include="System.Linq.Async" />
3240
</ItemGroup>
3341

3442
<ItemGroup>
@@ -42,11 +50,11 @@
4250
<Using Include="Microsoft.Shared.SampleUtilities" />
4351
</ItemGroup>
4452

45-
<PropertyGroup>
46-
<InjectSharedThrow>true</InjectSharedThrow>
47-
</PropertyGroup>
53+
<ItemGroup>
54+
<Using Include="Xunit" />
55+
<Using Include="Xunit.Abstractions" />
56+
</ItemGroup>
4857

49-
5058
<ItemGroup>
5159
<None Update="Resources\*">
5260
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System.Text.Json;
4+
5+
namespace Microsoft.Extensions.AI.Agents.Runtime.Samples;
6+
7+
/// <summary>
8+
/// Example demonstrating how to use the InMemoryActorStateStorage.
9+
/// </summary>
10+
public static class InMemoryActorStateStorageExample
11+
{
12+
/// <summary>
13+
/// Demonstrates the basic usage of InMemoryActorStateStorage.
14+
/// </summary>
15+
/// <returns>A task representing the asynchronous operation.</returns>
16+
public static async Task RunAsync()
17+
{
18+
// Create an in-memory actor state storage
19+
var storage = new InMemoryActorStateStorage();
20+
21+
// Create an actor ID
22+
var actorId = new ActorId("ExampleActor", "instance1");
23+
24+
Console.WriteLine("=== InMemoryActorStateStorage Example ===");
25+
Console.WriteLine();
26+
27+
// 1. Write some initial state
28+
Console.WriteLine("1. Writing initial state...");
29+
var initialOperations = new List<ActorStateWriteOperation>
30+
{
31+
new SetValueOperation("name", JsonSerializer.SerializeToElement("John Doe")),
32+
new SetValueOperation("age", JsonSerializer.SerializeToElement(30)),
33+
new SetValueOperation("city", JsonSerializer.SerializeToElement("Seattle"))
34+
};
35+
36+
var writeResult = await storage.WriteStateAsync(actorId, initialOperations, "0").ConfigureAwait(false);
37+
Console.WriteLine($" Write successful: {writeResult.Success}");
38+
Console.WriteLine($" New ETag: {writeResult.ETag}");
39+
Console.WriteLine($" Actor count: {storage.ActorCount}");
40+
Console.WriteLine($" Key count for actor: {storage.GetKeyCount(actorId)}");
41+
Console.WriteLine();
42+
43+
// 2. Read the state back
44+
Console.WriteLine("2. Reading state back...");
45+
var readOperations = new List<ActorStateReadOperation>
46+
{
47+
new GetValueOperation("name"),
48+
new GetValueOperation("age"),
49+
new GetValueOperation("city"),
50+
new GetValueOperation("nonexistent"), // This won't exist
51+
new ListKeysOperation(continuationToken: null) // List all keys
52+
};
53+
54+
var readResult = await storage.ReadStateAsync(actorId, readOperations).ConfigureAwait(false);
55+
Console.WriteLine($" Current ETag: {readResult.ETag}");
56+
Console.WriteLine(" Results:");
57+
58+
foreach (var result in readResult.Results)
59+
{
60+
switch (result)
61+
{
62+
case GetValueResult getValue:
63+
Console.WriteLine($" - Get value: {getValue.Value?.ToString() ?? "null"}");
64+
break;
65+
66+
case ListKeysResult listKeys:
67+
Console.WriteLine($" - Keys: [{string.Join(", ", listKeys.Keys)}]");
68+
break;
69+
}
70+
}
71+
Console.WriteLine();
72+
73+
// 3. Update some values
74+
Console.WriteLine("3. Updating state...");
75+
var updateOperations = new List<ActorStateWriteOperation>
76+
{
77+
new SetValueOperation("age", JsonSerializer.SerializeToElement(31)), // Update age
78+
new SetValueOperation("email", JsonSerializer.SerializeToElement("john@example.com")), // Add email
79+
new RemoveKeyOperation("city") // Remove city
80+
};
81+
82+
var updateResult = await storage.WriteStateAsync(actorId, updateOperations, writeResult.ETag).ConfigureAwait(false);
83+
Console.WriteLine($" Update successful: {updateResult.Success}");
84+
Console.WriteLine($" New ETag: {updateResult.ETag}");
85+
Console.WriteLine($" Key count for actor: {storage.GetKeyCount(actorId)}");
86+
Console.WriteLine();
87+
88+
// 4. Try to update with wrong ETag (should fail)
89+
Console.WriteLine("4. Trying to update with wrong ETag...");
90+
var failingOperations = new List<ActorStateWriteOperation>
91+
{
92+
new SetValueOperation("shouldFail", JsonSerializer.SerializeToElement("this should fail"))
93+
};
94+
95+
var failResult = await storage.WriteStateAsync(actorId, failingOperations, "wrong-etag").ConfigureAwait(false);
96+
Console.WriteLine($" Update successful: {failResult.Success}");
97+
Console.WriteLine($" Current ETag: {failResult.ETag}");
98+
Console.WriteLine();
99+
100+
// 5. Read final state
101+
Console.WriteLine("5. Reading final state...");
102+
var finalReadOperations = new List<ActorStateReadOperation>
103+
{
104+
new ListKeysOperation(continuationToken: null)
105+
};
106+
107+
var finalReadResult = await storage.ReadStateAsync(actorId, finalReadOperations).ConfigureAwait(false);
108+
var finalKeys = finalReadResult.Results.OfType<ListKeysResult>().First();
109+
Console.WriteLine($" Final keys: [{string.Join(", ", finalKeys.Keys)}]");
110+
111+
// 6. Read each value
112+
foreach (var key in finalKeys.Keys)
113+
{
114+
var valueReadOperations = new List<ActorStateReadOperation>
115+
{
116+
new GetValueOperation(key)
117+
};
118+
119+
var valueReadResult = await storage.ReadStateAsync(actorId, valueReadOperations).ConfigureAwait(false);
120+
var getValue = valueReadResult.Results.OfType<GetValueResult>().First();
121+
Console.WriteLine($" - {key}: {getValue.Value}");
122+
}
123+
124+
Console.WriteLine();
125+
Console.WriteLine("=== Example Complete ===");
126+
}
127+
}

0 commit comments

Comments
 (0)