// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.Agents.AI.Workflows.Execution; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Workflows; internal static class MemberAttributeExtensions { public static (IEnumerable Sent, IEnumerable Yielded) GetAttributeTypes(this MemberInfo memberInfo) { IEnumerable sendsMessageAttrs = memberInfo.GetCustomAttributes(); IEnumerable yieldsOutputAttrs = memberInfo.GetCustomAttributes(); // TODO: Should we include [MessageHandler]? return (Sent: sendsMessageAttrs.Select(attr => attr.Type), Yielded: yieldsOutputAttrs.Select(attr => attr.Type)); } } /// /// . /// public sealed class ProtocolBuilder { private readonly HashSet _sendTypes = []; private readonly HashSet _yieldTypes = []; internal ProtocolBuilder(DelayedExternalRequestContext delayRequestContext) { this.RouteBuilder = new RouteBuilder(delayRequestContext); } /// /// Adds types registered in or /// on the target . This can be used to implement delegate-based request handling akin /// to what is provided by or . /// /// The delegate to be registered. /// public ProtocolBuilder AddDelegateAttributeTypes(Delegate @delegate) => this.AddMethodAttributeTypes(Throw.IfNull(@delegate).Method); /// /// Adds types registered in or /// on the target . This can be used to implement delegate-based request handling akin /// to what is provided by or . /// /// The method to be registered. /// public ProtocolBuilder AddMethodAttributeTypes(MethodInfo method) { (IEnumerable sentTypes, IEnumerable yieldTypes) = method.GetAttributeTypes(); this._sendTypes.UnionWith(sentTypes); this._yieldTypes.UnionWith(yieldTypes); return method.DeclaringType != null ? this.AddClassAttributeTypes(method.DeclaringType) : this; } /// /// Adds types registered in or /// on the target . This can be used to implement delegate-based request handling akin /// to what is provided by or . /// /// The type to be registered. /// public ProtocolBuilder AddClassAttributeTypes(Type executorType) { (IEnumerable sentTypes, IEnumerable yieldTypes) = executorType.GetAttributeTypes(); this._sendTypes.UnionWith(sentTypes); this._yieldTypes.UnionWith(yieldTypes); return this; } /// /// Adds the specified type to the set of declared "sent" message types for the protocol. Objects of these types will be allowed to be /// sent through the Executor's outgoing edges, via . /// /// The type to be declared. /// public ProtocolBuilder SendsMessage() where TMessage : notnull => this.SendsMessageTypes([typeof(TMessage)]); /// /// Adds the specified type to the set of declared "sent" messagetypes for the protocol. Objects of these types will be allowed to be /// sent through the Executor's outgoing edges, via . /// /// The type to be declared. /// public ProtocolBuilder SendsMessageType(Type messageType) => this.SendsMessageTypes([messageType]); /// /// Adds the specified types to the set of declared "sent" message types for the protocol. Objects of these types will be allowed to be /// sent through the Executor's outgoing edges, via . /// /// A set of types to be declared. /// public ProtocolBuilder SendsMessageTypes(IEnumerable messageTypes) { Throw.IfNull(messageTypes); this._sendTypes.UnionWith(messageTypes); return this; } /// /// Adds the specified output type to the set of declared "yielded" output types for the protocol. Objects of this type will be /// allowed to be output from the executor through the , via . /// /// The type to be declared. /// public ProtocolBuilder YieldsOutput() where TOutput : notnull => this.YieldsOutputTypes([typeof(TOutput)]); /// /// Adds the specified output type to the set of declared "yielded" output types for the protocol. Objects of this type will be /// allowed to be output from the executor through the , via . /// /// The type to be declared. /// public ProtocolBuilder YieldsOutputType(Type outputType) => this.YieldsOutputTypes([outputType]); /// /// Adds the specified types to the set of declared "yielded" output types for the protocol. Objects of these types will be allowed to be /// output from the executor through the , via . /// /// A set of types to be declared. /// public ProtocolBuilder YieldsOutputTypes(IEnumerable yieldedTypes) { Throw.IfNull(yieldedTypes); this._yieldTypes.UnionWith(yieldedTypes); return this; } /// /// Gets a route builder to configure message handlers. /// public RouteBuilder RouteBuilder { get; } /// /// Fluently configures message handlers. /// /// The handler configuration callback. /// public ProtocolBuilder ConfigureRoutes(Action configureAction) { configureAction(this.RouteBuilder); return this; } internal ExecutorProtocol Build(ExecutorOptions options) { MessageRouter router = this.RouteBuilder.Build(); HashSet sendTypes = new(this._sendTypes); if (options.AutoSendMessageHandlerResultObject) { sendTypes.UnionWith(router.DefaultOutputTypes); } HashSet yieldTypes = new(this._yieldTypes); if (options.AutoYieldOutputHandlerResultObject) { yieldTypes.UnionWith(router.DefaultOutputTypes); } return new(router, sendTypes, yieldTypes); } }