From a72ebaef90b673e8a8d3efc0af950f57e75301ea Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Mon, 23 Sep 2024 10:50:18 -0700 Subject: [PATCH] Rysweet 531 dotnet create hello agent continued (#606) fixing base agent classes for io --- dotnet/Directory.Packages.props | 2 + .../{Agents/HelloAgent => }/Program.cs | 0 .../AgentBaseExtensions.cs | 2 +- .../IOAgent/ConsoleAgent/ConsoleAgent.cs | 59 ++++++++++++ .../Agents/IOAgent/FileAgent/FileAgent.cs | 83 +++++++++++++++++ .../Agents/IOAgent/IOAgent.cs | 18 +++- .../Agents/IOAgent/WebAPIAgent/WebAPIAgent.cs | 90 +++++++++++++++++++ ...rosoft.AutoGen.Agents.Worker.Client.csproj | 5 ++ .../Microsoft.AutoGen.Agents.csproj | 5 +- protos/agent_events.proto | 8 +- protos/agent_states.proto | 8 ++ 11 files changed, 270 insertions(+), 10 deletions(-) rename dotnet/samples/HelloAgents/HelloAgents.Agents/{Agents/HelloAgent => }/Program.cs (100%) create mode 100644 dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/ConsoleAgent/ConsoleAgent.cs create mode 100644 dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/FileAgent/FileAgent.cs create mode 100644 dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/WebAPIAgent/WebAPIAgent.cs create mode 100644 protos/agent_states.proto diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 440a7c03..42ccc66d 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -41,6 +41,8 @@ + + diff --git a/dotnet/samples/HelloAgents/HelloAgents.Agents/Agents/HelloAgent/Program.cs b/dotnet/samples/HelloAgents/HelloAgents.Agents/Program.cs similarity index 100% rename from dotnet/samples/HelloAgents/HelloAgents.Agents/Agents/HelloAgent/Program.cs rename to dotnet/samples/HelloAgents/HelloAgents.Agents/Program.cs diff --git a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/AgentBaseExtensions.cs b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/AgentBaseExtensions.cs index 9d2235da..a912d423 100644 --- a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/AgentBaseExtensions.cs +++ b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/AgentBaseExtensions.cs @@ -70,7 +70,7 @@ public static class AgentBaseExtensions activity.Start(); // rpc attributes from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md - activity.SetTag("rpc.system", "starfleet"); + activity.SetTag("rpc.system", "autogen"); activity.SetTag("rpc.service", agent.AgentId.ToString()); activity.SetTag("rpc.method", methodName); } diff --git a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/ConsoleAgent/ConsoleAgent.cs b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/ConsoleAgent/ConsoleAgent.cs new file mode 100644 index 00000000..97fe4280 --- /dev/null +++ b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/ConsoleAgent/ConsoleAgent.cs @@ -0,0 +1,59 @@ +using Microsoft.AutoGen.Agents.Abstractions; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AutoGen.Agents.Worker.Client; + +public class ConsoleAgent : IOAgent, + IUseConsole, + IHandle, + IHandle +{ + + // instead of the primary constructor above, make a constructr here that still calls the base constructor + public ConsoleAgent(IAgentContext context, [FromKeyedServices("EventTypes")] EventTypes typeRegistry) : base(context, typeRegistry) + { + _route = "console"; + } + public override async Task Handle(Input item) + { + Console.WriteLine("Please enter input:"); + string content = Console.ReadLine() ?? string.Empty; + + await ProcessInput(content); + + var evt = new InputProcessed + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public override async Task Handle(Output item) + { + // Assuming item has a property `Content` that we want to write to the console + Console.WriteLine(item.Message); + + var evt = new OutputWritten + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public override Task ProcessInput(string message) + { + // Implement your input processing logic here + return Task.FromResult(message); + } + + public override Task ProcessOutput(string message) + { + // Implement your output processing logic here + return Task.CompletedTask; + } +} + +public interface IUseConsole +{ + public Task ProcessOutput(string message); +} \ No newline at end of file diff --git a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/FileAgent/FileAgent.cs b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/FileAgent/FileAgent.cs new file mode 100644 index 00000000..b91fdd0d --- /dev/null +++ b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/FileAgent/FileAgent.cs @@ -0,0 +1,83 @@ +using Microsoft.AutoGen.Agents.Abstractions; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AutoGen.Agents.Worker.Client; + +[TopicSubscription("FileIO")] +public class FileAgent : IOAgent, + IUseFiles, + IHandle, + IHandle +{ + public FileAgent(IAgentContext context, EventTypes typeRegistry, string filePath) : base(context, typeRegistry) + { + _filePath = filePath; + } + private readonly string _filePath; + + public override async Task Handle(Input item) + { + + // validate that the file exists + if (!File.Exists(_filePath)) + { + string errorMessage = $"File not found: {_filePath}"; + Logger.LogError(errorMessage); + //publish IOError event + var err = new IOError + { + Message = errorMessage + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(err); + return; + } + + string content; + using (var reader = new StreamReader(item.Message)) + { + content = await reader.ReadToEndAsync(); + } + + await ProcessInput(content); + + var evt = new InputProcessed + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public override async Task Handle(Output item) + { + using (var writer = new StreamWriter(_filePath, append: true)) + { + await writer.WriteLineAsync(item.Message); + } + + var evt = new OutputWritten + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public override async Task ProcessInput(string message) + { + var evt = new InputProcessed + { + Route = _route, + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + return message; + } + + public override Task ProcessOutput(string message) + { + // Implement your output processing logic here + return Task.CompletedTask; + } +} + +public interface IUseFiles +{ +} \ No newline at end of file diff --git a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/IOAgent.cs b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/IOAgent.cs index 710c495e..ac00010d 100644 --- a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/IOAgent.cs +++ b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/IOAgent.cs @@ -5,16 +5,16 @@ namespace Microsoft.AutoGen.Agents.Worker.Client; public abstract class IOAgent : AgentBase where T : class, new() { protected AgentState _state; - private readonly string _route = "console"; + public string _route = "base"; public IOAgent(IAgentContext context, EventTypes typeRegistry) : base(context, typeRegistry) { _state = new(); } - public async Task Handle(Input item) + public virtual async Task Handle(Input item) { - //var processed = await ProcessInput(item.Message); + var evt = new InputProcessed { Route = _route @@ -22,6 +22,16 @@ public abstract class IOAgent : AgentBase where T : class, new() await PublishEvent(evt); } - public abstract Task ProcessInput(string message); + public virtual async Task Handle(Output item) + { + var evt = new OutputWritten + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public abstract Task ProcessInput(string message); + public abstract Task ProcessOutput(string message); } \ No newline at end of file diff --git a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/WebAPIAgent/WebAPIAgent.cs b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/WebAPIAgent/WebAPIAgent.cs new file mode 100644 index 00000000..9afec96d --- /dev/null +++ b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Agents/IOAgent/WebAPIAgent/WebAPIAgent.cs @@ -0,0 +1,90 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.AutoGen.Agents.Abstractions; + +namespace Microsoft.AutoGen.Agents.Worker.Client; + +public class WebAPIAgent : IOAgent, + IUseWebAPI, + IHandle, + IHandle +{ + private readonly string _url = "/agents/webio"; + + public WebAPIAgent( + IAgentContext context, + [FromKeyedServices("EventTypes")] EventTypes typeRegistry, + string url, + ILogger logger) : base( + context, + typeRegistry) + { + _url = url; + var builder = WebApplication.CreateBuilder(); + var app = builder.Build(); + + app.MapPost(_url, async (HttpContext httpContext) => + { + var input = await httpContext.Request.ReadFromJsonAsync(); + if (input != null) + { + await Handle(input); + await httpContext.Response.WriteAsync("Input processed"); + } + else + { + httpContext.Response.StatusCode = 400; + await httpContext.Response.WriteAsync("Invalid input"); + } + }); + + app.MapGet(_url, async (HttpContext httpContext) => + { + var output = new Output(); // Replace with actual output retrieval logic + await Handle(output); + await httpContext.Response.WriteAsJsonAsync(output); + }); + + app.Run(); + } + + public override async Task Handle(Input item) + { + // Process the input (this is a placeholder, replace with actual processing logic) + await ProcessInput(item.Message); + + var evt = new InputProcessed + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public override async Task Handle(Output item) + { + // Assuming item has a property `Content` that we want to return in the response + var evt = new OutputWritten + { + Route = _route + }.ToCloudEvent(this.AgentId.Key); + await PublishEvent(evt); + } + + public override Task ProcessInput(string message) + { + // Implement your input processing logic here + return Task.FromResult(message); + } + + public override Task ProcessOutput(string message) + { + // Implement your output processing logic here + return Task.CompletedTask; + } +} + +public interface IUseWebAPI +{ +} \ No newline at end of file diff --git a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Microsoft.AutoGen.Agents.Worker.Client.csproj b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Microsoft.AutoGen.Agents.Worker.Client.csproj index e8a63849..0af9d0a1 100644 --- a/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Microsoft.AutoGen.Agents.Worker.Client.csproj +++ b/dotnet/src/Microsoft.AutoGen.Agents.Worker.Client/Microsoft.AutoGen.Agents.Worker.Client.csproj @@ -15,4 +15,9 @@ + + + + + diff --git a/dotnet/src/Microsoft.AutoGen.Agents/Microsoft.AutoGen.Agents.csproj b/dotnet/src/Microsoft.AutoGen.Agents/Microsoft.AutoGen.Agents.csproj index 35bfcbf9..deadfa8c 100644 --- a/dotnet/src/Microsoft.AutoGen.Agents/Microsoft.AutoGen.Agents.csproj +++ b/dotnet/src/Microsoft.AutoGen.Agents/Microsoft.AutoGen.Agents.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - AutoGennnnnnn.Core + AutoGen.Core https://github.com/microsoft/agnext Microsoft AutoGenn Core Library @@ -13,7 +13,8 @@ - + + diff --git a/protos/agent_events.proto b/protos/agent_events.proto index c0984f4d..6c20466c 100644 --- a/protos/agent_events.proto +++ b/protos/agent_events.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package helloagents; +package agents; option csharp_namespace = "Microsoft.AutoGen.Agents.Worker.Client"; @@ -14,7 +14,9 @@ message InputProcessed { message Output { string message = 1; } - -message OutputProcessed { +message OutputWritten { string route = 1; } +message IOError { + string message = 1; +} diff --git a/protos/agent_states.proto b/protos/agent_states.proto new file mode 100644 index 00000000..e4e41f6d --- /dev/null +++ b/protos/agent_states.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package agents; + +option csharp_namespace = "Microsoft.AutoGen.Agents.Worker.Client"; + +message AgentState { + string message = 1; +}