mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
feat: Expose wrapped Request/Response API (#471)
* Allows creation of ExternalRequest objects directly to control the requestId * Allows receiving ExternalResponse objects rather than unwrapped Data * Normalizing naming
This commit is contained in:
committed by
GitHub
Unverified
parent
6e9d35830f
commit
b26d9c95fe
@@ -25,9 +25,15 @@ public abstract class Executor : IIdentified
|
||||
/// </summary>
|
||||
/// <param name="id">A optional unique identifier for the executor. If <c>null</c>, a type-tagged
|
||||
/// UUID will be generated.</param>
|
||||
protected Executor(string? id = null)
|
||||
protected Executor(string? id = null) : this(ExecutorOptions.Default, id)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly ExecutorOptions _options;
|
||||
internal Executor(ExecutorOptions options, string? id = null)
|
||||
{
|
||||
this.Id = id ?? $"{this.GetType().Name}/{Guid.NewGuid():N}";
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,7 +102,7 @@ public abstract class Executor : IIdentified
|
||||
}
|
||||
|
||||
// If we had a real return type, raise it as a SendMessage; TODO: Should we have a way to disable this behaviour?
|
||||
if (result.Result != null && ExecutorOptions.Default.AutoSendMessageHandlerResultObject)
|
||||
if (result.Result != null && this._options.AutoSendMessageHandlerResultObject)
|
||||
{
|
||||
await context.SendMessageAsync(result.Result).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public sealed class ExecutorIsh :
|
||||
{
|
||||
Type.Unbound => throw new InvalidOperationException($"Executor with ID '{this.Id}' is unbound."),
|
||||
Type.Executor => () => this._executorValue!,
|
||||
Type.InputPort => () => new RequestInputExecutor(this._inputPortValue!),
|
||||
Type.InputPort => () => new RequestInfoExecutor(this._inputPortValue!),
|
||||
Type.Agent => () => new AIAgentHostExecutor(this._aiAgentValue!),
|
||||
_ => throw new InvalidOperationException($"Unknown ExecutorIsh type: {this.ExecutorType}")
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ public sealed class ExecutorOptions
|
||||
/// </summary>
|
||||
public static ExecutorOptions Default { get; } = new();
|
||||
|
||||
private ExecutorOptions() { }
|
||||
internal ExecutorOptions() { }
|
||||
|
||||
/// <summary>
|
||||
/// If <see langword="true"/>, the result of a message handler that returns a value will be sent as a message to the workflow.
|
||||
|
||||
@@ -34,7 +34,7 @@ internal class InProcessRunnerContext<TExternalInput> : IRunnerContext
|
||||
|
||||
this._executors[executorId] = executor = provider();
|
||||
|
||||
if (executor is RequestInputExecutor requestInputExecutor)
|
||||
if (executor is RequestInfoExecutor requestInputExecutor)
|
||||
{
|
||||
requestInputExecutor.AttachRequestSink(this);
|
||||
}
|
||||
|
||||
+23
-4
@@ -7,22 +7,40 @@ using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.Workflows.Specialized;
|
||||
|
||||
internal class RequestInputExecutor : Executor
|
||||
internal class RequestInfoExecutor : Executor
|
||||
{
|
||||
private InputPort Port { get; }
|
||||
private IExternalRequestSink? RequestSink { get; set; }
|
||||
|
||||
public RequestInputExecutor(InputPort port) : base(port.Id)
|
||||
private static ExecutorOptions DefaultOptions => new()
|
||||
{
|
||||
// We need to be able to return the ExternalRequest/Result objects so they can be bubbled up
|
||||
// through the event system, but we do not want to forward the Request message.
|
||||
AutoSendMessageHandlerResultObject = false
|
||||
};
|
||||
|
||||
private readonly bool _allowWrapped;
|
||||
public RequestInfoExecutor(InputPort port, bool allowWrapped = true) : base(RequestInfoExecutor.DefaultOptions, port.Id)
|
||||
{
|
||||
this.Port = port;
|
||||
|
||||
this._allowWrapped = allowWrapped;
|
||||
}
|
||||
|
||||
protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder)
|
||||
{
|
||||
return routeBuilder
|
||||
routeBuilder = routeBuilder
|
||||
// Handle incoming requests (as raw request payloads)
|
||||
.AddHandler(this.Port.Request, this.HandleAsync)
|
||||
.AddHandler(typeof(object), this.HandleAsync)
|
||||
.AddHandler(typeof(object), this.HandleAsync);
|
||||
|
||||
if (this._allowWrapped)
|
||||
{
|
||||
routeBuilder = routeBuilder
|
||||
.AddHandler<ExternalRequest, ExternalRequest>((request, context) => this.HandleAsync(request.Data, context));
|
||||
}
|
||||
|
||||
return routeBuilder
|
||||
// Handle incoming responses (as wrapped Response object)
|
||||
.AddHandler<ExternalResponse, ExternalResponse>(this.HandleAsync);
|
||||
}
|
||||
@@ -53,6 +71,7 @@ internal class RequestInputExecutor : Executor
|
||||
$"Message type {message.Data.GetType().Name} is not assignable to the response type {this.Port.Response.Name} of input port {this.Port.Id}.");
|
||||
}
|
||||
|
||||
await context.SendMessageAsync(message).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(message.Data).ConfigureAwait(false);
|
||||
|
||||
return message;
|
||||
Reference in New Issue
Block a user