Skip to content

Commit bc5fb33

Browse files
authored
Merge branch 'main' into dotnet_unit_rt
2 parents 5b198d2 + 08f9830 commit bc5fb33

35 files changed

+1861
-269
lines changed

.github/workflows/dotnet-build.yml

+34
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,38 @@ jobs:
107107
- name: Unit Test V2
108108
run: dotnet test --no-build -bl --configuration Release --filter "Category=UnitV2"
109109

110+
grpc-unit-tests:
111+
name: Dotnet Grpc unit tests
112+
needs: paths-filter
113+
if: needs.paths-filter.outputs.hasChanges == 'true'
114+
defaults:
115+
run:
116+
working-directory: dotnet
117+
strategy:
118+
fail-fast: false
119+
matrix:
120+
os: [ ubuntu-latest ]
121+
runs-on: ${{ matrix.os }}
122+
timeout-minutes: 30
123+
steps:
124+
- uses: actions/checkout@v4
125+
with:
126+
lfs: true
127+
- name: Setup .NET 8.0
128+
uses: actions/setup-dotnet@v4
129+
with:
130+
dotnet-version: '8.0.x'
131+
- name: Install dev certs
132+
run: dotnet --version && dotnet dev-certs https --trust
133+
- name: Restore dependencies
134+
run: |
135+
# dotnet nuget add source --name dotnet-tool https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json --configfile NuGet.config
136+
dotnet restore -bl
137+
- name: Build
138+
run: dotnet build --no-restore --configuration Release -bl /p:SignAssembly=true
139+
- name: GRPC tests
140+
run: dotnet test --no-build -bl --configuration Release --filter "Category=GRPC"
141+
110142
integration-test:
111143
strategy:
112144
fail-fast: true
@@ -224,6 +256,8 @@ jobs:
224256
with:
225257
dotnet-version: '8.0.x'
226258
global-json-file: dotnet/global.json
259+
- name: Install dev certs
260+
run: dotnet --version && dotnet dev-certs https --trust
227261
- name: Restore dependencies
228262
run: |
229263
dotnet restore -bl

.github/workflows/dotnet-release.yml

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ jobs:
5252
run: |
5353
echo "Build AutoGen"
5454
dotnet build --no-restore --configuration Release -bl /p:SignAssembly=true
55+
- run: sudo dotnet dev-certs https --trust --no-password
5556
- name: Unit Test
5657
run: dotnet test --no-build -bl --configuration Release
5758
env:

dotnet/AutoGen.sln

+14
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hello", "Hello", "{F42F9C8E
118118
EndProject
119119
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AutoGen.Core.Grpc", "src\Microsoft.AutoGen\Core.Grpc\Microsoft.AutoGen.Core.Grpc.csproj", "{3D83C6DB-ACEA-48F3-959F-145CCD2EE135}"
120120
EndProject
121+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStartedGrpc", "samples\GettingStartedGrpc\GettingStartedGrpc.csproj", "{C3740DF1-18B1-4607-81E4-302F0308C848}"
122+
EndProject
123+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AutoGen.Core.Grpc.Tests", "test\Microsoft.AutoGen.Core.Grpc.Tests\Microsoft.AutoGen.Core.Grpc.Tests.csproj", "{23A028D3-5EB1-4FA0-9CD1-A1340B830579}"
124+
EndProject
121125
Global
122126
GlobalSection(SolutionConfigurationPlatforms) = preSolution
123127
Debug|Any CPU = Debug|Any CPU
@@ -306,6 +310,14 @@ Global
306310
{AAD593FE-A49B-425E-A9FE-A0022CD25E3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
307311
{AAD593FE-A49B-425E-A9FE-A0022CD25E3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
308312
{AAD593FE-A49B-425E-A9FE-A0022CD25E3D}.Release|Any CPU.Build.0 = Release|Any CPU
313+
{C3740DF1-18B1-4607-81E4-302F0308C848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
314+
{C3740DF1-18B1-4607-81E4-302F0308C848}.Debug|Any CPU.Build.0 = Debug|Any CPU
315+
{C3740DF1-18B1-4607-81E4-302F0308C848}.Release|Any CPU.ActiveCfg = Release|Any CPU
316+
{C3740DF1-18B1-4607-81E4-302F0308C848}.Release|Any CPU.Build.0 = Release|Any CPU
317+
{23A028D3-5EB1-4FA0-9CD1-A1340B830579}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
318+
{23A028D3-5EB1-4FA0-9CD1-A1340B830579}.Debug|Any CPU.Build.0 = Debug|Any CPU
319+
{23A028D3-5EB1-4FA0-9CD1-A1340B830579}.Release|Any CPU.ActiveCfg = Release|Any CPU
320+
{23A028D3-5EB1-4FA0-9CD1-A1340B830579}.Release|Any CPU.Build.0 = Release|Any CPU
309321
EndGlobalSection
310322
GlobalSection(SolutionProperties) = preSolution
311323
HideSolutionNode = FALSE
@@ -359,6 +371,8 @@ Global
359371
{3D83C6DB-ACEA-48F3-959F-145CCD2EE135} = {18BF8DD7-0585-48BF-8F97-AD333080CE06}
360372
{AAD593FE-A49B-425E-A9FE-A0022CD25E3D} = {F42F9C8E-7BD9-4687-9B63-AFFA461AF5C1}
361373
{F42F9C8E-7BD9-4687-9B63-AFFA461AF5C1} = {CE0AA8D5-12B8-4628-9589-DAD8CB0DDCF6}
374+
{C3740DF1-18B1-4607-81E4-302F0308C848} = {CE0AA8D5-12B8-4628-9589-DAD8CB0DDCF6}
375+
{23A028D3-5EB1-4FA0-9CD1-A1340B830579} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64}
362376
EndGlobalSection
363377
GlobalSection(ExtensibilityGlobals) = postSolution
364378
SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Checker.cs
3+
4+
using Microsoft.AutoGen.Contracts;
5+
using Microsoft.AutoGen.Core;
6+
using Microsoft.Extensions.Hosting;
7+
using TerminationF = System.Func<int, bool>;
8+
9+
namespace GettingStartedGrpcSample;
10+
11+
[TypeSubscription("default")]
12+
public class Checker(
13+
AgentId id,
14+
IAgentRuntime runtime,
15+
IHostApplicationLifetime hostApplicationLifetime,
16+
TerminationF runUntilFunc
17+
) :
18+
BaseAgent(id, runtime, "Modifier", null),
19+
IHandle<Events.CountUpdate>
20+
{
21+
public async ValueTask HandleAsync(Events.CountUpdate item, MessageContext messageContext)
22+
{
23+
if (!runUntilFunc(item.NewCount))
24+
{
25+
Console.WriteLine($"\nChecker:\n{item.NewCount} passed the check, continue.");
26+
await this.PublishMessageAsync(new Events.CountMessage { Content = item.NewCount }, new TopicId("default"));
27+
}
28+
else
29+
{
30+
Console.WriteLine($"\nChecker:\n{item.NewCount} failed the check, stopping.");
31+
hostApplicationLifetime.StopApplication();
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<RootNamespace>getting_started</RootNamespace>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\src\Microsoft.AutoGen\Contracts\Microsoft.AutoGen.Contracts.csproj" />
13+
<ProjectReference Include="..\..\src\Microsoft.AutoGen\Core\Microsoft.AutoGen.Core.csproj" />
14+
<ProjectReference Include="..\..\src\Microsoft.AutoGen\Core.Grpc\Microsoft.AutoGen.Core.Grpc.csproj" />
15+
</ItemGroup>
16+
17+
18+
<ItemGroup>
19+
<Protobuf Include="message.proto" GrpcServices="Client" Link="Protos\message.proto" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
24+
</ItemGroup>
25+
26+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Modifier.cs
3+
4+
using Microsoft.AutoGen.Contracts;
5+
using Microsoft.AutoGen.Core;
6+
7+
using ModifyF = System.Func<int, int>;
8+
9+
namespace GettingStartedGrpcSample;
10+
11+
[TypeSubscription("default")]
12+
public class Modifier(
13+
AgentId id,
14+
IAgentRuntime runtime,
15+
ModifyF modifyFunc
16+
) :
17+
BaseAgent(id, runtime, "Modifier", null),
18+
IHandle<Events.CountMessage>
19+
{
20+
21+
public async ValueTask HandleAsync(Events.CountMessage item, MessageContext messageContext)
22+
{
23+
int newValue = modifyFunc(item.Content);
24+
Console.WriteLine($"\nModifier:\nModified {item.Content} to {newValue}");
25+
26+
var updateMessage = new Events.CountUpdate { NewCount = newValue };
27+
await this.PublishMessageAsync(updateMessage, topic: new TopicId("default"));
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Program.cs
3+
using GettingStartedGrpcSample;
4+
using Microsoft.AutoGen.Contracts;
5+
using Microsoft.AutoGen.Core;
6+
using Microsoft.AutoGen.Core.Grpc;
7+
using Microsoft.Extensions.DependencyInjection.Extensions;
8+
using ModifyF = System.Func<int, int>;
9+
using TerminationF = System.Func<int, bool>;
10+
11+
ModifyF modifyFunc = (int x) => x - 1;
12+
TerminationF runUntilFunc = (int x) =>
13+
{
14+
return x <= 1;
15+
};
16+
17+
AgentsAppBuilder appBuilder = new AgentsAppBuilder();
18+
appBuilder.AddGrpcAgentWorker("http://localhost:50051");
19+
20+
appBuilder.Services.TryAddSingleton(modifyFunc);
21+
appBuilder.Services.TryAddSingleton(runUntilFunc);
22+
23+
appBuilder.AddAgent<Checker>("Checker");
24+
appBuilder.AddAgent<Modifier>("Modifier");
25+
26+
var app = await appBuilder.BuildAsync();
27+
await app.StartAsync();
28+
29+
// Send the initial count to the agents app, running on the `local` runtime, and pass through the registered services via the application `builder`
30+
await app.PublishMessageAsync(new GettingStartedGrpcSample.Events.CountMessage
31+
{
32+
Content = 10
33+
}, new TopicId("default"));
34+
35+
// Run until application shutdown
36+
await app.WaitForShutdownAsync();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
syntax = "proto3";
2+
3+
option csharp_namespace = "GettingStartedGrpcSample.Events";
4+
5+
message CountMessage {
6+
int32 content = 1;
7+
}
8+
9+
message CountUpdate {
10+
int32 new_count = 1;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// AgentsAppBuilderExtensions.cs
3+
4+
using System.Diagnostics;
5+
using Grpc.Core;
6+
using Grpc.Net.Client.Configuration;
7+
using Microsoft.AutoGen.Contracts;
8+
using Microsoft.AutoGen.Protobuf;
9+
using Microsoft.Extensions.Configuration;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.DependencyInjection.Extensions;
12+
using Microsoft.Extensions.Logging;
13+
namespace Microsoft.AutoGen.Core.Grpc;
14+
15+
public static class AgentsAppBuilderExtensions
16+
{
17+
private const string _defaultAgentServiceAddress = "http://localhost:53071";
18+
19+
// TODO: How do we ensure AddGrpcAgentWorker and UseInProcessRuntime are mutually exclusive?
20+
public static AgentsAppBuilder AddGrpcAgentWorker(this AgentsAppBuilder builder, string? agentServiceAddress = null)
21+
{
22+
builder.Services.AddGrpcClient<AgentRpc.AgentRpcClient>(options =>
23+
{
24+
options.Address = new Uri(agentServiceAddress ?? builder.Configuration.GetValue("AGENT_HOST", _defaultAgentServiceAddress));
25+
options.ChannelOptionsActions.Add(channelOptions =>
26+
{
27+
var loggerFactory = new LoggerFactory();
28+
if (Debugger.IsAttached)
29+
{
30+
channelOptions.HttpHandler = new SocketsHttpHandler
31+
{
32+
EnableMultipleHttp2Connections = false,
33+
KeepAlivePingDelay = TimeSpan.FromSeconds(200),
34+
KeepAlivePingTimeout = TimeSpan.FromSeconds(100),
35+
KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always
36+
};
37+
}
38+
else
39+
{
40+
channelOptions.HttpHandler = new SocketsHttpHandler
41+
{
42+
EnableMultipleHttp2Connections = true,
43+
KeepAlivePingDelay = TimeSpan.FromSeconds(20),
44+
KeepAlivePingTimeout = TimeSpan.FromSeconds(10),
45+
KeepAlivePingPolicy = HttpKeepAlivePingPolicy.WithActiveRequests
46+
};
47+
}
48+
49+
var methodConfig = new MethodConfig
50+
{
51+
Names = { MethodName.Default },
52+
RetryPolicy = new RetryPolicy
53+
{
54+
MaxAttempts = 5,
55+
InitialBackoff = TimeSpan.FromSeconds(1),
56+
MaxBackoff = TimeSpan.FromSeconds(5),
57+
BackoffMultiplier = 1.5,
58+
RetryableStatusCodes = { StatusCode.Unavailable }
59+
}
60+
};
61+
62+
channelOptions.ServiceConfig = new() { MethodConfigs = { methodConfig } };
63+
channelOptions.ThrowOperationCanceledOnCancellation = true;
64+
});
65+
});
66+
67+
builder.Services.TryAddSingleton(DistributedContextPropagator.Current);
68+
builder.Services.AddSingleton<IAgentRuntime, GrpcAgentRuntime>();
69+
builder.Services.AddHostedService<GrpcAgentRuntime>(services =>
70+
{
71+
return (services.GetRequiredService<IAgentRuntime>() as GrpcAgentRuntime)!;
72+
});
73+
74+
return builder;
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// CloudEventExtensions.cs
3+
4+
using Microsoft.AutoGen.Contracts;
5+
6+
namespace Microsoft.AutoGen.Core.Grpc;
7+
8+
internal static class CloudEventExtensions
9+
{
10+
// Convert an ISubscrptionDefinition to a Protobuf Subscription
11+
internal static CloudEvent CreateCloudEvent(Google.Protobuf.WellKnownTypes.Any payload, TopicId topic, string dataType, AgentId? sender, string messageId)
12+
{
13+
var attributes = new Dictionary<string, CloudEvent.Types.CloudEventAttributeValue>
14+
{
15+
{
16+
Constants.DATA_CONTENT_TYPE_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = Constants.DATA_CONTENT_TYPE_PROTOBUF_VALUE }
17+
},
18+
{
19+
Constants.DATA_SCHEMA_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = dataType }
20+
},
21+
{
22+
Constants.MESSAGE_KIND_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = Constants.MESSAGE_KIND_VALUE_PUBLISH }
23+
}
24+
};
25+
26+
if (sender != null)
27+
{
28+
var senderNonNull = (AgentId)sender;
29+
attributes.Add(Constants.AGENT_SENDER_TYPE_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = senderNonNull.Type });
30+
attributes.Add(Constants.AGENT_SENDER_KEY_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = senderNonNull.Key });
31+
}
32+
33+
return new CloudEvent
34+
{
35+
ProtoData = payload,
36+
Type = topic.Type,
37+
Source = topic.Source,
38+
Id = messageId,
39+
Attributes = { attributes }
40+
};
41+
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Constants.cs
3+
4+
namespace Microsoft.AutoGen.Core.Grpc;
5+
6+
public static class Constants
7+
{
8+
public const string DATA_CONTENT_TYPE_PROTOBUF_VALUE = "application/x-protobuf";
9+
public const string DATA_CONTENT_TYPE_JSON_VALUE = "application/json";
10+
public const string DATA_CONTENT_TYPE_TEXT_VALUE = "text/plain";
11+
12+
public const string DATA_CONTENT_TYPE_ATTR = "datacontenttype";
13+
public const string DATA_SCHEMA_ATTR = "dataschema";
14+
public const string AGENT_SENDER_TYPE_ATTR = "agagentsendertype";
15+
public const string AGENT_SENDER_KEY_ATTR = "agagentsenderkey";
16+
17+
public const string MESSAGE_KIND_ATTR = "agmsgkind";
18+
public const string MESSAGE_KIND_VALUE_PUBLISH = "publish";
19+
public const string MESSAGE_KIND_VALUE_RPC_REQUEST = "rpc_request";
20+
public const string MESSAGE_KIND_VALUE_RPC_RESPONSE = "rpc_response";
21+
}

0 commit comments

Comments
 (0)