From 39598741e45d667c0e1f91c3097bb7baacee5555 Mon Sep 17 00:00:00 2001
From: Chris <66376200+crickman@users.noreply.github.com>
Date: Tue, 11 Nov 2025 10:36:18 -0800
Subject: [PATCH] .NET Workflows - Support "structured inputs" feature for
declarative workflows (#2053)
---
dotnet/agent-framework-dotnet.slnx | 1 +
.../Declarative/ConfirmInput/Program.cs | 2 +-
.../Declarative/DeepResearch/Program.cs | 2 +-
.../Declarative/FunctionTools/Program.cs | 2 +-
.../InputArguments/InputArguments.csproj | 39 +++++
.../InputArguments/InputArguments.yaml | 97 ++++++++++++
.../Declarative/InputArguments/Program.cs | 147 ++++++++++++++++++
.../Declarative/Marketing/Program.cs | 2 +-
.../Declarative/StudentTeacher/Program.cs | 2 +-
.../Declarative/ToolApproval/Program.cs | 2 +-
.../AzureAgentProvider.cs | 18 ++-
.../Extensions/AgentProviderExtensions.cs | 3 +-
.../Extensions/FormulaValueExtensions.cs | 35 +++--
.../Kit/AgentExecutor.cs | 2 +-
.../ObjectModel/InvokeAzureAgentExecutor.cs | 21 ++-
.../ObjectModel/SetVariableExecutor.cs | 7 +-
.../PowerFx/Functions/AgentMessage.cs | 15 ++
.../PowerFx/Functions/MessageFunction.cs | 36 +++++
.../PowerFx/Functions/MessageText.cs | 54 +++++++
.../PowerFx/Functions/UserMessage.cs | 29 +---
.../PowerFx/RecalcEngineFactory.cs | 4 +
.../WorkflowAgentProvider.cs | 9 +-
.../Agents/AgentProvider.cs | 4 +-
.../Agents/PoemAgentProvider.cs | 43 +++++
.../DeclarativeWorkflowTest.cs | 7 +-
.../Testcases/InputArguments.json | 23 +++
.../Testcases/MathChat.json | 2 +-
.../Workflows/InputArguments.yaml | 15 ++
.../PowerFx/Functions/AgentMessageTests.cs | 68 ++++++++
.../PowerFx/Functions/MessageTextTests.cs | 113 ++++++++++++++
.../PowerFx/Functions/UserMessageTests.cs | 5 +-
workflow-samples/DeepResearch.yaml | 12 +-
workflow-samples/MathChat.yaml | 2 +-
workflow-samples/README.md | 2 +-
34 files changed, 749 insertions(+), 76 deletions(-)
create mode 100644 dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj
create mode 100644 dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.yaml
create mode 100644 dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs
create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/PowerFx/Functions/AgentMessage.cs
create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/PowerFx/Functions/MessageFunction.cs
create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/PowerFx/Functions/MessageText.cs
create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs
create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Testcases/InputArguments.json
create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InputArguments.yaml
create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/Functions/AgentMessageTests.cs
create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/Functions/MessageTextTests.cs
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 2c1e84208c..8f0f34093b 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -116,6 +116,7 @@
+
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/Program.cs
index 5ecda10049..2117bf8b07 100644
--- a/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/Program.cs
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/Program.cs
@@ -10,7 +10,7 @@ namespace Demo.Workflows.Declarative.ConfirmInput;
/// and confirm it matches the original input.
///
///
-/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
+/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
///
internal sealed class Program
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs
index 0d1368d7ac..b3a7be9171 100644
--- a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs
@@ -14,7 +14,7 @@ namespace Demo.Workflows.Declarative.DeepResearch;
/// using the Magentic orchestration pattern developed by AutoGen.
///
///
-/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
+/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
///
internal sealed class Program
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs
index 170f9b08a0..2549312b95 100644
--- a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs
@@ -15,7 +15,7 @@ namespace Demo.Workflows.Declarative.FunctionTools;
/// with function tools assigned. Exits the loop when the user enters "exit".
///
///
-/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
+/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
///
internal sealed class Program
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj b/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj
new file mode 100644
index 0000000000..af04305c91
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj
@@ -0,0 +1,39 @@
+
+
+
+ Exe
+ net9.0
+ net9.0
+ $(ProjectsDebugTargetFrameworks)
+ enable
+ enable
+
+
+
+ true
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.yaml b/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.yaml
new file mode 100644
index 0000000000..3f602d0e7b
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.yaml
@@ -0,0 +1,97 @@
+#
+# This workflow demonstrates providing input arguments to an agent.
+#
+# Example input:
+# I'd like to go on vacation.
+#
+kind: Workflow
+trigger:
+
+ kind: OnConversationStart
+ id: workflow_demo
+ actions:
+
+ # Capture the original user message for input to the location-aware agent
+ - kind: SetVariable
+ id: set_count_increment
+ variable: Local.InputMessage
+ value: =System.LastMessage
+
+ # Invoke the triage agent to determine location requirements
+ - kind: InvokeAzureAgent
+ id: solicit_input
+ conversationId: =System.ConversationId
+ agent:
+ name: LocationTriageAgent
+ input:
+ messages: =Local.ActionMessage
+ output:
+ messages: Local.TriageResponse
+
+ # Request input from the user based on the triage response
+ - kind: RequestExternalInput
+ id: request_requirements
+ variable: Local.NextInput
+
+ # Capture the most recent interaction for evaluation
+ - kind: SetTextVariable
+ id: set_status_message
+ variable: Local.LocationStatusInput
+ value: |-
+ AGENT - {MessageText(Local.TriageResponse)}
+
+ USER - {MessageText(Local.NextInput)}
+
+ # Evaluate the status of the location triage
+ - kind: InvokeAzureAgent
+ id: evaluate_location
+ agent:
+ name: LocationCaptureAgent
+ input:
+ messages: =UserMessage(Local.LocationStatusInput)
+ output:
+ responseObject: Local.LocationResponse
+
+ # Determine if the location information is complete
+ - kind: ConditionGroup
+ id: check_completion
+ conditions:
+
+ - condition: |-
+ =Local.LocationResponse.is_location_defined = false Or
+ Local.LocationResponse.is_location_confirmed = false
+ id: check_done
+ actions:
+
+ # Capture the action message for input to the triage agent
+ - kind: SetVariable
+ id: set_next_message
+ variable: Local.ActionMessage
+ value: =AgentMessage(Local.LocationResponse.action)
+
+ - kind: GotoAction
+ id: goto_solicit_input
+ actionId: solicit_input
+
+ elseActions:
+
+ # Create a new conversation so the prior context does not interfere
+ - kind: CreateConversation
+ id: conversation_location
+ conversationId: Local.LocationConversationId
+
+ # Invoke the location-aware agent with the location argument
+ # and loop until the user types "EXIT"
+ - kind: InvokeAzureAgent
+ id: location_response
+ conversationId: =Local.LocationConversationId
+ agent:
+ name: LocationAwareAgent
+ input:
+ messages: =Local.InputMessage
+ arguments:
+ location: =Local.LocationResponse.place
+ externalLoop:
+ when: =Upper(System.LastMessage.Text) <> "EXIT"
+ output:
+ autoSend: true
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs
new file mode 100644
index 0000000000..ff12ccd874
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.Agents;
+using Azure.Identity;
+using Microsoft.Extensions.Configuration;
+using OpenAI.Responses;
+using Shared.Foundry;
+using Shared.Workflows;
+
+namespace Demo.Workflows.Declarative.InputArguments;
+
+///
+/// Demonstrate a workflow that consumes input arguments to dynamically enhance the agent
+/// instructions. Exits the loop when the user enters "exit".
+///
+///
+/// See the README.md file in the parent folder (../README.md) for detailed
+/// information the configuration required to run this sample.
+///
+internal sealed class Program
+{
+ public static async Task Main(string[] args)
+ {
+ // Initialize configuration
+ IConfiguration configuration = Application.InitializeConfig();
+ Uri foundryEndpoint = new(configuration.GetValue(Application.Settings.FoundryEndpoint));
+
+ // Ensure sample agents exist in Foundry.
+ await CreateAgentAsync(foundryEndpoint, configuration);
+
+ // Get input from command line or console
+ string workflowInput = Application.GetInput(args);
+
+ // Create the workflow factory. This class demonstrates how to initialize a
+ // declarative workflow from a YAML file. Once the workflow is created, it
+ // can be executed just like any regular workflow.
+ WorkflowFactory workflowFactory = new("InputArguments.yaml", foundryEndpoint);
+
+ // Execute the workflow: The WorkflowRunner demonstrates how to execute
+ // a workflow, handle the workflow events, and providing external input.
+ // This also includes the ability to checkpoint workflow state and how to
+ // resume execution.
+ WorkflowRunner runner = new();
+ await runner.ExecuteAsync(workflowFactory.CreateWorkflow, workflowInput);
+ }
+
+ private static async Task CreateAgentAsync(Uri foundryEndpoint, IConfiguration configuration)
+ {
+ AgentClient agentsClient = new(foundryEndpoint, new AzureCliCredential());
+
+ await agentsClient.CreateAgentAsync(
+ agentName: "LocationTriageAgent",
+ agentDefinition: DefineLocationTriageAgent(configuration),
+ agentDescription: "Chats with the user to solicit a location of interest.");
+
+ await agentsClient.CreateAgentAsync(
+ agentName: "LocationCaptureAgent",
+ agentDefinition: DefineLocationCaptureAgent(configuration),
+ agentDescription: "Evaluate the status of soliciting the location.");
+
+ await agentsClient.CreateAgentAsync(
+ agentName: "LocationAwareAgent",
+ agentDefinition: DefineLocationAwareAgent(configuration),
+ agentDescription: "Chats with the user with location awareness.");
+ }
+
+ private static PromptAgentDefinition DefineLocationTriageAgent(IConfiguration configuration) =>
+ new(configuration.GetValue(Application.Settings.FoundryModelMini))
+ {
+ Instructions =
+ """
+ Your only job is to solicit a location from the user.
+
+ Always repeat back the location when addressing the user, except when it is not known.
+ """
+ };
+
+ private static PromptAgentDefinition DefineLocationCaptureAgent(IConfiguration configuration) =>
+ new(configuration.GetValue(Application.Settings.FoundryModelMini))
+ {
+ Instructions =
+ """
+ Request a location from the user. This location could be their own location
+ or perhaps a location they are interested in.
+
+ City level precision is sufficient.
+
+ If extrapolating region and country, confirm you have it right.
+ """,
+ TextOptions =
+ new ResponseTextOptions
+ {
+ TextFormat =
+ ResponseTextFormat.CreateJsonSchemaFormat(
+ "TaskEvaluation",
+ BinaryData.FromString(
+ """
+ {
+ "type": "object",
+ "properties": {
+ "place": {
+ "type": "string",
+ "description": "Captures only your understanding of the location specified by the user without explanation, or 'unknown' if not yet defined."
+ },
+ "action": {
+ "type": "string",
+ "description": "The instruction for the next action to take regarding the need for additional detail or confirmation."
+ },
+ "is_location_defined": {
+ "type": "boolean",
+ "description": "True if the user location is understood."
+ },
+ "is_location_confirmed": {
+ "type": "boolean",
+ "description": "True if the user location is confirmed. An unambiguous location may be implicitly confirmed without explicit user confirmation."
+ }
+ },
+ "required": ["place", "action", "is_location_defined", "is_location_confirmed"],
+ "additionalProperties": false
+ }
+ """),
+ jsonSchemaFormatDescription: null,
+ jsonSchemaIsStrict: true),
+ }
+ };
+
+ private static PromptAgentDefinition DefineLocationAwareAgent(IConfiguration configuration) =>
+ new(configuration.GetValue(Application.Settings.FoundryModelMini))
+ {
+ // Parameterized instructions reference the "location" input argument.
+ Instructions =
+ """
+ Talk to the user about their request.
+ Their request is related to a specific location: {{location}}.
+ """,
+ StructuredInputs =
+ {
+ ["location"] =
+ new StructuredInputDefinition
+ {
+ IsRequired = false,
+ DefaultValue = BinaryData.FromString(@"""unknown"""),
+ Description = "The user's location",
+ }
+ }
+ };
+}
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs
index 20987c6bfe..edeb82419b 100644
--- a/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs
@@ -13,7 +13,7 @@ namespace Demo.Workflows.Declarative.Marketing;
/// sequentially engaging in a task.
///
///
-/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
+/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
///
internal sealed class Program
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs
index d2299add5f..bbe1c526d5 100644
--- a/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs
@@ -13,7 +13,7 @@ namespace Demo.Workflows.Declarative.StudentTeacher;
/// in an iterative conversation.
///
///
-/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
+/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
///
internal sealed class Program
diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs
index 01be1abfd5..736edadf8d 100644
--- a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs
+++ b/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs
@@ -14,7 +14,7 @@ namespace Demo.Workflows.Declarative.ToolApproval;
/// has an MCP tool that requires approval. Exits the loop when the user enters "exit".
///
///
-/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
+/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
///
internal sealed class Program
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/AzureAgentProvider.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/AzureAgentProvider.cs
index 79906fb3f5..de50e16922 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/AzureAgentProvider.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/AzureAgentProvider.cs
@@ -7,10 +7,12 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
+using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Agents;
using Azure.Core;
+using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
@@ -32,6 +34,11 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
private AgentClient? _agentClient;
private ConversationClient? _conversationClient;
+ ///
+ /// Optional options used when creating the .
+ ///
+ public AgentClientOptions? ClientOptions { get; init; }
+
///
public override async Task CreateConversationAsync(CancellationToken cancellationToken = default)
{
@@ -78,6 +85,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
string? agentVersion,
string? conversationId,
IEnumerable? messages,
+ IDictionary? inputArguments,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
AgentVersion agentVersionResult = await this.QueryAgentAsync(agentId, agentVersion, cancellationToken).ConfigureAwait(false);
@@ -90,6 +98,14 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
AllowMultipleToolCalls = this.AllowMultipleToolCalls,
};
+ if (inputArguments is not null)
+ {
+ JsonNode jsonNode = inputArguments.ToFormula().ToJson();
+ ResponseCreationOptions responseCreationOptions = new();
+ responseCreationOptions.SetStructuredInputs(BinaryData.FromString(jsonNode.ToJsonString()));
+ chatOptions.RawRepresentationFactory = (_) => responseCreationOptions;
+ }
+
ChatClientAgentRunOptions runOptions = new(chatOptions);
IAsyncEnumerable agentResponse =
@@ -206,7 +222,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
{
if (this._agentClient is null)
{
- AgentClientOptions clientOptions = new();
+ AgentClientOptions clientOptions = this.ClientOptions ?? new();
if (httpClient is not null)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/AgentProviderExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/AgentProviderExtensions.cs
index 170d5a52a9..037665e8b8 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/AgentProviderExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/AgentProviderExtensions.cs
@@ -17,9 +17,10 @@ internal static class AgentProviderExtensions
string? conversationId,
bool autoSend,
IEnumerable? inputMessages = null,
+ IDictionary? inputArguments = null,
CancellationToken cancellationToken = default)
{
- IAsyncEnumerable agentUpdates = agentProvider.InvokeAgentAsync(agentName, null, conversationId, inputMessages, cancellationToken);
+ IAsyncEnumerable agentUpdates = agentProvider.InvokeAgentAsync(agentName, null, conversationId, inputMessages, inputArguments, cancellationToken);
// Enable "autoSend" behavior if this is the workflow conversation.
bool isWorkflowConversation = context.IsWorkflowConversation(conversationId, out string? workflowConversationId);
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/FormulaValueExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/FormulaValueExtensions.cs
index 108dca7682..3e397f3e87 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/FormulaValueExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/FormulaValueExtensions.cs
@@ -163,6 +163,24 @@ internal static class FormulaValueExtensions
}
}
}
+
+ public static JsonNode ToJson(this FormulaValue value) =>
+ value switch
+ {
+ BooleanValue booleanValue => JsonValue.Create(booleanValue.Value),
+ DecimalValue decimalValue => JsonValue.Create(decimalValue.Value),
+ NumberValue numberValue => JsonValue.Create(numberValue.Value),
+ DateValue dateValue => JsonValue.Create(dateValue.GetConvertedValue(TimeZoneInfo.Utc)),
+ DateTimeValue datetimeValue => JsonValue.Create(datetimeValue.GetConvertedValue(TimeZoneInfo.Utc)),
+ TimeValue timeValue => JsonValue.Create($"{timeValue.Value}"),
+ StringValue stringValue => JsonValue.Create(stringValue.Value),
+ GuidValue guidValue => JsonValue.Create(guidValue.Value),
+ RecordValue recordValue => recordValue.ToJson(),
+ TableValue tableValue => tableValue.ToJson(),
+ BlankValue => JsonValue.Create(string.Empty),
+ _ => $"[{value.GetType().Name}]",
+ };
+
public static RecordValue ToRecord(this Dictionary value) =>
FormulaValue.NewRecordFromFields(
value.Select(
@@ -256,23 +274,6 @@ internal static class FormulaValueExtensions
private static KeyValuePair GetKeyValuePair(this NamedValue value) => new(value.Name, value.Value.ToDataValue());
- private static JsonNode ToJson(this FormulaValue value) =>
- value switch
- {
- BooleanValue booleanValue => JsonValue.Create(booleanValue.Value),
- DecimalValue decimalValue => JsonValue.Create(decimalValue.Value),
- NumberValue numberValue => JsonValue.Create(numberValue.Value),
- DateValue dateValue => JsonValue.Create(dateValue.GetConvertedValue(TimeZoneInfo.Utc)),
- DateTimeValue datetimeValue => JsonValue.Create(datetimeValue.GetConvertedValue(TimeZoneInfo.Utc)),
- TimeValue timeValue => JsonValue.Create($"{timeValue.Value}"),
- StringValue stringValue => JsonValue.Create(stringValue.Value),
- GuidValue guidValue => JsonValue.Create(guidValue.Value),
- RecordValue recordValue => recordValue.ToJson(),
- TableValue tableValue => tableValue.ToJson(),
- BlankValue => JsonValue.Create(string.Empty),
- _ => $"[{value.GetType().Name}]",
- };
-
private static JsonArray ToJson(this TableValue value)
{
return new([.. GetJsonElements()]);
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Kit/AgentExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Kit/AgentExecutor.cs
index 9e6a6884b6..45a5b47bd8 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Kit/AgentExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Kit/AgentExecutor.cs
@@ -33,5 +33,5 @@ public abstract class AgentExecutor(string id, FormulaSession session, WorkflowA
bool autoSend,
IEnumerable? inputMessages = null,
CancellationToken cancellationToken = default)
- => agentProvider.InvokeAgentAsync(this.Id, context, agentName, conversationId, autoSend, inputMessages, cancellationToken);
+ => agentProvider.InvokeAgentAsync(this.Id, context, agentName, conversationId, autoSend, inputMessages, inputArguments: null, cancellationToken);
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs
index cafece9a57..ed069a9b78 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs
@@ -60,8 +60,8 @@ internal sealed class InvokeAzureAgentExecutor(InvokeAzureAgent model, WorkflowA
string? conversationId = this.GetConversationId();
string agentName = this.GetAgentName();
bool autoSend = this.GetAutoSendValue();
-
- AgentRunResponse agentResponse = await agentProvider.InvokeAgentAsync(this.Id, context, agentName, conversationId, autoSend, messages, cancellationToken).ConfigureAwait(false);
+ Dictionary? inputParameters = this.GetStructuredInputs();
+ AgentRunResponse agentResponse = await agentProvider.InvokeAgentAsync(this.Id, context, agentName, conversationId, autoSend, messages, inputParameters, cancellationToken).ConfigureAwait(false);
ChatMessage[] actionableMessages = FilterActionableContent(agentResponse).ToArray();
if (actionableMessages.Length > 0)
@@ -107,6 +107,23 @@ internal sealed class InvokeAzureAgentExecutor(InvokeAzureAgent model, WorkflowA
await context.SendResultMessageAsync(this.Id, result: null, cancellationToken).ConfigureAwait(false);
}
+ private Dictionary? GetStructuredInputs()
+ {
+ Dictionary? inputs = null;
+
+ if (this.AgentInput?.Arguments is not null)
+ {
+ inputs = [];
+
+ foreach (KeyValuePair argument in this.AgentInput.Arguments)
+ {
+ inputs[argument.Key] = this.Evaluator.GetValue(argument.Value).Value.ToObject();
+ }
+ }
+
+ return inputs;
+ }
+
private IEnumerable? GetInputMessages()
{
DataValue? userInput = null;
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/SetVariableExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/SetVariableExecutor.cs
index 449e982982..81ed6e30d3 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/SetVariableExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/SetVariableExecutor.cs
@@ -8,7 +8,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
using Microsoft.Bot.ObjectModel;
using Microsoft.Bot.ObjectModel.Abstractions;
using Microsoft.PowerFx.Types;
-using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.ObjectModel;
@@ -17,17 +16,15 @@ internal sealed class SetVariableExecutor(SetVariable model, WorkflowFormulaStat
{
protected override async ValueTask