diff --git a/dotnet/src/Microsoft.AutoGen/Core.Grpc/GrpcAgentWorkerHostBuilderExtension.cs b/dotnet/src/Microsoft.AutoGen/Core.Grpc/GrpcAgentWorkerHostBuilderExtension.cs new file mode 100644 index 00000000000..7f43b9620f5 --- /dev/null +++ b/dotnet/src/Microsoft.AutoGen/Core.Grpc/GrpcAgentWorkerHostBuilderExtension.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// GrpcAgentWorkerHostBuilderExtension.cs +using System.Diagnostics; +using Grpc.Core; +using Grpc.Net.Client.Configuration; +using Microsoft.AutoGen.Contracts; +using Microsoft.AutoGen.Protobuf; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +namespace Microsoft.AutoGen.Core.Grpc; + +public static class GrpcAgentWorkerHostBuilderExtensions +{ + private const string _defaultAgentServiceAddress = "https://localhost:53071"; + + // TODO: How do we ensure AddGrpcAgentWorker and UseInProcessRuntime are mutually exclusive? + public static AgentsAppBuilder AddGrpcAgentWorker(this AgentsAppBuilder builder, string? agentServiceAddress = null) + { + builder.Services.AddGrpcClient(options => + { + options.Address = new Uri(agentServiceAddress ?? builder.Configuration["AGENT_HOST"] ?? _defaultAgentServiceAddress); + options.ChannelOptionsActions.Add(channelOptions => + { + var loggerFactory = new LoggerFactory(); + if (Debugger.IsAttached) + { + channelOptions.HttpHandler = new SocketsHttpHandler + { + EnableMultipleHttp2Connections = false, + KeepAlivePingDelay = TimeSpan.FromSeconds(200), + KeepAlivePingTimeout = TimeSpan.FromSeconds(100), + KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always + }; + } + else + { + channelOptions.HttpHandler = new SocketsHttpHandler + { + EnableMultipleHttp2Connections = true, + KeepAlivePingDelay = TimeSpan.FromSeconds(20), + KeepAlivePingTimeout = TimeSpan.FromSeconds(10), + KeepAlivePingPolicy = HttpKeepAlivePingPolicy.WithActiveRequests + }; + } + + var methodConfig = new MethodConfig + { + Names = { MethodName.Default }, + RetryPolicy = new RetryPolicy + { + MaxAttempts = 5, + InitialBackoff = TimeSpan.FromSeconds(1), + MaxBackoff = TimeSpan.FromSeconds(5), + BackoffMultiplier = 1.5, + RetryableStatusCodes = { StatusCode.Unavailable } + } + }; + + channelOptions.ServiceConfig = new() { MethodConfigs = { methodConfig } }; + channelOptions.ThrowOperationCanceledOnCancellation = true; + }); + }); + builder.Services.TryAddSingleton(DistributedContextPropagator.Current); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(sp => (IHostedService)sp.GetRequiredService()); + return builder; + } +} diff --git a/dotnet/src/Microsoft.AutoGen/Core.Grpc/IAgentRuntimeExtensions.cs b/dotnet/src/Microsoft.AutoGen/Core.Grpc/IAgentRuntimeExtensions.cs index c820baa527c..8179ff4b494 100644 --- a/dotnet/src/Microsoft.AutoGen/Core.Grpc/IAgentRuntimeExtensions.cs +++ b/dotnet/src/Microsoft.AutoGen/Core.Grpc/IAgentRuntimeExtensions.cs @@ -4,16 +4,17 @@ using System.Diagnostics; using Google.Protobuf.Collections; using Microsoft.AutoGen.Contracts; +using Microsoft.AutoGen.Protobuf; using Microsoft.Extensions.DependencyInjection; using static Microsoft.AutoGen.Contracts.CloudEvent.Types; namespace Microsoft.AutoGen.Core.Grpc; -public static class IAgentRuntimeExtensions +public static class GrpcAgentRuntimeExtensions { - public static (string?, string?) GetTraceIdAndState(IAgentRuntime runtime, IDictionary metadata) + public static (string?, string?) GetTraceIdAndState(GrpcAgentRuntime runtime, IDictionary metadata) { - var dcp = runtime.RuntimeServiceProvider.GetRequiredService(); + var dcp = runtime.ServiceProvider.GetRequiredService(); dcp.ExtractTraceIdAndState(metadata, static (object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { @@ -25,9 +26,9 @@ public static (string?, string?) GetTraceIdAndState(IAgentRuntime runtime, IDict out var traceState); return (traceParent, traceState); } - public static (string?, string?) GetTraceIdAndState(IAgentRuntime worker, MapField metadata) + public static (string?, string?) GetTraceIdAndState(GrpcAgentRuntime worker, MapField metadata) { - var dcp = worker.RuntimeServiceProvider.GetRequiredService(); + var dcp = worker.ServiceProvider.GetRequiredService(); dcp.ExtractTraceIdAndState(metadata, static (object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { @@ -40,9 +41,9 @@ public static (string?, string?) GetTraceIdAndState(IAgentRuntime worker, MapFie out var traceState); return (traceParent, traceState); } - public static void Update(IAgentRuntime worker, RpcRequest request, Activity? activity = null) + public static void Update(GrpcAgentRuntime worker, RpcRequest request, Activity? activity = null) { - var dcp = worker.RuntimeServiceProvider.GetRequiredService(); + var dcp = worker.ServiceProvider.GetRequiredService(); dcp.Inject(activity, request.Metadata, static (carrier, key, value) => { var metadata = (IDictionary)carrier!; @@ -56,9 +57,9 @@ public static void Update(IAgentRuntime worker, RpcRequest request, Activity? ac } }); } - public static void Update(IAgentRuntime worker, CloudEvent cloudEvent, Activity? activity = null) + public static void Update(GrpcAgentRuntime worker, CloudEvent cloudEvent, Activity? activity = null) { - var dcp = worker.RuntimeServiceProvider.GetRequiredService(); + var dcp = worker.ServiceProvider.GetRequiredService(); dcp.Inject(activity, cloudEvent.Attributes, static (carrier, key, value) => { var mapField = (MapField)carrier!; @@ -73,9 +74,9 @@ public static void Update(IAgentRuntime worker, CloudEvent cloudEvent, Activity? }); } - public static IDictionary ExtractMetadata(IAgentRuntime worker, IDictionary metadata) + public static IDictionary ExtractMetadata(GrpcAgentRuntime worker, IDictionary metadata) { - var dcp = worker.RuntimeServiceProvider.GetRequiredService(); + var dcp = worker.ServiceProvider.GetRequiredService(); var baggage = dcp.ExtractBaggage(metadata, static (object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { var metadata = (IDictionary)carrier!; @@ -85,9 +86,9 @@ public static IDictionary ExtractMetadata(IAgentRuntime worker, return baggage as IDictionary ?? new Dictionary(); } - public static IDictionary ExtractMetadata(IAgentRuntime worker, MapField metadata) + public static IDictionary ExtractMetadata(GrpcAgentRuntime worker, MapField metadata) { - var dcp = worker.RuntimeServiceProvider.GetRequiredService(); + var dcp = worker.ServiceProvider.GetRequiredService(); var baggage = dcp.ExtractBaggage(metadata, static (object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { var metadata = (MapField)carrier!; diff --git a/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoSerializationRegistry.cs b/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoSerializationRegistry.cs new file mode 100644 index 00000000000..e744bcb0eee --- /dev/null +++ b/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoSerializationRegistry.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ProtoSerializationRegistry.cs + +namespace Microsoft.AutoGen.Core.Grpc; + +public class ProtoSerializationRegistry : IProtoSerializationRegistry +{ + private readonly Dictionary _serializers + = new Dictionary(); + + public ITypeNameResolver TypeNameResolver => new ProtoTypeNameResolver(); + + public bool Exists(Type type) + { + return _serializers.ContainsKey(TypeNameResolver.ResolveTypeName(type)); + } + + public IProtoMessageSerializer? GetSerializer(Type type) + { + return GetSerializer(TypeNameResolver.ResolveTypeName(type)); + } + + public IProtoMessageSerializer? GetSerializer(string typeName) + { + _serializers.TryGetValue(typeName, out var serializer); + return serializer; + } + + public void RegisterSerializer(Type type, IProtoMessageSerializer serializer) + { + if (_serializers.ContainsKey(TypeNameResolver.ResolveTypeName(type))) + { + throw new InvalidOperationException($"Serializer already registered for {type.FullName}"); + } + _serializers[TypeNameResolver.ResolveTypeName(type)] = serializer; + } +} diff --git a/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoTypeNameResolver.cs b/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoTypeNameResolver.cs index 808116139ba..a769b0f31c8 100644 --- a/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoTypeNameResolver.cs +++ b/dotnet/src/Microsoft.AutoGen/Core.Grpc/ProtoTypeNameResolver.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// ITypeNameResolver.cs +// ProtoTypeNameResolver.cs using Google.Protobuf; diff --git a/dotnet/src/Microsoft.AutoGen/Core.Grpc/SerializationRegistry.cs b/dotnet/src/Microsoft.AutoGen/Core.Grpc/SerializationRegistry.cs deleted file mode 100644 index d7bf3a37325..00000000000 --- a/dotnet/src/Microsoft.AutoGen/Core.Grpc/SerializationRegistry.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// SerializationRegistry.cs - -namespace Microsoft.AutoGen.Core.Grpc; - -public class ProtoSerializationRegistry : IProtoSerializationRegistry -{ - private readonly Dictionary _serializers - = new Dictionary(); - - public bool Exists(Type type) - { - return _serializers.ContainsKey(type); - } - - public IProtoMessageSerializer? GetSerializer(Type type) - { - _serializers.TryGetValue(type, out var serializer); - return serializer; - } - - public void RegisterSerializer(Type type, IProtoMessageSerializer serializer) - { - if (_serializers.ContainsKey(type)) - { - throw new InvalidOperationException($"Serializer already registered for {type.FullName}"); - } - _serializers[type] = serializer; - } -} diff --git a/dotnet/src/Microsoft.AutoGen/Core/AgentsApp.cs b/dotnet/src/Microsoft.AutoGen/Core/AgentsApp.cs index bae09a9f191..cecd8d9ec48 100644 --- a/dotnet/src/Microsoft.AutoGen/Core/AgentsApp.cs +++ b/dotnet/src/Microsoft.AutoGen/Core/AgentsApp.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Reflection; using Microsoft.AutoGen.Contracts; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -21,6 +22,7 @@ public AgentsAppBuilder(HostApplicationBuilder? baseBuilder = null) } public IServiceCollection Services => this.builder.Services; + public IConfiguration Configuration => this.builder.Configuration; public void AddAgentsFromAssemblies() {