Skip to content

feat: add MAF Agent extension package - #185

Open
hallvictoria wants to merge 26 commits into
devfrom
hallvictoria/pluggable-agent-extensions
Open

feat: add MAF Agent extension package#185
hallvictoria wants to merge 26 commits into
devfrom
hallvictoria/pluggable-agent-extensions

Conversation

@hallvictoria

@hallvictoria hallvictoria commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds pluggable Agent support through two new extension packages:

  • azurefunctions-agents-extensions-base: provider-neutral discovery, binding, lifecycle, and Durable contracts.
  • azurefunctions-agents-extensions-agent-framework: Microsoft Agent Framework implementation with typed app and decorator APIs.

Key Changes

  • Resolves raw .agent.md instructions from the app root or agents/.
  • Supports app-level provider defaults.
    • Per-binding provider overrides is not supported in V1.
  • Creates and closes fresh clients and Agents for each invocation.
  • Adds typed AgentFunctionApp and app.markdown_agent APIs for MAF.
  • Adds optional, import-safe Durable support through the [durable] extra.
  • Routes Durable calls through a replay-safe hidden activity using a deterministic schema-v1 payload.

Design Notes

  • The base package is provider infrastructure and is installed transitively by provider packages.
  • Skills and MCP servers are automatically discovered and enabled. Python tools are configured explicitly with tools=.
    • Filtering is not supported in V1.
  • Provider discovery and compiled Durable recipes may be cached, but live clients and Agents are not.
  • Durable Functions remains optional and is not imported during normal package import.

Customer Experience

Non-durable app:

app = AgentFunctionApp(client_factory=create_chat_client)

@app.route(route="orders/{orderId}", methods=["POST"])
@app.markdown_agent(arg_name="order_agent", agent_name="order-fulfillment")
async def process_order(
    req: func.HttpRequest,
    order_agent: Agent,
) -> func.HttpResponse:
    ...

Durable app:

app = AgentFunctionApp(client_factory=create_chat_client)

# Orchestration trigger with `call_agent` - built-in activity trigger with agent binding
@app.orchestration_trigger(context_name="context")
def order_orchestrator(context: Any):
    assessment = yield context.call_agent(...)

# Activity trigger with explicit, customer-added agent binding
@app.activity_trigger(input_name="payload")
@app.markdown_agent(arg_name="agent", agent_name="order-fulfillment")
async def process_order(payload: dict, agent: Agent[Any]) -> dict:
    ...

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A few introduced behaviors and docs need adjustment (typed decorator option defaulting/override semantics, README code blocks containing tab-indented Python, and CI template job condition consistency).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR introduces new “Agents” extension packages to the Azure Functions Python extensions repo, adding a provider-agnostic base contract and a Microsoft Agent Framework provider implementation, along with CI wiring, tests, and end-to-end samples.

Changes:

  • Add azurefunctions-extensions-agents-base (provider contracts, markdown lookup, optional Durable integration) and azurefunctions-extensions-agents-framework (MAF provider + typed AiApp wrappers).
  • Add unit test jobs and build matrix entries for the new packages in the engineering pipeline templates.
  • Add test suites and runnable samples (HTTP + Durable) demonstrating markdown-driven agent invocation.
File summaries
File Description
README.md Adds new extensions to the top-level catalog.
eng/templates/official/jobs/unit-tests.yml Adds CI jobs to run tests for Agents Base and Agents Framework extensions.
eng/templates/official/jobs/build-artifacts.yml Adds artifact build matrix entries for the new Agents packages.
eng/templates/jobs/build.yml Adds build matrix entries for the new Agents packages.
azurefunctions-extensions-agents-framework/tests/test_samples.py Validates sample apps index expected function names.
azurefunctions-extensions-agents-framework/tests/test_provider.py Unit tests for the MAF provider binding behavior and validation.
azurefunctions-extensions-agents-framework/tests/test_imports.py Ensures importing the framework extension doesn’t eagerly import Durable.
azurefunctions-extensions-agents-framework/tests/test_apps.py Tests typed AiApp / DurableAiApp wrapper behavior.
azurefunctions-extensions-agents-framework/samples/README.md Documents the included MAF samples.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/requirements.txt Sample dependency list for the HTTP/queue hybrid scenario.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/order-fulfillment.agent.md Sample raw markdown instructions for the agent.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/order_processing.py Sample validation/minimization logic for order payloads.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/local.settings.template.json Sample local settings template for Foundry configuration.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/host.json Sample host configuration for local execution.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/function_app.py Sample HTTP function using typed AiApp + injected MAF Agent.
azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/README.md Usage instructions for the hybrid function sample.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/requirements.txt Sample dependency list for the Durable orchestration scenario.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/order-fulfillment.agent.md Sample raw markdown instructions for the Durable scenario.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/order_processing.py Sample validation/minimization logic for Durable scenario.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/local.settings.template.json Sample local settings template for Durable scenario.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/host.json Sample host configuration for Durable scenario.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/function_app.py Sample Durable orchestration calling agents via replay-safe activity.
azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/README.md Usage instructions for the hybrid durable sample.
azurefunctions-extensions-agents-framework/README.md Public documentation for installing/using the MAF extension.
azurefunctions-extensions-agents-framework/pyproject.toml Packaging metadata, dependencies, extras, and entry point registration.
azurefunctions-extensions-agents-framework/MANIFEST.in Package include rules for source, tests, and metadata.
azurefunctions-extensions-agents-framework/LICENSE MIT license for the new distribution.
azurefunctions-extensions-agents-framework/azurefunctions/extensions/agents_framework/py.typed Marks the framework package as typed per PEP 561.
azurefunctions-extensions-agents-framework/azurefunctions/extensions/agents_framework/provider.py Implements the agent_framework provider compilation + binding.
azurefunctions-extensions-agents-framework/azurefunctions/extensions/agents_framework/apps.py Adds typed app wrappers and decorator forwarding for provider options.
azurefunctions-extensions-agents-framework/azurefunctions/extensions/agents_framework/init.py Exposes public API surface and version for the framework extension.
azurefunctions-extensions-agents-framework/azurefunctions/extensions/init.py Configures namespace package behavior.
azurefunctions-extensions-agents-framework/azurefunctions/init.py Configures namespace package behavior.
azurefunctions-extensions-agents-base/tests/test_providers.py Tests provider discovery and caching behavior via entry points.
azurefunctions-extensions-agents-base/tests/test_imports.py Ensures base import doesn’t eagerly import Durable.
azurefunctions-extensions-agents-base/tests/test_durable.py Tests deterministic durable payload handling and hidden activity behavior.
azurefunctions-extensions-agents-base/tests/test_bindings.py Tests decorator injection, option merging, file resolution, and lifecycle.
azurefunctions-extensions-agents-base/README.md Documents provider contract, markdown lookup, and durable behavior.
azurefunctions-extensions-agents-base/pyproject.toml Packaging metadata, dependencies, and extras for the base extension.
azurefunctions-extensions-agents-base/MANIFEST.in Package include rules for base extension distribution.
azurefunctions-extensions-agents-base/LICENSE MIT license for the new distribution.
azurefunctions-extensions-agents-base/azurefunctions/extensions/agents_base/py.typed Marks the base package as typed per PEP 561.
azurefunctions-extensions-agents-base/azurefunctions/extensions/agents_base/providers.py Defines provider protocols, metadata, and entry point loading logic.
azurefunctions-extensions-agents-base/azurefunctions/extensions/agents_base/durable.py Durable orchestration support: payload canonicalization + hidden activity.
azurefunctions-extensions-agents-base/azurefunctions/extensions/agents_base/bindings.py Core decorator and instruction resolution/injection implementation.
azurefunctions-extensions-agents-base/azurefunctions/extensions/agents_base/init.py Exposes base extension public API surface and version.
azurefunctions-extensions-agents-base/azurefunctions/extensions/init.py Configures namespace package behavior.
azurefunctions-extensions-agents-base/azurefunctions/init.py Configures namespace package behavior.
Review details

Suppressed comments (4)

azurefunctions-extensions-agents-framework/README.md:41

  • This code sample line uses tab indentation; replace with spaces so the snippet is valid Python when copied.
	response = await agent.run(req.get_body().decode())
	return response.text

azurefunctions-extensions-agents-framework/README.md:61

  • The body line uses a leading tab; replace with spaces so the snippet is valid Python when copied.
	...

azurefunctions-extensions-agents-framework/README.md:59

  • This decorator example uses tab indentation for keyword arguments; replace with spaces so the snippet is valid and consistent Python formatting.
	provider="agent_framework",
	arg_name="agent",
	agent_name="orders",
	client_factory=create_chat_client,
)

eng/templates/official/jobs/unit-tests.yml:56

  • All other extension test jobs in this template set condition: always(), but the newly added Agents Framework job does not. Adding it keeps behavior consistent (and avoids surprises if dependencies are introduced later).
  • Files reviewed: 48/48 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread azurefunctions-extensions-agents-framework/README.md
Comment thread eng/templates/official/jobs/unit-tests.yml

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It introduces two new extension packages (including Durable orchestration behavior and provider discovery/entry-point loading) that warrant a final human pass on API compatibility and operational/runtime implications beyond the minor nits noted.

Review details

Suppressed comments (3)

azurefunctions-extensions-agents-framework/README.md:31

  • The Python examples in this README use tab indentation, which can render inconsistently in Markdown and may cause copy/paste issues in Python editors that enforce spaces. Use spaces for indentation in the code blocks.
    from agent_framework.openai import OpenAIChatClient

    return OpenAIChatClient()

azurefunctions-extensions-agents-framework/README.md:58

  • This decorator example uses tabs for indentation inside the call, which can lead to inconsistent rendering and formatting when copied. Use spaces for indentation in the snippet.
@app.markdown_agent(
    provider="agent_framework",
    arg_name="agent",
    agent_name="orders",
    client_factory=create_chat_client,

azurefunctions-extensions-agents-framework/README.md:61

  • The example function body line is tab-indented; switching to spaces keeps the snippet consistent with standard Python formatting and avoids mixed-indentation issues.
async def process_order(req: func.HttpRequest, agent: Agent):
    ...
  • Files reviewed: 48/48 changed files
  • Comments generated: 1
  • Review effort level: Lite

hallvictoria and others added 3 commits September 3, 2026 09:32
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Provider input validation should explicitly reject async client_factory callables to prevent confusing runtime failures for consumers.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

azurefunctions-extensions-agents-framework/azurefunctions/extensions/agents/framework/provider.py:91

  • client_factory is required to be a zero-argument synchronous factory (the binding calls it without await). If a user supplies async def client_factory(), this will pass the current validation and later fail with a confusing error when Agent(client=...) receives a coroutine. Add an explicit check to reject coroutine factories early with a clear message.
  • Files reviewed: 48/48 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are confirmed correctness/documented-contract issues (instruction file “raw” passthrough newline handling and sample request JSON parsing returning 500 instead of 400) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

azurefunctions-extensions-agents-framework/samples/hybrid-durable-agent/src/function_app.py:36

  • req.get_json() is passed directly into start_new(). If the request body is not valid JSON, get_json() raises ValueError and the function returns a 500; the sample should return a 400 with a clear error instead.
    azurefunctions-extensions-agents-framework/samples/hybrid-function-agent/src/function_app.py:40
  • req.get_json() is called outside the try/except. Invalid JSON will raise ValueError and return a 500 instead of the intended 400 validation response for this sample.
  • Files reviewed: 48/48 changed files
  • Comments generated: 1
  • Review effort level: Lite

hallvictoria and others added 7 commits September 3, 2026 10:48
Comment thread azurefunctions-agents-extensions-agent-framework/README.md Outdated
Comment thread azurefunctions-agents-extensions-agent-framework/README.md Outdated
Comment thread azurefunctions-agents-extensions-base/tests/test_durable.py
Comment thread azurefunctions-agents-extensions-base/pyproject.toml
)
```

The decorator resolves `order-fulfillment.agent.md` and supplies the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Can the agent name: inside the file be same for different files? Basically just wondering if we enforce unique Agent names via the user provided name: property or the file path for that agent file since the filenames are guaranteed to be unique.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .agent.md file contains raw instructions, so there isn't a name: property to validate. The agent_name passed to markdown_agent() or call_agent() is the logical name and maps to the filename. If the same file exists in both supported locations, we raise an ambiguity error.

EXTENSION_NAME: 'Base'
agents_base_extension:
EXTENSION_DIRECTORY: 'azurefunctions-agents-extensions-base'
EXTENSION_NAME: 'Agents Base'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Agents Base" is a bit confusing, I wonder if we should call it "AF Agent Extensions Base" or something?
(Also not sure how this name is reflected and where its used and we might already have this so apologies if its already handled).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's internal-only, used for naming the pipeline jobs. I think it's fine to keep as it matches the view for the other extensions (plus making it AF Agents Extensions Base makes the display name really long)

For example:
image

Comment thread updated-agent-binding-issue.md Outdated
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.

3 participants