feat: add MAF Agent extension package - #185
Conversation
There was a problem hiding this comment.
🟡 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) andazurefunctions-extensions-agents-framework(MAF provider + typedAiAppwrappers). - 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.
There was a problem hiding this comment.
🔵 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
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…hub.com/Azure/azure-functions-python-extensions into hallvictoria/pluggable-agent-extensions
There was a problem hiding this comment.
🔵 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_factoryis required to be a zero-argument synchronous factory (the binding calls it withoutawait). If a user suppliesasync def client_factory(), this will pass the current validation and later fail with a confusing error whenAgent(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
There was a problem hiding this comment.
🟡 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
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| ) | ||
| ``` | ||
|
|
||
| The decorator resolves `order-fulfillment.agent.md` and supplies the |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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' |
There was a problem hiding this comment.
"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).

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
.agent.mdinstructions from the app root oragents/.AgentFunctionAppandapp.markdown_agentAPIs for MAF.[durable]extra.Design Notes
tools=.Customer Experience
Non-durable app:
Durable app: