From 52da946efd6ea9b55aeb7d36140dc8aba1a4c97a Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Fri, 23 Jan 2026 10:16:31 -0800 Subject: [PATCH] minor tweaks --- .../AzureFunctions/09_Workflow/Program.cs | 10 +-- ...erIdParserExecutor.cs => OrderIdParser.cs} | 8 +-- .../12_ConditionalEdges/Program.cs | 8 +-- .../Workflows/Visualization/Program.cs | 61 ++++++++++++++++--- .../Visualization/Visualization.csproj | 1 + 5 files changed, 63 insertions(+), 25 deletions(-) rename dotnet/samples/AzureFunctions/12_ConditionalEdges/{OrderIdParserExecutor.cs => OrderIdParser.cs} (88%) diff --git a/dotnet/samples/AzureFunctions/09_Workflow/Program.cs b/dotnet/samples/AzureFunctions/09_Workflow/Program.cs index fa21e2bcfa..38bc6b84a9 100644 --- a/dotnet/samples/AzureFunctions/09_Workflow/Program.cs +++ b/dotnet/samples/AzureFunctions/09_Workflow/Program.cs @@ -32,17 +32,9 @@ Workflow fulfillOrder = new WorkflowBuilder(orderParserExecutor) .AddEdge(orderEnricherExeecutor, paymentProcessorExecutor) .Build(); -//OrderCancel orderArchiverExecutor = new(); -//Workflow cancelOrder = new WorkflowBuilder(orderParserExecutor) -// .WithName("CancelOrder") -// .WithDescription("Cancel an order") -// .AddEdge(orderParserExecutor, orderLookupExecutor) -// .AddEdge(orderLookupExecutor, orderArchiverExecutor) -// .Build(); - var host = FunctionsApplication.CreateBuilder(args) .ConfigureFunctionsWebApplication() - .ConfigureDurableOptions(options => options.Workflows.AddWorkflow(fulfillOrder)) + .ConfigureDurableOptions(options => options.Workflows.AddWorkflow(fulfillOrder, enableMcpToolTrigger: true)) .Build(); host.Run(); diff --git a/dotnet/samples/AzureFunctions/12_ConditionalEdges/OrderIdParserExecutor.cs b/dotnet/samples/AzureFunctions/12_ConditionalEdges/OrderIdParser.cs similarity index 88% rename from dotnet/samples/AzureFunctions/12_ConditionalEdges/OrderIdParserExecutor.cs rename to dotnet/samples/AzureFunctions/12_ConditionalEdges/OrderIdParser.cs index 31ad643fda..f64e8e5743 100644 --- a/dotnet/samples/AzureFunctions/12_ConditionalEdges/OrderIdParserExecutor.cs +++ b/dotnet/samples/AzureFunctions/12_ConditionalEdges/OrderIdParser.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // This sample demonstrates how to use durable state management in Azure Functions workflows. -// The OrderIdParserExecutor writes a value to shared state, and the FraudValidation reads it back. +// The OrderIdParser writes a value to shared state, and the FraudValidation reads it back. // The state is persisted durably using Durable Entities behind the scenes. using Microsoft.Agents.AI.Workflows; @@ -32,7 +32,7 @@ internal sealed class Order public sealed record Customer(int Id, string Name, bool IsBlocked); -internal sealed class OrderIdParserExecutor() : Executor("OrderIdParserExecutor") +internal sealed class OrderIdParser() : Executor("OrderIdParser") { public override async ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) { @@ -65,7 +65,7 @@ internal sealed class OrderEnrich() : Executor("EnrichOrder") } } -internal sealed class PaymentProcesserExecutor() : Executor("PaymentProcesserExecutor") +internal sealed class PaymentProcesser() : Executor("PaymentProcesser") { public override async ValueTask HandleAsync(Order message, IWorkflowContext context, CancellationToken cancellationToken = default) { @@ -75,7 +75,7 @@ internal sealed class PaymentProcesserExecutor() : Executor("Payme } } -internal sealed class NotifyFraudExecutor() : Executor("NotifyFraud") +internal sealed class NotifyFraud() : Executor("NotifyFraud") { public override async ValueTask HandleAsync(Order message, IWorkflowContext context, CancellationToken cancellationToken = default) { diff --git a/dotnet/samples/AzureFunctions/12_ConditionalEdges/Program.cs b/dotnet/samples/AzureFunctions/12_ConditionalEdges/Program.cs index b0f075e7cc..995a3b6945 100644 --- a/dotnet/samples/AzureFunctions/12_ConditionalEdges/Program.cs +++ b/dotnet/samples/AzureFunctions/12_ConditionalEdges/Program.cs @@ -6,10 +6,10 @@ using Microsoft.Azure.Functions.Worker.Builder; using Microsoft.Extensions.Hosting; using SingleAgent; -OrderIdParserExecutor orderParser = new(); +OrderIdParser orderParser = new(); OrderEnrich orderEnrich = new(); -PaymentProcesserExecutor paymentProcessor = new(); -NotifyFraudExecutor notifyFraud = new(); +PaymentProcesser paymentProcessor = new(); +NotifyFraud notifyFraud = new(); WorkflowBuilder builder = new(orderParser); builder @@ -21,6 +21,6 @@ var workflow = builder.WithName("AuditOrder").Build(); FunctionsApplication.CreateBuilder(args) .ConfigureFunctionsWebApplication() - .ConfigureDurableOptions(options => options.Workflows.AddWorkflow(workflow)) + .ConfigureDurableOptions(options => options.Workflows.AddWorkflow(workflow, enableMcpToolTrigger: true)) .Build() .Run(); diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/Program.cs b/dotnet/samples/GettingStarted/Workflows/Visualization/Program.cs index 5e567e7953..04c7529b34 100644 --- a/dotnet/samples/GettingStarted/Workflows/Visualization/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Visualization/Program.cs @@ -1,6 +1,11 @@ // Copyright (c) Microsoft. All rights reserved. +using Azure; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; using Microsoft.Agents.AI.Workflows; +using OpenAI.Chat; namespace WorkflowVisualizationSample; @@ -20,8 +25,29 @@ internal static class Program /// Command line arguments (not used). private static void Main(string[] args) { - // Step 1: Build the workflow you want to visualize - Workflow workflow = WorkflowMapReduceSample.Program.BuildWorkflow(); + // Get the Azure OpenAI endpoint and deployment name from environment variables. + string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); + string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); + + // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. + string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); + AzureOpenAIClient client = !string.IsNullOrEmpty(azureOpenAiKey) + ? new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(azureOpenAiKey)) + : new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()); + + AIAgent physicist = client.GetChatClient(deploymentName).CreateAIAgent("You are an expert in physics. You answer questions from a physics perspective.", "Physicist"); + AIAgent chemist = client.GetChatClient(deploymentName).CreateAIAgent("You are an expert in chemistry. You answer questions from a chemistry perspective.", "Chemist"); + + var startExecutor = new PrepareQuery(); + var aggregationExecutor = new ResultAggregator(); + + var workflow = new WorkflowBuilder(startExecutor) + .WithName("ExpertReview") + .AddFanOutEdge(startExecutor, [physicist, chemist]) + .AddFanInEdge([physicist, chemist], aggregationExecutor) + .Build(); // Step 2: Generate and display workflow visualization Console.WriteLine("Generating workflow visualization..."); @@ -31,11 +57,30 @@ internal static class Program var mermaid = workflow.ToMermaidString(); Console.WriteLine(mermaid); Console.WriteLine("======="); - - // DOT - Console.WriteLine("DiGraph string: *** Tip: To export DOT as an image, install Graphviz and pipe the DOT output to 'dot -Tsvg', 'dot -Tpng', etc. *** \n======="); - var dotString = workflow.ToDotString(); - Console.WriteLine(dotString); - Console.WriteLine("======="); + } +} + +internal sealed class PrepareQuery() : Executor("PrepareQuery") +{ + public override ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) + { + // do some initial parsing and validation of the message. + // Return a polished version ith additional metadta. + if (!message.StartsWith("Query for the agent:", StringComparison.OrdinalIgnoreCase)) + { + message = "Query for the agent: " + message; + } + + return ValueTask.FromResult(message); + } +} + +internal sealed class ResultAggregator() : Executor("ResultAggregator") +{ + public override ValueTask HandleAsync(string[] message, IWorkflowContext context, CancellationToken cancellationToken = default) + { + // Aggregate all responses from parallel executors. + string aggregatedResponse = string.Join("\n---\n", message); + return ValueTask.FromResult($"Aggregated {message.Length} responses:\n{aggregatedResponse}"); } } diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj b/dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj index 57b1fef0e1..a29c663ee8 100644 --- a/dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj +++ b/dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj @@ -9,6 +9,7 @@ +