Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[.NET] Add happy path test for in-memory agent && Simplify HelloAgent example && some clean-up in extension APIs #4227

Merged
merged 12 commits into from
Nov 18, 2024
Merged
3 changes: 1 addition & 2 deletions dotnet/samples/Hello/HelloAgent/HelloAgent.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
Expand All @@ -8,7 +8,6 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Aspire.Hosting.AppHost" />
</ItemGroup>

<ItemGroup>
Expand Down
97 changes: 50 additions & 47 deletions dotnet/samples/Hello/HelloAgent/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,72 @@
using Microsoft.AutoGen.Agents;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Host = Microsoft.Extensions.Hosting.Host;

// step 1: create in-memory agent runtime
var builder = Host.CreateApplicationBuilder();

// step 1: create in-memory agent runtime
// step 2: register HelloAgent to that agent runtime
builder
.AddAgentService(local: true, useGrpc: false)
.AddAgentWorker(local: true)
.AddAgent<HelloAgent>("HelloAgent");

// step 3: start the agent runtime

// step 4: send a message to the agent
// step 3: wait for the agent runtime to shutdown
var app = builder.Build();
await app.StartAsync();

// step 5: wait for the agent runtime to shutdown
var app = await AgentsApp.PublishMessageAsync("HelloAgents", new NewMessageReceived
var client = app.Services.GetRequiredService<Client>();
await client.PublishEventAsync("HelloAgents", new NewMessageReceived
{
Message = "World"
}, local: true);
}, new CancellationToken());

await app.WaitForShutdownAsync();

namespace Hello
[TopicSubscription("HelloAgents")]
public class HelloAgent(
IAgentRuntime context, IHostApplicationLifetime hostApplicationLifetime,
[FromKeyedServices("EventTypes")] EventTypes typeRegistry) : AgentBase(
context,
typeRegistry),
ISayHello,
IHandleConsole,
IHandle<NewMessageReceived>,
IHandle<ConversationClosed>
{
[TopicSubscription("HelloAgents")]
public class HelloAgent(
IAgentRuntime context, IHostApplicationLifetime hostApplicationLifetime,
[FromKeyedServices("EventTypes")] EventTypes typeRegistry) : AgentBase(
context,
typeRegistry),
ISayHello,
IHandleConsole,
IHandle<NewMessageReceived>,
IHandle<ConversationClosed>
public async Task Handle(NewMessageReceived item)
{
public async Task Handle(NewMessageReceived item)
var response = await SayHello(item.Message);
var evt = new Output { Message = response };
await PublishMessageAsync(evt);
var goodbye = new ConversationClosed
{
var response = await SayHello(item.Message).ConfigureAwait(false);
var evt = new Output { Message = response };
await PublishMessageAsync(evt).ConfigureAwait(false);
var goodbye = new ConversationClosed
{
UserId = this.AgentId.Key,
UserMessage = "Goodbye"
};
await PublishMessageAsync(goodbye).ConfigureAwait(false);
}
public async Task Handle(ConversationClosed item)
UserId = this.AgentId.Key,
UserMessage = "Goodbye"
};
await PublishMessageAsync(goodbye);
}
public async Task Handle(ConversationClosed item)
{
var goodbye = $"********************* {item.UserId} said {item.UserMessage} ************************";
var evt = new Output
{
var goodbye = $"********************* {item.UserId} said {item.UserMessage} ************************";
var evt = new Output
{
Message = goodbye
};
await PublishMessageAsync(evt).ConfigureAwait(false);
Message = goodbye
};
await PublishMessageAsync(evt);

// Signal shutdown.
hostApplicationLifetime.StopApplication();
}

public async Task<string> SayHello(string ask)
{
var response = $"\n\n\n\n***************Hello {ask}**********************\n\n\n\n";
return response;
}
// Signal shutdown.
hostApplicationLifetime.StopApplication();
}
public interface ISayHello

public async Task<string> SayHello(string ask)
{
public Task<string> SayHello(string ask);
var response = $"\n\n\n\n***************Hello {ask}**********************\n\n\n\n";
return response;
}
}
public interface ISayHello
{
public Task<string> SayHello(string ask);
}
2 changes: 1 addition & 1 deletion dotnet/samples/dev-team/DevTeam.AgentHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.AutoGen.Agents;
var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddAutoGenServices();
builder.AddAgentService();

var app = builder.Build();
Expand Down
2 changes: 1 addition & 1 deletion dotnet/samples/dev-team/DevTeam.Agents/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddAutoGenServices();

builder.ConfigureSemanticKernel();

Expand Down
2 changes: 1 addition & 1 deletion dotnet/samples/dev-team/DevTeam.Backend/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddAutoGenServices();
builder.ConfigureSemanticKernel();

builder.Services.AddHttpClient();
Expand Down
5 changes: 3 additions & 2 deletions dotnet/src/Microsoft.AutoGen/Agents/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public static class AgentsApp
{
// need a variable to store the runtime instance
public static WebApplication? Host { get; private set; }

[MemberNotNull(nameof(Host))]
public static async ValueTask<WebApplication> StartAsync(WebApplicationBuilder? builder = null, AgentTypes? agentTypes = null, bool local = false)
{
Expand All @@ -23,7 +24,7 @@ public static async ValueTask<WebApplication> StartAsync(WebApplicationBuilder?
}
builder.AddAgentWorker(local: local)
.AddAgents(agentTypes);
builder.AddServiceDefaults();
builder.AddAutoGenServices();
var app = builder.Build();
if (local)
{
Expand Down Expand Up @@ -58,7 +59,7 @@ public static async ValueTask ShutdownAsync()
await Host.StopAsync();
}

private static AgentApplicationBuilder AddAgents(this AgentApplicationBuilder builder, AgentTypes? agentTypes)
private static IHostApplicationBuilder AddAgents(this IHostApplicationBuilder builder, AgentTypes? agentTypes)
{
agentTypes ??= AgentTypes.GetAgentTypesFromAssembly()
?? throw new InvalidOperationException("No agent types found in the assembly");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
Expand All @@ -13,25 +11,9 @@ namespace Microsoft.AutoGen.Agents;

public static class AgentWorkerHostingExtensions
{
public static WebApplicationBuilder AddAgentService(this WebApplicationBuilder builder, bool local = false, bool useGrpc = true)
public static IHostApplicationBuilder AddAgentService(this IHostApplicationBuilder builder, bool local = false, bool useGrpc = true)
{
if (local)
{
//TODO: make configuration more flexible
builder.WebHost.ConfigureKestrel(serverOptions =>
rysweet marked this conversation as resolved.
Show resolved Hide resolved
{
serverOptions.ListenLocalhost(5001, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
listenOptions.UseHttps();
});
});
builder.AddOrleans(local);
}
else
{
builder.AddOrleans();
}
builder.AddOrleans(local);

builder.Services.TryAddSingleton(DistributedContextPropagator.Current);

Expand All @@ -45,10 +27,11 @@ public static WebApplicationBuilder AddAgentService(this WebApplicationBuilder b
return builder;
}

public static WebApplicationBuilder AddLocalAgentService(this WebApplicationBuilder builder, bool useGrpc = true)
public static IHostApplicationBuilder AddLocalAgentService(this IHostApplicationBuilder builder, bool useGrpc = true)
{
return builder.AddAgentService(local: true, useGrpc);
}

public static WebApplication MapAgentService(this WebApplication app, bool local = false, bool useGrpc = true)
{
if (useGrpc) { app.MapGrpcService<GrpcGatewayService>(); }
Expand Down
2 changes: 1 addition & 1 deletion dotnet/src/Microsoft.AutoGen/Agents/Services/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static class Host
public static async Task<WebApplication> StartAsync(bool local = false, bool useGrpc = true)
{
var builder = WebApplication.CreateBuilder();
builder.AddServiceDefaults();
builder.AddAutoGenServices();
if (local)
{
builder.AddLocalAgentService(useGrpc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,23 @@ namespace Microsoft.AutoGen.Agents;
public static class HostBuilderExtensions
{
private const string _defaultAgentServiceAddress = "https://localhost:5001";
public static AgentApplicationBuilder AddAgentWorker(this IHostApplicationBuilder builder, string agentServiceAddress = _defaultAgentServiceAddress, bool local = false)

public static IHostApplicationBuilder AddAgent<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TAgent>(this IHostApplicationBuilder builder, string typeName) where TAgent : AgentBase
{
builder.Services.AddKeyedSingleton("AgentTypes", (sp, key) => Tuple.Create(typeName, typeof(TAgent)));

return builder;
}

public static IHostApplicationBuilder AddAgent(this IHostApplicationBuilder builder, string typeName, Type agentType)
{
builder.Services.AddKeyedSingleton("AgentTypes", (sp, key) => Tuple.Create(typeName, agentType));

return builder;
}

public static IHostApplicationBuilder AddAgentWorker(this IHostApplicationBuilder builder, string agentServiceAddress = _defaultAgentServiceAddress, bool local = false)
{
builder.Services.TryAddSingleton(DistributedContextPropagator.Current);

Expand Down Expand Up @@ -98,7 +114,9 @@ public static AgentApplicationBuilder AddAgentWorker(this IHostApplicationBuilde
return new EventTypes(typeRegistry, types, eventsMap);
});
builder.Services.AddSingleton<Client>();
return new AgentApplicationBuilder(builder);
builder.Services.AddSingleton(new AgentApplicationBuilder(builder));

return builder;
}

private static MessageDescriptor? GetMessageDescriptor(Type type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ public static class OrleansRuntimeHostingExtenions
{
public static WebApplicationBuilder AddOrleans(this WebApplicationBuilder builder, bool local = false)
{
return builder.AddOrleans(local);
}

public static IHostApplicationBuilder AddOrleans(this IHostApplicationBuilder builder, bool local = false)
{
builder.Services.AddSerializer(serializer => serializer.AddProtobufSerializer());
builder.Services.AddSingleton<IRegistryGrain, RegistryGrain>();

// Ensure Orleans is added before the hosted service to guarantee that it starts first.
//TODO: make all of this configurable
builder.Host.UseOrleans(siloBuilder =>
builder.UseOrleans((siloBuilder) =>
{
// Development mode or local mode uses in-memory storage and streams
if (builder.Environment.IsDevelopment() || local)
Expand Down Expand Up @@ -51,16 +57,16 @@ public static WebApplicationBuilder AddOrleans(this WebApplicationBuilder builde
options.SystemResponseTimeout = TimeSpan.FromMinutes(3);
});
siloBuilder.Configure<ClientMessagingOptions>(options =>
{
options.ResponseTimeout = TimeSpan.FromMinutes(3);
});
{
options.ResponseTimeout = TimeSpan.FromMinutes(3);
});
siloBuilder.UseCosmosClustering(o =>
{
o.ConfigureCosmosClient(cosmosDbconnectionString);
o.ContainerName = "AutoGen";
o.DatabaseName = "clustering";
o.IsResourceCreationEnabled = true;
});
{
o.ConfigureCosmosClient(cosmosDbconnectionString);
o.ContainerName = "AutoGen";
o.DatabaseName = "clustering";
o.IsResourceCreationEnabled = true;
});

siloBuilder.UseCosmosReminderService(o =>
{
Expand All @@ -84,7 +90,7 @@ public static WebApplicationBuilder AddOrleans(this WebApplicationBuilder builde
.AddMemoryGrainStorage("PubSubStore");
}
});
builder.Services.AddSingleton<IRegistryGrain, RegistryGrain>();

return builder;
}
}
rysweet marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ namespace Microsoft.Extensions.Hosting;
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
public static class Extensions
{
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
public static IHostApplicationBuilder AddAutoGenServices(this IHostApplicationBuilder builder)
{
builder.ConfigureOpenTelemetry();
builder.ConfigureOpenTelemetryForAutoGen();

builder.AddDefaultHealthChecks();

Expand All @@ -41,7 +41,7 @@ public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBu
// });
return builder;
}
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
public static IHostApplicationBuilder ConfigureOpenTelemetryForAutoGen(this IHostApplicationBuilder builder)
{
builder.Logging.AddOpenTelemetry(logging =>
{
Expand All @@ -66,11 +66,11 @@ public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicati
.AddSource("AutoGen.Agent");
});

builder.AddOpenTelemetryExporters();
builder.AddOpenTelemetryExportersForAutoGen();

return builder;
}
private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
private static IHostApplicationBuilder AddOpenTelemetryExportersForAutoGen(this IHostApplicationBuilder builder)
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

Expand All @@ -94,6 +94,7 @@ public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicati
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
return builder;
}

public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
// Adding health checks endpoints to applications in non-development environments has security implications.
Expand Down
Loading
Loading