Skip to content

feat: prototype opt-in DAFX agent and YAML workflow hosting - #186

Draft
Ahmed Muhsin (ahmedmuhsin) wants to merge 6 commits into
hallvictoria/pluggable-agent-extensionsfrom
ahmedmuhsin/prototype-lazy-dafx
Draft

feat: prototype opt-in DAFX agent and YAML workflow hosting#186
Ahmed Muhsin (ahmedmuhsin) wants to merge 6 commits into
hallvictoria/pluggable-agent-extensionsfrom
ahmedmuhsin/prototype-lazy-dafx

Conversation

@ahmedmuhsin

@ahmedmuhsin Ahmed Muhsin (ahmedmuhsin) commented Sep 10, 2026

Copy link
Copy Markdown

Prototype stacked on #185, replacing its custom context.call_agent() activity path with optional DAFX hosting while keeping ordinary markdown_agent() bindings lightweight.

  • Use discover_agents=True and discover_workflows=True independently for bulk registration. Control generated HTTP routes separately with expose_agent_endpoints and expose_workflow_endpoints (both default to true for discovery).
  • Use durable_markdown_agent() or durable_workflow() for selective registration and injected orchestration proxies. Bindings default to no generated HTTP endpoints unless expose_http_endpoint=True. Discovery and bindings share registrations; enabling exposure is additive.
  • durable_workflow() calls the hosted YAML workflow as a child orchestration and returns its outputs. It does not aggregate child HITL management into the parent.
  • Keep native MAF YAML loading and pass workflow_factory= through unchanged for configured agents, tools, HTTP/MCP handlers, and configuration.

Prototype dependencies remain pinned to microsoft/agent-framework-durable-extension#72 for SDK 2 support.

Verified 192 tests, strict typing, lint, and package builds locally. SDK replay tested; no live Functions host/backend validation.

Usage samples

Complete function-app code from this PR's current head. Each linked directory includes the supporting definitions, helpers, and setup instructions.

Endpoint-only durable agents

Full sample and setup

"""Discover markdown agents and expose their DAFX endpoints without handlers."""

from azurefunctions.agents.extensions.agent_framework import AgentFunctionApp
from local_chat_client import LocalChatClient

app = AgentFunctionApp(client_factory=LocalChatClient, discover_agents=True)

Durable agent binding and multi-turn orchestration

Full sample and setup

"""Inject a durable markdown agent into a two-turn generator orchestrator."""

import azure.durable_functions as df
import azure.functions as func
from agent_framework_durabletask import DurableAgentTask, DurableAIAgent
from azurefunctions.agents.extensions.agent_framework import AgentFunctionApp
from local_chat_client import LocalChatClient

# The binding registers only the selected agent, without an agent HTTP endpoint.
app = AgentFunctionApp(client_factory=LocalChatClient)


@app.orchestration_trigger(context_name="context")
@app.durable_markdown_agent(
    arg_name="agent", agent_name="orders", context_name="context"
)
def orders(
    context: df.DurableOrchestrationContext,
    agent: DurableAIAgent[DurableAgentTask],
):
    session = agent.create_session()
    first = yield agent.run("Assess the order.", session=session)
    second = yield agent.run("Make a fulfillment plan.", session=session)
    return {"assessment": first.text, "plan": second.text}


@app.route(route="orders/orchestrations", methods=["POST"])
@app.durable_client_input(client_name="client")
async def start_orders(
    req: func.HttpRequest, client: df.DurableFunctionsClient
) -> func.HttpResponse:
    # Fixed prompts keep this example focused on durable session continuity.
    instance_id = await client.start_new("orders", client_input={})
    return client.create_check_status_response(req, instance_id)

YAML workflow discovery

Full sample and setup

"""Publish YAML workflows with a private Markdown adapter for agent actions."""

from azurefunctions.agents.extensions.agent_framework import AgentFunctionApp

from local_chat_client import LocalChatClient

app = AgentFunctionApp(
    client_factory=LocalChatClient,
    discover_workflows=True,
)

Durable workflow binding and child orchestration

Full sample and setup

"""Call a private YAML child workflow from a generator orchestrator."""

from typing import NoReturn

import azure.durable_functions as df
import azure.functions as func
from azurefunctions.agents.extensions.agent_framework import AgentFunctionApp


def no_agent_client() -> NoReturn:
    raise AssertionError("This workflow must not create an agent client.")


app = AgentFunctionApp(client_factory=no_agent_client)


@app.orchestration_trigger(context_name="context")
@app.durable_workflow(arg_name="child", workflow_name="Child")
def parent(context: df.DurableOrchestrationContext, child):
    outputs = yield child.run(context.get_input())
    return {"child_outputs": outputs}


@app.route(route="parent/orchestrations", methods=["POST"])
@app.durable_client_input(client_name="client")
async def start_parent(
    req: func.HttpRequest, client: df.DurableFunctionsClient
) -> func.HttpResponse:
    # The child emits fixed text, so this starter does not consume a request body.
    instance_id = await client.start_new("parent", client_input={})
    return client.create_check_status_response(req, instance_id)

Configured MAF workflow factory

Full sample and setup

"""Host a tool-only YAML workflow with a configured public MAF factory."""

from typing import NoReturn

from agent_framework.declarative import WorkflowFactory
from azurefunctions.agents.extensions.agent_framework import AgentFunctionApp


def format_order(order: str, prefix: str) -> str:
    """Format local input without a model or external service."""
    return f"{prefix} order {order}."


def no_agent_client() -> NoReturn:
    raise AssertionError("This tool-only workflow must not create an agent client.")


workflow_factory = WorkflowFactory(
    configuration={"ORDER_PREFIX": "Local"},
    restrict_env_to_configuration=True,
)
workflow_factory.register_tool("format_order", format_order)

app = AgentFunctionApp(
    client_factory=no_agent_client,
    discover_workflows=True,
    workflow_factory=workflow_factory,
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant