First round of cleanup of runtime abstractions (#156)

This commit is contained in:
Stephen Toub
2025-07-10 07:57:51 -04:00
committed by GitHub
Unverified
parent a233d31813
commit fbf1f10a8a
76 changed files with 1254 additions and 2079 deletions
@@ -16,114 +16,87 @@ public sealed class BasicMessage
public sealed class TestException : Exception;
#pragma warning restore RCS1194 // Implement exception constructors
public sealed class PublisherAgent : TestAgent, IHandle<BasicMessage>
public sealed class PublisherAgent : TestAgent
{
private readonly IList<TopicId> _targetTopics;
public PublisherAgent(AgentId id, IAgentRuntime runtime, string description, IList<TopicId> targetTopics)
: base(id, runtime, description)
public PublisherAgent(ActorId id, IAgentRuntime runtime, string description, IList<TopicId> targetTopics) : base(id, runtime, description)
{
this._targetTopics = targetTopics;
}
public async ValueTask HandleAsync(BasicMessage item, MessageContext messageContext)
{
this.ReceivedMessages.Add(item);
foreach (TopicId targetTopic in this._targetTopics)
this.RegisterMessageHandler<BasicMessage>(async (item, messageContext, cancellationToken) =>
{
await this.PublishMessageAsync(
new BasicMessage { Content = $"@{targetTopic}: {item.Content}" },
targetTopic);
}
this.ReceivedMessages.Add(item);
foreach (TopicId targetTopic in targetTopics)
{
await this.PublishMessageAsync(
new BasicMessage { Content = $"@{targetTopic}: {item.Content}" },
targetTopic,
cancellationToken: cancellationToken);
}
});
}
}
public sealed class SendOnAgent : TestAgent, IHandle<BasicMessage>
public sealed class SendOnAgent : TestAgent
{
private readonly IList<Guid> _targetKeys;
public SendOnAgent(AgentId id, IAgentRuntime runtime, string description, IList<Guid> targetKeys)
: base(id, runtime, description)
public SendOnAgent(ActorId id, IAgentRuntime runtime, string description, IList<Guid> targetKeys) : base(id, runtime, description)
{
this._targetKeys = targetKeys;
}
public async ValueTask HandleAsync(BasicMessage item, MessageContext messageContext)
{
foreach (Guid targetKey in this._targetKeys)
this.RegisterMessageHandler<BasicMessage>(async (item, messageContext, cancellationToken) =>
{
AgentId targetId = new(nameof(ReceiverAgent), targetKey.ToString());
BasicMessage response = new() { Content = $"@{targetKey}: {item.Content}" };
await this.SendMessageAsync(response, targetId);
}
foreach (Guid targetKey in targetKeys)
{
ActorId targetId = new(nameof(ReceiverAgent), targetKey.ToString());
BasicMessage response = new() { Content = $"@{targetKey}: {item.Content}" };
await this.SendMessageAsync(response, targetId, cancellationToken: cancellationToken);
}
});
}
}
public sealed class ReceiverAgent : TestAgent, IHandle<BasicMessage>
public sealed class ReceiverAgent : TestAgent
{
public List<BasicMessage> Messages { get; } = [];
public ReceiverAgent(AgentId id, IAgentRuntime runtime, string description)
: base(id, runtime, description)
public ReceiverAgent(ActorId id, IAgentRuntime runtime, string description) : base(id, runtime, description)
{
}
public ValueTask HandleAsync(BasicMessage item, MessageContext messageContext)
{
this.Messages.Add(item);
return default;
this.RegisterMessageHandler<BasicMessage>(async (item, messageContext, cancellationToken) =>
{
this.Messages.Add(item);
});
}
}
public sealed class ProcessorAgent : TestAgent, IHandle<BasicMessage, BasicMessage>
public sealed class ProcessorAgent : TestAgent
{
private Func<string, string> ProcessFunc { get; }
public ProcessorAgent(AgentId id, IAgentRuntime runtime, Func<string, string> processFunc, string description)
: base(id, runtime, description)
public ProcessorAgent(ActorId id, IAgentRuntime runtime, Func<string, string> processFunc, string description) : base(id, runtime, description)
{
this.ProcessFunc = processFunc;
}
public ValueTask<BasicMessage> HandleAsync(BasicMessage item, MessageContext messageContext)
{
BasicMessage result = new() { Content = this.ProcessFunc.Invoke(((BasicMessage)item).Content) };
return new(result);
this.RegisterMessageHandler<BasicMessage, BasicMessage>(async (item, messageContext, cancellationtoken) =>
{
return new BasicMessage() { Content = processFunc.Invoke(((BasicMessage)item).Content) };
});
}
}
public sealed class CancelAgent : TestAgent, IHandle<BasicMessage>
public sealed class CancelAgent : TestAgent
{
public CancelAgent(AgentId id, IAgentRuntime runtime, string description)
: base(id, runtime, description)
public CancelAgent(ActorId id, IAgentRuntime runtime, string description) : base(id, runtime, description)
{
}
public ValueTask HandleAsync(BasicMessage item, MessageContext messageContext)
{
CancellationToken cancelledToken = new(canceled: true);
cancelledToken.ThrowIfCancellationRequested();
return default;
this.RegisterMessageHandler<BasicMessage>(async (item, messageContext, cancellationToken) =>
{
CancellationToken cancelledToken = new(canceled: true);
cancelledToken.ThrowIfCancellationRequested();
});
}
}
public sealed class ErrorAgent : TestAgent, IHandle<BasicMessage>
public sealed class ErrorAgent : TestAgent
{
public ErrorAgent(AgentId id, IAgentRuntime runtime, string description)
: base(id, runtime, description)
public ErrorAgent(ActorId id, IAgentRuntime runtime, string description) : base(id, runtime, description)
{
this.RegisterMessageHandler<BasicMessage>(async (item, messageContext, cancellationToken) =>
{
this.DidThrow = true;
throw new TestException();
});
}
public bool DidThrow { get; private set; }
public ValueTask HandleAsync(BasicMessage item, MessageContext messageContext)
{
this.DidThrow = true;
throw new TestException();
}
}
public sealed class MessagingTestFixture
@@ -131,23 +104,23 @@ public sealed class MessagingTestFixture
private Dictionary<Type, object> AgentsTypeMap { get; } = [];
public InProcessRuntime Runtime { get; } = new();
public ValueTask<AgentType> RegisterFactoryMapInstances<TAgent>(AgentType type, Func<AgentId, IAgentRuntime, ValueTask<TAgent>> factory)
where TAgent : IHostableAgent
public ValueTask<ActorType> RegisterFactoryMapInstances<TAgent>(ActorType type, Func<ActorId, IAgentRuntime, ValueTask<TAgent>> factory)
where TAgent : IRuntimeActor
{
async ValueTask<TAgent> WrappedFactory(AgentId id, IAgentRuntime runtime)
async ValueTask<TAgent> WrappedFactory(ActorId id, IAgentRuntime runtime)
{
TAgent agent = await factory(id, runtime);
this.GetAgentInstances<TAgent>()[id] = agent;
return agent;
}
return this.Runtime.RegisterAgentFactoryAsync(type, WrappedFactory);
return this.Runtime.RegisterActorFactoryAsync(type, WrappedFactory);
}
public Dictionary<AgentId, TAgent> GetAgentInstances<TAgent>() where TAgent : IHostableAgent
public Dictionary<ActorId, TAgent> GetAgentInstances<TAgent>() where TAgent : IRuntimeActor
{
if (!this.AgentsTypeMap.TryGetValue(typeof(TAgent), out object? maybeAgentMap) ||
maybeAgentMap is not Dictionary<AgentId, TAgent> result)
maybeAgentMap is not Dictionary<ActorId, TAgent> result)
{
this.AgentsTypeMap[typeof(TAgent)] = result = [];
}
@@ -157,24 +130,24 @@ public sealed class MessagingTestFixture
public async ValueTask RegisterReceiverAgentAsync(string? agentNameSuffix = null, params string[] topicTypes)
{
await this.RegisterFactoryMapInstances(
$"{nameof(ReceiverAgent)}{agentNameSuffix ?? string.Empty}",
new($"{nameof(ReceiverAgent)}{agentNameSuffix ?? string.Empty}"),
(id, runtime) => new ValueTask<ReceiverAgent>(new ReceiverAgent(id, runtime, string.Empty)));
foreach (string topicType in topicTypes)
{
await this.Runtime.AddSubscriptionAsync(new TestSubscription(topicType, $"{nameof(ReceiverAgent)}{agentNameSuffix ?? string.Empty}"));
await this.Runtime.AddSubscriptionAsync(new TestSubscription(topicType, new($"{nameof(ReceiverAgent)}{agentNameSuffix ?? string.Empty}")));
}
}
public async ValueTask RegisterErrorAgentAsync(string? agentNameSuffix = null, params string[] topicTypes)
{
await this.RegisterFactoryMapInstances(
$"{nameof(ErrorAgent)}{agentNameSuffix ?? string.Empty}",
new($"{nameof(ErrorAgent)}{agentNameSuffix ?? string.Empty}"),
(id, runtime) => new ValueTask<ErrorAgent>(new ErrorAgent(id, runtime, string.Empty)));
foreach (string topicType in topicTypes)
{
await this.Runtime.AddSubscriptionAsync(new TestSubscription(topicType, $"{nameof(ErrorAgent)}{agentNameSuffix ?? string.Empty}"));
await this.Runtime.AddSubscriptionAsync(new TestSubscription(topicType, new($"{nameof(ErrorAgent)}{agentNameSuffix ?? string.Empty}")));
}
}
@@ -187,7 +160,7 @@ public sealed class MessagingTestFixture
await this.Runtime.RunUntilIdleAsync();
}
public async ValueTask<object?> RunSendTestAsync(AgentId sendTarget, object message, string? messageId = null)
public async ValueTask<object?> RunSendTestAsync(ActorId sendTarget, object message, string? messageId = null)
{
messageId ??= Guid.NewGuid().ToString();