[.NET] Fix ConfigureDurableAgents and ConfigureDurableWorkflows not composing - #67
[.NET] Fix ConfigureDurableAgents and ConfigureDurableWorkflows not composing#67Shyju Krishnankutty (kshyju) wants to merge 4 commits into
Conversation
Covers #27. Calling ConfigureDurableAgents and ConfigureDurableWorkflows together does not compose today: - Azure Functions, agents configured first: the agent path registers the built-in execution middleware with a predicate covering only agent entry points, and the guard in EnsureMiddlewareRegistered then short-circuits the workflow path, so workflow entry points are never routed to BuiltInFunctionExecutor. - Azure Functions, workflows configured first: the agent path registers the middleware again, duplicating it in the invocation pipeline. - Core hosting: EnsureDurableServicesRegistered returns early on its marker, so worker and client builders passed to a later Configure* call are dropped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 889a6ebe-a00c-48f4-b5e9-ec0872f872db
`ConfigureDurableAgents` and `ConfigureDurableWorkflows` did not compose when both were called on the same application, forcing users onto `ConfigureDurableOptions`. Azure Functions layer: - `ConfigureDurableAgents` registered its own `UseWhen` middleware filter whose predicate only covered the three agent entry points, then registered `BuiltInFunctionExecutor`. A later `ConfigureDurableWorkflows` saw that executor registration and early-returned from `EnsureMiddlewareRegistered`, so workflow functions got metadata but no `IFunctionExecutor` and never ran. - In the reverse order the agent path registered the middleware and executor a second time, duplicating them in the invocation pipeline. All three public entry points now funnel through a single `ConfigureDurableCore` helper, so the full middleware predicate is registered exactly once no matter which combination of methods is called or in which order. Default agent options are applied only to agents added by that call, so agents auto-registered by a workflow no longer gain an HTTP trigger just because `ConfigureDurableAgents` happened to run afterwards. Core service collection layer: - `EnsureDurableServicesRegistered` guarded the whole registration block with a single marker, so a `workerBuilder` or `clientBuilder` passed to any call after the first was silently dropped and no worker or client was registered. Worker and client registration are now tracked with their own markers and are independent of the shared services guard, so each is registered at most once while still honoring a builder supplied by a later call. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 889a6ebe-a00c-48f4-b5e9-ec0872f872db
The Azure Functions "workflow and agents" sample steered users to `ConfigureDurableOptions` as the way to register both agents and workflows, because separate `ConfigureDurableAgents` and `ConfigureDurableWorkflows` calls did not compose. Now that they do, the sample uses the split calls and mentions `ConfigureDurableOptions` as an equivalent alternative. Also corrects the console sample comments that described the three methods as mutually exclusive, and documents composition in the durable agents README. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 889a6ebe-a00c-48f4-b5e9-ec0872f872db
There was a problem hiding this comment.
Pull request overview
This PR fixes configuration composition so ConfigureDurableAgents(...) and ConfigureDurableWorkflows(...) can be used together (in any order) without forcing callers onto ConfigureDurableOptions(...), aligning Azure Functions and core IServiceCollection behavior with the intended additive options model.
Changes:
- Unifies Azure Functions configuration paths via a shared core registration method so built-in middleware/executor routing works for both agent and workflow entry points regardless of call order.
- Fixes core DI registration so
workerBuilder/clientBuilderprovided in a laterConfigure*call are no longer silently ignored. - Adds regression tests plus sample/docs updates and changelog entries documenting the supported, composable configuration experience.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/FunctionsDurableConfigurationCompositionTests.cs | New regression tests validating built-in entry points route to BuiltInFunctionExecutor and middleware/executor are registered once. |
| dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/DurableConfigurationCompositionTests.cs | New regression tests validating additive agent/workflow registration and honoring worker/client builders across multiple calls. |
| dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/FunctionsApplicationBuilderExtensions.cs | Refactors durable configuration into a shared core path and expands middleware predicate coverage to include workflow entry points. |
| dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/CHANGELOG.md | Documents the Azure Functions composition fix. |
| dotnet/src/Microsoft.Agents.AI.DurableTask/ServiceCollectionExtensions.cs | Splits “core shared services” registration from worker/client registration so later worker/client builders are honored without duplicating shared services. |
| dotnet/src/Microsoft.Agents.AI.DurableTask/DurableServicesMarker.cs | Introduces dedicated markers for worker/client configuration to support composition across calls. |
| dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md | Documents the core IServiceCollection builder-composition fix. |
| dotnet/samples/DurableWorkflows/ConsoleApps/04_WorkflowAndAgents/Program.cs | Updates sample narrative to reflect that all three configuration methods compose additively. |
| dotnet/samples/DurableWorkflows/AzureFunctions/05_WorkflowAndAgents/README.md | Updates sample docs to show separate, composable ConfigureDurableAgents + ConfigureDurableWorkflows usage (and optional ConfigureDurableOptions). |
| dotnet/samples/DurableWorkflows/AzureFunctions/05_WorkflowAndAgents/Program.cs | Switches sample implementation to separate composable configuration calls. |
| docs/features/durable-agents/README.md | Updates feature documentation to explicitly describe hosting workflows alongside agents using composable calls. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Agent names are case-insensitive in DurableAgentsOptions and in the Functions agent options registry. Match that comparer when snapshotting agent keys so the set semantics cannot drift. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 889a6ebe-a00c-48f4-b5e9-ec0872f872db
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (2)
dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/FunctionsDurableConfigurationCompositionTests.cs:51
- The built-in entry point routing tests don't cover all entry points that
EnsureMiddlewareRegisteredroutes toBuiltInFunctionExecutionMiddleware(notably the agent MCP tool trigger, workflow MCP tool trigger, and workflow HTTP response entry point). Adding these cases would better protect against regressions in the shared predicate and ensure composition works for all built-in triggers.
public static TheoryData<string> BuiltInEntryPointNames() =>
[
"AgentHttp",
"AgentEntity",
"WorkflowOrchestrationHttp",
"WorkflowOrchestration",
"WorkflowActivity",
"WorkflowStatusHttp",
];
private static string ResolveEntryPoint(string name) => name switch
{
"AgentHttp" => BuiltInFunctions.RunAgentHttpFunctionEntryPoint,
"AgentEntity" => BuiltInFunctions.RunAgentEntityFunctionEntryPoint,
"WorkflowOrchestrationHttp" => BuiltInFunctions.RunWorkflowOrchestrationHttpFunctionEntryPoint,
"WorkflowOrchestration" => BuiltInFunctions.RunWorkflowOrchestrationFunctionEntryPoint,
"WorkflowActivity" => BuiltInFunctions.InvokeWorkflowActivityFunctionEntryPoint,
"WorkflowStatusHttp" => BuiltInFunctions.GetWorkflowStatusHttpFunctionEntryPoint,
_ => throw new ArgumentOutOfRangeException(nameof(name), name, "Unknown built-in entry point."),
};
dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/FunctionsApplicationBuilderExtensions.cs:70
- The parameter indentation on
ConfigureDurableWorkflowsis off by one space compared to the rest of the file, which makes the signature formatting inconsistent and harder to scan.
public static FunctionsApplicationBuilder ConfigureDurableWorkflows(
this FunctionsApplicationBuilder builder,
Action<DurableWorkflowOptions> configure)
Fixes #27
ConfigureDurableAgents(...)andConfigureDurableWorkflows(...)could not be used together, forcing callers ontoConfigureDurableOptions(...).Implementation: all three Functions extension methods now route through a shared
ConfigureDurableCore, so the execution middleware is registered once with a predicate covering both agent and workflow entry points. At the core layer, separate markers track worker and client registration so aworkerBuilder/clientBuildersupplied to a later call is no longer dropped. Both call orders now produce identical registrations.Tests are committed separately (
af0f0777b) and fail without the fix.