.net changes to re-enable xlang support, add subscription apis (#4159)

* add subscription response

* fix send subscription response

* add register agent type response

* adding a test

* working on shaping up a test

* appsettins update for backend

* another appsettings

* fixup aspire hosting

* enable AGENT_HOST var from aspire

* add SendMessageAsync

* remove broken test

* test compiles and runs but is not (yet) correct

* subscriptions grain wireup.

* temp assert true.

* remove DI for SubscriptionGrain

* add xlang python code

* add subscription response

* rebond

* Update to .NET 9.0

* Fix Backend project SDK

* Package updates

* get RegisterAgentTypeRequest working

* fix exceptions

* add error handling for requests

* whoops

* send cloud event message type

* processing cloudevents

* trying tosend proto data - doesn't work

* trying to pack proto_data

* fix (#4238)

* pack the Message from agents_events

* format - not sure why these?

* format

* cleanup, error handling, xlang sample publishes messages that can be heard by .NET and vice versa

* format

* sdk version

* sdk vers

* net8

* back to net8

* remove netstandard2

* fix used

* remove unused

* more cleanup

* remove unneeded package

* I'm terrible at writing tests

* deserialize the cloud events and sent them as events

* comment

* cleanup

* await

* Delete dotnet/samples/Hello/Backend/Backend.csproj

unneeded change

* whoops

* merge main python back into here

* revert back to local

* revert some of the helloAgents changes.

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

* add happy path test

* remove unnecessary namespace

* fix build error

* Update AgentBaseTests.cs

* revert changes

---------

* fix busted merge from main

* addressing review comments

* make internal

* case sensitive rename step 1

* case sensitive rename step 2

* remove!

---------

Co-authored-by: Peter Chang <petchang@microsoft.com>
Co-authored-by: Reuben Bond <reuben.bond@gmail.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
Co-authored-by: Xiaoyun Zhang <bigmiao.zhang@gmail.com>
This commit is contained in:
Ryan Sweet 2024-11-19 11:00:48 -08:00 committed by GitHub
parent 2b7658a9da
commit 1e3b765e3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 265 additions and 84 deletions

View File

@ -4,26 +4,25 @@
<MicrosoftSemanticKernelVersion>1.22.0</MicrosoftSemanticKernelVersion>
<MicrosoftSemanticKernelExperimentalVersion>1.22.0-alpha</MicrosoftSemanticKernelExperimentalVersion>
<MicrosoftExtensionsAIVersion>9.0.0-preview.9.24525.1</MicrosoftExtensionsAIVersion>
<NuGetAuditMode>direct</NuGetAuditMode>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Aspire.Hosting" Version="9.0.0-rc.1.24511.1" />
<PackageVersion Include="Aspire.Hosting" Version="9.0.0" />
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="8.0.1-preview.8.24267.1" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="8.2.1" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="8.2.1" />
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="8.2.1" />
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.NodeJs" Version="8.2.0" />
<PackageVersion Include="Aspire.Hosting.Orleans" Version="8.2.1" />
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="8.2.1" />
<PackageVersion Include="Aspire.Hosting.Orleans" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="9.0.0" />
<PackageVersion Include="Aspire.Hosting.Redis" Version="8.2.0" />
<PackageVersion Include="Azure.AI.OpenAI" Version=" 2.1.0-beta.1" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.1.0-beta.2" />
<PackageVersion Include="Azure.AI.Inference" Version="1.0.0-beta.1" />
<PackageVersion Include="Azure.Data.Tables" Version="12.9.1" />
<PackageVersion Include="Azure.Identity" Version="1.13.0" />
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" />
<PackageVersion Include="Azure.Storage.Files.Shares" Version="12.20.1" />
<PackageVersion Include="Azure.Storage.Files.Shares" Version="12.21.0" />
<PackageVersion Include="CloudNative.CloudEvents.SystemTextJson" Version="2.7.1" />
<PackageVersion Include="Elsa" Version="3.1.3" />
<PackageVersion Include="Elsa.EntityFrameworkCore" Version="3.1.3" />
@ -43,25 +42,25 @@
<PackageVersion Include="Microsoft.AspNetCore.App" Version="8.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageVersion Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0-rc.2.24473.5" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.AzureAIInference" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.Ollama" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.6" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.8.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.10.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="8.2.1" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="9.0.0" />
<PackageVersion Include="Microsoft.Orleans.Clustering.Cosmos" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.CodeGenerator" Version="8.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -78,17 +77,17 @@
<PackageVersion Include="Microsoft.Orleans.Server" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Streaming" Version="8.2.0" />
<PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="8.2.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="$(MicrosoftSemanticKernelVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.29.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Agents.Core" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="$(MicrosoftSemanticKernelVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="1.29.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Web" Version="$(MicrosoftSemanticKernelExperimentalVersion)" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Octokit" Version="13.0.1" />
<PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.4.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
@ -96,28 +95,29 @@
<PackageVersion Include="PdfPig" Version="0.1.9-alpha-20240324-e7896" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
<PackageVersion Include="System.Formats.Asn1" Version="9.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.7.1" />
<PackageVersion Include="System.IO.Packaging" Version="8.0.1" />
<PackageVersion Include="System.Memory.Data" Version="8.0.1" />
<PackageVersion Include="System.IO.Packaging" Version="9.0.0" />
<PackageVersion Include="System.Memory.Data" Version="9.0.0" />
<PackageVersion Include="JsonSchema.Net.Generation" Version="4.5.1" />
<PackageVersion Include="Microsoft.DotNet.Interactive" Version="1.0.0-beta.24229.4" />
<PackageVersion Include="Microsoft.DotNet.Interactive.Jupyter" Version="1.0.0-beta.24229.4" />
<PackageVersion Include="Microsoft.DotNet.Interactive.PackageManagement" Version="1.0.0-beta.24229.4" />
<PackageVersion Include="Google.Cloud.AIPlatform.V1" Version="3.9.0" />
<PackageVersion Include="Google.Cloud.AIPlatform.V1" Version="3.10.0" />
<PackageVersion Include="OpenAI" Version="2.1.0-beta.1" />
<PackageVersion Include="System.CodeDom" Version="5.0.0" />
<PackageVersion Include="System.CodeDom" Version="9.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="ApprovalTests" Version="6.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
<PackageVersion Include="FluentAssertions" Version="6.12.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.console" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.4.5" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="8.0.0" />
</ItemGroup>
</Project>
</Project>

View File

@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.104",
"version": "8.0.401",
"rollForward": "latestMinor"
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Agents\Microsoft.AutoGen.Agents.csproj" />
</ItemGroup>

View File

@ -1,7 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Program.cs
using Microsoft.Extensions.Hosting;
var app = await Microsoft.AutoGen.Agents.Host.StartAsync(local: false, useGrpc: true);
await app.WaitForShutdownAsync();

View File

@ -1,9 +1,15 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Orleans": "Warning"
}
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Orleans": "Warning"
}
}
},
"AllowedHosts": "*",
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
}
}
}

View File

@ -1,7 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>

View File

@ -1,7 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Program.cs
using Microsoft.Extensions.Hosting;
var builder = DistributedApplication.CreateBuilder(args);
var backend = builder.AddProject<Projects.Backend>("backend");
builder.AddProject<Projects.HelloAgent>("client").WithReference(backend).WaitFor(backend);
builder.Build().Run();
var backend = builder.AddProject<Projects.Backend>("backend").WithExternalHttpEndpoints();
builder.AddProject<Projects.HelloAgent>("client")
.WithReference(backend)
.WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("https").Property(EndpointProperty.Url)}")
.WaitFor(backend);
using var app = builder.Build();
await app.StartAsync();
var url = backend.GetEndpoint("http").Url;
Console.WriteLine("Backend URL: " + url);
await app.WaitForShutdownAsync();

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -19,7 +19,7 @@ var app = await AgentsApp.PublishMessageAsync("HelloAgents", new NewMessageRecei
{
Message = "World"
}, local: true);
//var app = await AgentsApp.StartAsync();
await app.WaitForShutdownAsync();
namespace Hello
@ -33,7 +33,8 @@ namespace Hello
ISayHello,
IHandleConsole,
IHandle<NewMessageReceived>,
IHandle<ConversationClosed>
IHandle<ConversationClosed>,
IHandle<Shutdown>
{
public async Task Handle(NewMessageReceived item)
{
@ -50,13 +51,14 @@ namespace Hello
public async Task Handle(ConversationClosed item)
{
var goodbye = $"********************* {item.UserId} said {item.UserMessage} ************************";
var evt = new Output
{
Message = goodbye
};
await PublishMessageAsync(evt).ConfigureAwait(false);
var evt = new Output { Message = goodbye };
await PublishMessageAsync(evt).ConfigureAwait(true);
await PublishMessageAsync(new Shutdown()).ConfigureAwait(false);
}
// Signal shutdown.
public async Task Handle(Shutdown item)
{
Console.WriteLine("Shutting down...");
hostApplicationLifetime.StopApplication();
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -9,7 +9,7 @@ using Microsoft.AutoGen.Agents;
var app = await AgentsApp.PublishMessageAsync("HelloAgents", new NewMessageReceived
{
Message = "World"
}, local: true);
}, local: false);
await app.WaitForShutdownAsync();

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!--orleans doesn't have strong name package-->

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

View File

@ -1,8 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>

View File

@ -5,7 +5,7 @@
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

View File

@ -5,7 +5,7 @@
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -98,7 +98,7 @@ public class FunctionContract
[NamespaceKey] = contract.Namespace,
[ClassNameKey] = contract.ClassName,
},
Parameters = [.. contract.Parameters?.Select(p => (AIFunctionParameterMetadata)p)],
Parameters = [.. contract.Parameters?.Select(p => (AIFunctionParameterMetadata)p)!],
};
}
}

View File

@ -19,4 +19,5 @@ public interface IAgentBase
Task<T> ReadAsync<T>(AgentId agentId, CancellationToken cancellationToken = default) where T : IMessage, new();
ValueTask PublishEventAsync(CloudEvent item, CancellationToken cancellationToken = default);
ValueTask PublishEventAsync(string topic, IMessage evt, CancellationToken cancellationToken = default);
List<string> Subscribe(string topic);
}

View File

@ -13,6 +13,7 @@ public interface IAgentRuntime
ValueTask<AgentState> ReadAsync(AgentId agentId, CancellationToken cancellationToken = default);
ValueTask SendResponseAsync(RpcRequest request, RpcResponse response, CancellationToken cancellationToken = default);
ValueTask SendRequestAsync(IAgentBase agent, RpcRequest request, CancellationToken cancellationToken = default);
ValueTask SendMessageAsync(Message message, CancellationToken cancellationToken = default);
ValueTask PublishEventAsync(CloudEvent @event, CancellationToken cancellationToken = default);
void Update(Activity? activity, RpcRequest request);
void Update(Activity? activity, CloudEvent cloudEvent);

View File

@ -8,6 +8,7 @@ public interface IAgentWorker
ValueTask PublishEventAsync(CloudEvent evt, CancellationToken cancellationToken = default);
ValueTask SendRequestAsync(IAgentBase agent, RpcRequest request, CancellationToken cancellationToken = default);
ValueTask SendResponseAsync(RpcResponse response, CancellationToken cancellationToken = default);
ValueTask SendMessageAsync(Message message, CancellationToken cancellationToken = default);
ValueTask StoreAsync(AgentState value, CancellationToken cancellationToken = default);
ValueTask<AgentState> ReadAsync(AgentId agentId, CancellationToken cancellationToken = default);
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>AutoGen.Core</PackageId>

View File

@ -114,6 +114,27 @@ public abstract class AgentBase : IAgentBase, IHandle
break;
}
}
public List<string> Subscribe(string topic)
{
Message message = new()
{
AddSubscriptionRequest = new()
{
RequestId = Guid.NewGuid().ToString(),
Subscription = new Subscription
{
TypeSubscription = new TypeSubscription
{
TopicType = topic,
AgentType = this.AgentId.Key
}
}
}
};
_context.SendMessageAsync(message).AsTask().Wait();
return new List<string> { topic };
}
public async Task StoreAsync(AgentState state, CancellationToken cancellationToken = default)
{
await _context.StoreAsync(state, cancellationToken).ConfigureAwait(false);

View File

@ -45,6 +45,10 @@ internal sealed class AgentRuntime(AgentId agentId, IAgentWorker worker, ILogger
{
await worker.SendRequestAsync(agent, request, cancellationToken).ConfigureAwait(false);
}
public async ValueTask SendMessageAsync(Message message, CancellationToken cancellationToken = default)
{
await worker.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
}
public async ValueTask PublishEventAsync(CloudEvent @event, CancellationToken cancellationToken = default)
{
await worker.PublishEventAsync(@event, cancellationToken).ConfigureAwait(false);

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>Microsoft.AutoGen.Agents</PackageId>

View File

@ -47,7 +47,7 @@ public class AgentWorker :
{
foreach (var (typeName, _) in _agentTypes)
{
if (typeName == "Client") { continue; }
if (typeName == nameof(Client)) { continue; }
var agent = GetOrActivateAgent(new AgentId(typeName, cloudEvent.Source));
agent.ReceiveMessage(new Message { CloudEvent = cloudEvent });
}
@ -63,6 +63,10 @@ public class AgentWorker :
{
return _mailbox.Writer.WriteAsync(new Message { Response = response }, cancellationToken);
}
public ValueTask SendMessageAsync(Message message, CancellationToken cancellationToken = default)
{
return _mailbox.Writer.WriteAsync(message, cancellationToken);
}
public ValueTask StoreAsync(AgentState value, CancellationToken cancellationToken = default)
{
var agentId = value.AgentId ?? throw new InvalidOperationException("AgentId is required when saving AgentState.");
@ -92,6 +96,10 @@ public class AgentWorker :
if (message == null) { continue; }
switch (message)
{
case Message.MessageOneofCase.AddSubscriptionResponse:
break;
case Message.MessageOneofCase.RegisterAgentTypeResponse:
break;
case Message msg:
var item = msg.CloudEvent;

View File

@ -29,7 +29,7 @@ public static class AgentWorkerHostingExtensions
public static IHostApplicationBuilder AddLocalAgentService(this IHostApplicationBuilder builder, bool useGrpc = true)
{
return builder.AddAgentService(local: true, useGrpc);
return builder.AddAgentService(local: false, useGrpc);
}
public static WebApplication MapAgentService(this WebApplication app, bool local = false, bool useGrpc = true)

View File

@ -85,6 +85,13 @@ public sealed class GrpcAgentWorker(
}
break;
case Message.MessageOneofCase.AddSubscriptionResponse:
if (!message.AddSubscriptionResponse.Success)
{
throw new InvalidOperationException($"Failed to add subscription: '{message.AddSubscriptionResponse.Error}'.");
}
break;
case Message.MessageOneofCase.CloudEvent:
// HACK: Send the message to an instance of each agent type
@ -153,6 +160,13 @@ public sealed class GrpcAgentWorker(
item.WriteCompletionSource?.TrySetCanceled();
break;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable)
{
// we could not connect to the endpoint - most likely we have the wrong port or failed ssl
// we need to let the user know what port we tried to connect to and then do backoff and retry
_logger.LogError(ex, "Error connecting to GRPC endpoint {Endpoint}.", channel.ToString());
break;
}
catch (Exception ex) when (!_shutdownCts.IsCancellationRequested)
{
item.WriteCompletionSource?.TrySetException(ex);
@ -230,6 +244,11 @@ public sealed class GrpcAgentWorker(
await WriteChannelAsync(new Message { Request = request }, cancellationToken).ConfigureAwait(false);
}
// new is intentional
public new async ValueTask SendMessageAsync(Message message, CancellationToken cancellationToken = default)
{
await WriteChannelAsync(message, cancellationToken).ConfigureAwait(false);
}
// new is intentional
public new async ValueTask PublishEventAsync(CloudEvent @event, CancellationToken cancellationToken = default)
{
await WriteChannelAsync(new Message { CloudEvent = @event }, cancellationToken).ConfigureAwait(false);

View File

@ -11,12 +11,12 @@ namespace Microsoft.AutoGen.Agents;
public static class GrpcAgentWorkerHostBuilderExtensions
{
private const string _defaultAgentServiceAddress = "https://localhost:5001";
public static IHostApplicationBuilder AddGrpcAgentWorker(this IHostApplicationBuilder builder, string agentServiceAddress = _defaultAgentServiceAddress)
private const string _defaultAgentServiceAddress = "https://localhost:53071";
public static IHostApplicationBuilder AddGrpcAgentWorker(this IHostApplicationBuilder builder, string? agentServiceAddress = null)
{
builder.Services.AddGrpcClient<AgentRpc.AgentRpcClient>(options =>
{
options.Address = new Uri(agentServiceAddress);
options.Address = new Uri(agentServiceAddress ?? builder.Configuration["AGENT_HOST"] ?? _defaultAgentServiceAddress);
options.ChannelOptionsActions.Add(channelOptions =>
{

View File

@ -16,10 +16,13 @@ public sealed class GrpcGateway : BackgroundService, IGateway
private readonly IClusterClient _clusterClient;
private readonly ConcurrentDictionary<string, AgentState> _agentState = new();
private readonly IRegistryGrain _gatewayRegistry;
private readonly ISubscriptionsGrain _subscriptions;
private readonly IGateway _reference;
// The agents supported by each worker process.
private readonly ConcurrentDictionary<string, List<GrpcWorkerConnection>> _supportedAgentTypes = [];
public readonly ConcurrentDictionary<IConnection, IConnection> _workers = new();
private readonly ConcurrentDictionary<string, Subscription> _subscriptionsByAgentType = new();
private readonly ConcurrentDictionary<string, List<string>> _subscriptionsByTopic = new();
// The mapping from agent id to worker process.
private readonly ConcurrentDictionary<(string Type, string Key), GrpcWorkerConnection> _agentDirectory = new();
@ -33,6 +36,7 @@ public sealed class GrpcGateway : BackgroundService, IGateway
_clusterClient = clusterClient;
_reference = clusterClient.CreateObjectReference<IGateway>(this);
_gatewayRegistry = clusterClient.GetGrain<IRegistryGrain>(0);
_subscriptions = clusterClient.GetGrain<ISubscriptionsGrain>(0);
}
public async ValueTask BroadcastEvent(CloudEvent evt)
{
@ -102,16 +106,54 @@ public sealed class GrpcGateway : BackgroundService, IGateway
case Message.MessageOneofCase.RegisterAgentTypeRequest:
await RegisterAgentTypeAsync(connection, message.RegisterAgentTypeRequest);
break;
case Message.MessageOneofCase.AddSubscriptionRequest:
await AddSubscriptionAsync(connection, message.AddSubscriptionRequest);
break;
default:
throw new InvalidOperationException($"Unknown message type for message '{message}'.");
// if it wasn't recognized return bad request
await RespondBadRequestAsync(connection, $"Unknown message type for message '{message}'.");
break;
};
}
private async ValueTask RespondBadRequestAsync(GrpcWorkerConnection connection, string error)
{
throw new RpcException(new Status(StatusCode.InvalidArgument, error));
}
private async ValueTask AddSubscriptionAsync(GrpcWorkerConnection connection, AddSubscriptionRequest request)
{
var topic = request.Subscription.TypeSubscription.TopicType;
var agentType = request.Subscription.TypeSubscription.AgentType;
_subscriptionsByAgentType[agentType] = request.Subscription;
_subscriptionsByTopic.GetOrAdd(topic, _ => []).Add(agentType);
await _subscriptions.Subscribe(topic, agentType);
//var response = new AddSubscriptionResponse { RequestId = request.RequestId, Error = "", Success = true };
Message response = new()
{
AddSubscriptionResponse = new()
{
RequestId = request.RequestId,
Error = "",
Success = true
}
};
await connection.ResponseStream.WriteAsync(response).ConfigureAwait(false);
}
private async ValueTask RegisterAgentTypeAsync(GrpcWorkerConnection connection, RegisterAgentTypeRequest msg)
{
connection.AddSupportedType(msg.Type);
_supportedAgentTypes.GetOrAdd(msg.Type, _ => []).Add(connection);
await _gatewayRegistry.RegisterAgentType(msg.Type, _reference);
await _gatewayRegistry.RegisterAgentType(msg.Type, _reference).ConfigureAwait(true);
Message response = new()
{
RegisterAgentTypeResponse = new()
{
RequestId = msg.RequestId,
Error = "",
Success = true
}
};
await connection.ResponseStream.WriteAsync(response).ConfigureAwait(false);
}
private async ValueTask DispatchEventAsync(CloudEvent evt)
{

View File

@ -14,11 +14,11 @@ public static class Host
builder.AddServiceDefaults();
if (local)
{
builder.AddLocalAgentService(useGrpc);
builder.AddLocalAgentService(useGrpc: useGrpc);
}
else
{
builder.AddAgentService(useGrpc);
builder.AddAgentService(useGrpc: useGrpc);
}
var app = builder.Build();
app.MapAgentService(local, useGrpc);

View File

@ -15,7 +15,7 @@ namespace Microsoft.AutoGen.Agents;
public static class HostBuilderExtensions
{
private const string _defaultAgentServiceAddress = "https://localhost:5001";
private const string _defaultAgentServiceAddress = "https://localhost:53071";
public static IHostApplicationBuilder AddAgent<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TAgent>(this IHostApplicationBuilder builder, string typeName) where TAgent : AgentBase
@ -28,12 +28,12 @@ public static class HostBuilderExtensions
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)
public static IHostApplicationBuilder AddAgentWorker(this IHostApplicationBuilder builder, string? agentServiceAddress = null, bool local = false)
{
agentServiceAddress ??= builder.Configuration["AGENT_HOST"] ?? _defaultAgentServiceAddress;
builder.Services.TryAddSingleton(DistributedContextPropagator.Current);
// if !local, then add the gRPC client

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ISubscriptionsGrain.cs
namespace Microsoft.AutoGen.Agents;
public interface ISubscriptionsGrain : IGrainWithIntegerKey
{
ValueTask Subscribe(string agentType, string topic);
ValueTask Unsubscribe(string agentType, string topic);
ValueTask<Dictionary<string, List<string>>> GetSubscriptions(string agentType);
}

View File

@ -5,7 +5,7 @@ using Microsoft.AutoGen.Abstractions;
namespace Microsoft.AutoGen.Agents;
public sealed class RegistryGrain : Grain, IRegistryGrain
internal sealed class RegistryGrain : Grain, IRegistryGrain
{
// TODO: use persistent state for some of these or (better) extend Orleans to implement some of this natively.
private readonly Dictionary<IGateway, WorkerState> _workerStates = new();

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SubscriptionsGrain.cs
namespace Microsoft.AutoGen.Agents;
internal sealed class SubscriptionsGrain([PersistentState("state", "PubSubStore")] IPersistentState<SubscriptionsState> state) : Grain, ISubscriptionsGrain
{
private readonly Dictionary<string, List<string>> _subscriptions = new();
public ValueTask<Dictionary<string, List<string>>> GetSubscriptions(string agentType)
{
return new ValueTask<Dictionary<string, List<string>>>(_subscriptions);
}
public ValueTask Subscribe(string agentType, string topic)
{
if (!_subscriptions.TryGetValue(topic, out var subscriptions))
{
subscriptions = _subscriptions[topic] = [];
}
if (!subscriptions.Contains(agentType))
{
subscriptions.Add(agentType);
}
_subscriptions[topic] = subscriptions;
state.State.Subscriptions = _subscriptions;
state.WriteStateAsync();
return ValueTask.CompletedTask;
}
public ValueTask Unsubscribe(string agentType, string topic)
{
if (!_subscriptions.TryGetValue(topic, out var subscriptions))
{
subscriptions = _subscriptions[topic] = [];
}
if (!subscriptions.Contains(agentType))
{
subscriptions.Remove(agentType);
}
_subscriptions[topic] = subscriptions;
state.State.Subscriptions = _subscriptions;
state.WriteStateAsync();
return ValueTask.CompletedTask;
}
}
public sealed class SubscriptionsState
{
public Dictionary<string, List<string>> Subscriptions { get; set; } = new();
}

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
@ -14,6 +14,6 @@
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="System.Text.Json" VersionOverride="9.0.0-rc.2.24473.5" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
</Project>

View File

@ -6,7 +6,7 @@
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>

View File

@ -14,7 +14,7 @@
<ProjectReference Include="..\..\src\AutoGen\AutoGen.csproj" />
<ProjectReference Include="..\AutoGen.Test.Share\AutoGen.Tests.Share.csproj" />
<PackageReference Include="Microsoft.Extensions.AI" />
<PackageReference Include="System.Text.Json" VersionOverride="9.0.0-rc.2.24473.5" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup>

3
python/.gitignore vendored
View File

@ -172,3 +172,6 @@ docs/**/jupyter_execute
# Temporary files
tmp_code_*.py
# .NET Development settings
appsettings.Development.json