diff --git a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs
index 194632b56a..ece973d7d1 100644
--- a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs
+++ b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs
@@ -25,9 +25,15 @@ public abstract class Executor : IIdentified
///
/// A optional unique identifier for the executor. If null, a type-tagged
/// UUID will be generated.
- 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;
}
///
@@ -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);
}
diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs
index 4520e286ce..a117f1ae0b 100644
--- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs
+++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs
@@ -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}")
};
diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs
index dfb335b077..a19630bf31 100644
--- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs
+++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs
@@ -12,7 +12,7 @@ public sealed class ExecutorOptions
///
public static ExecutorOptions Default { get; } = new();
- private ExecutorOptions() { }
+ internal ExecutorOptions() { }
///
/// If , the result of a message handler that returns a value will be sent as a message to the workflow.
diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs
index a797832524..c2206fdea6 100644
--- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs
+++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs
@@ -34,7 +34,7 @@ internal class InProcessRunnerContext : IRunnerContext
this._executors[executorId] = executor = provider();
- if (executor is RequestInputExecutor requestInputExecutor)
+ if (executor is RequestInfoExecutor requestInputExecutor)
{
requestInputExecutor.AttachRequestSink(this);
}
diff --git a/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInputExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs
similarity index 65%
rename from dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInputExecutor.cs
rename to dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs
index ea181d3991..4cb151666f 100644
--- a/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInputExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs
@@ -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((request, context) => this.HandleAsync(request.Data, context));
+ }
+
+ return routeBuilder
// Handle incoming responses (as wrapped Response object)
.AddHandler(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;