Working demos

This commit is contained in:
Shyju Krishnankutty
2026-01-22 22:09:33 -08:00
Unverified
parent 04fdf25019
commit 12256b59aa
10 changed files with 39 additions and 107 deletions
@@ -8,3 +8,6 @@ dotnet_diagnostic.DURABLE0003.severity = none
dotnet_diagnostic.DURABLE0004.severity = none
dotnet_diagnostic.DURABLE0005.severity = none
dotnet_diagnostic.DURABLE0006.severity = none
# CA1812: Internal classes are instantiated via dependency injection or reflection in samples
dotnet_diagnostic.CA1812.severity = none
@@ -23,9 +23,8 @@ var orderParserExecutor = orderParserFunc.BindAsExecutor("ParseOrderId");
OrderLookup orderLookupExecutor = new();
OrderEnrich orderEnricherExeecutor = new();
PaymentProcessor paymentProcessorExecutor = new();
OrderCancel orderArchiverExecutor = new();
Workflow processOrder = new WorkflowBuilder(orderParserExecutor)
Workflow fulfillOrder = new WorkflowBuilder(orderParserExecutor)
.WithName("FulfillOrder")
.WithDescription("Looks up an order by ID and run payment processing")
.AddEdge(orderParserExecutor, orderLookupExecutor)
@@ -33,20 +32,17 @@ Workflow processOrder = new WorkflowBuilder(orderParserExecutor)
.AddEdge(orderEnricherExeecutor, paymentProcessorExecutor)
.Build();
Workflow cancelOrder = new WorkflowBuilder(orderParserExecutor)
.WithName("CancelOrder")
.WithDescription("Cancel an order")
.AddEdge(orderParserExecutor, orderLookupExecutor)
.AddEdge(orderLookupExecutor, orderArchiverExecutor)
.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(processOrder);
options.Workflows.AddWorkflow(cancelOrder, true);
})
.ConfigureDurableOptions(options => options.Workflows.AddWorkflow(fulfillOrder))
.Build();
host.Run();
@@ -30,21 +30,19 @@ AIAgent chemist = client.GetChatClient(deploymentName).CreateAIAgent("You are an
var startExecutor = new ConcurrentStartExecutor();
var aggregationExecutor = new ResultAggregationExecutor();
// Build the workflow by adding executors and connecting them
var workflow = new WorkflowBuilder(startExecutor)
.WithName("FanOutWorkflow")
.WithName("ExpertReview")
.AddFanOutEdge(startExecutor, [physicist, chemist])
.AddFanInEdge([physicist, chemist], aggregationExecutor)
.Build();
// Configure the function app to host AI agents and workflows in a unified way.
// This will automatically generate HTTP API endpoints for agents and workflows.
FunctionsApplication.CreateBuilder(args)
var host = FunctionsApplication.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableOptions(options =>
{
// Configure workflows
options.Workflows.AddWorkflow(workflow, enableMcpToolTrigger: true);
options.Workflows.AddWorkflow(workflow);
})
.Build().Run();
.Build();
host.Run();
@@ -2,7 +2,7 @@
@authority=http://localhost:7071
### Start the workflow
POST {{authority}}/api/workflows/FanOutWorkflow/run
POST {{authority}}/api/workflows/ExpertReview/run
Content-Type: text/plain
What is temperature?
@@ -1,64 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Logging;
namespace SingleAgent;
public static class MyOrchFunction
{
[Function(nameof(MyOrchFunction))]
public static async Task<List<string>> RunOrchestratorAsync(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
ILogger logger = context.CreateReplaySafeLogger(nameof(MyOrchFunction));
logger.LogInformation("Saying hello.");
var outputs = new List<string>();
// Replace name and input with values relevant for your Durable Functions Activity
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
[Function(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name,
TaskActivityContext taskActivityContext,
FunctionContext functionContext)
{
ILogger logger = functionContext.GetLogger("SayHello");
if (logger.IsEnabled(LogLevel.Information))
{
var instanceId = (string)functionContext.BindingContext.BindingData["instanceId"]!;
logger.LogInformation("Saying hello to {Name} for instanceId:{InstanceId}", name, instanceId);
}
return $"Hello {name}!";
}
[Function("MyOrchFunction_HttpStart")]
public static async Task<HttpResponseData> HttpStartAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("MyOrchFunction_HttpStart");
// Function input comes from the request content.
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(MyOrchFunction));
if (logger.IsEnabled(LogLevel.Information))
{
logger.LogInformation("Started orchestration with ID = '{InstanceId}'.", instanceId);
}
// Returns an HTTP 202 response with an instance management payload.
// See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}
}
@@ -19,11 +19,5 @@ var workflow = builder.WithName("ProcessOrder").Build();
FunctionsApplication.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableOptions(options =>
{
options.Workflows.AddWorkflow(workflow);
// Optional - Configure AI agents
// options.Agents.AddAIAgent(agent);
})
.ConfigureDurableOptions(options => options.Workflows.AddWorkflow(workflow))
.Build().Run();
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
@@ -15,7 +15,10 @@
</ItemGroup>
<ItemGroup>
<None Include="local.settings.json" />
<None Include="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<!-- Azure Functions packages -->
@@ -12,13 +12,15 @@ PaymentProcesserExecutor paymentProcessor = new();
NotifyFraudExecutor notifyFraud = new();
WorkflowBuilder builder = new(orderParser);
builder.AddEdge(orderParser, orderEnrich);
builder.AddEdge(orderEnrich, notifyFraud, condition: OrderRouteConditions.WhenBlocked());
builder.AddEdge(orderEnrich, paymentProcessor, condition: OrderRouteConditions.WhenNotBlocked());
builder
.AddEdge(orderParser, orderEnrich)
.AddEdge(orderEnrich, notifyFraud, condition: OrderRouteConditions.WhenBlocked())
.AddEdge(orderEnrich, paymentProcessor, condition: OrderRouteConditions.WhenNotBlocked());
var workflow = builder.WithName("ProcessOrder").Build();
var workflow = builder.WithName("AuditOrder").Build();
FunctionsApplication.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableOptions(options => options.Workflows.AddWorkflow(workflow))
.Build().Run();
.Build()
.Run();
@@ -67,7 +67,7 @@ public sealed class DurableWorkflowOptions
/// Adds a collection of workflows to the current instance.
/// </summary>
/// <param name="workflows">The collection of <see cref="Workflow"/> objects to add. Cannot be <see langword="null"/>.</param>
public void AddWorkflow(IEnumerable<Workflow> workflows)
public void AddWorkflows(IEnumerable<Workflow> workflows)
{
ArgumentNullException.ThrowIfNull(workflows);
@@ -103,37 +103,37 @@ internal static partial class Logs
[LoggerMessage(
EventId = 12,
Level = LogLevel.Information,
Level = LogLevel.Debug,
Message = "Attempting to run workflow: {WorkflowName}")]
public static partial void LogAttemptingToRunWorkflow(this ILogger logger, string workflowName);
[LoggerMessage(
EventId = 13,
Level = LogLevel.Information,
Level = LogLevel.Debug,
Message = "Running workflow: {WorkflowName}")]
public static partial void LogRunningWorkflow(this ILogger logger, string? workflowName);
[LoggerMessage(
EventId = 14,
Level = LogLevel.Information,
Level = LogLevel.Debug,
Message = "Attempting to execute activity in workflow '{WorkflowName}' for executor '{ExecutorName}'")]
public static partial void LogAttemptingToExecuteActivity(this ILogger logger, string workflowName, string executorName);
[LoggerMessage(
EventId = 15,
Level = LogLevel.Information,
Level = LogLevel.Debug,
Message = "Executing activity for executor '{ExecutorId}' of type '{ExecutorType}'")]
public static partial void LogExecutingActivity(this ILogger logger, string executorId, string executorType);
[LoggerMessage(
EventId = 16,
Level = LogLevel.Information,
Level = LogLevel.Debug,
Message = "Activity executed for executor '{ExecutorId}' with result: {Result}")]
public static partial void LogActivityExecuted(this ILogger logger, string executorId, string result);
[LoggerMessage(
EventId = 17,
Level = LogLevel.Information,
Level = LogLevel.Debug,
Message = "Executor '{ExecutorId}' skipped due to edge condition evaluation")]
public static partial void LogExecutorSkipped(this ILogger logger, string executorId);
}