Python: Add Durable Agent Wrapper code (#1913)

* add initial changes

* Move code and add single sample

* Update logger

* Remove unused code

* address PR comments

* cleanup code and address comments

---------

Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
This commit is contained in:
Laveesh Rohra
2025-11-05 16:21:57 -08:00
committed by GitHub
Unverified
parent 5686a009fb
commit 1762cda5f7
18 changed files with 5584 additions and 3412 deletions
@@ -0,0 +1,49 @@
# Single Agent Sample (Python)
This sample demonstrates how to use the Durable Extension for Agent Framework to create a simple Azure Functions app that hosts a single AI agent and provides direct HTTP API access for interactive conversations.
## Key Concepts Demonstrated
- Defining a simple agent with the Microsoft Agent Framework and wiring it into
an Azure Functions app via the Durable Extension for Agent Framework.
- Calling the agent through generated HTTP endpoints (`/api/agents/Joker/run`).
- Managing conversation state with session identifiers, so multiple clients can
interact with the agent concurrently without sharing context.
## Environment Setup
### 1. Create and activate a virtual environment
**Windows (PowerShell):**
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
```
**Linux/macOS:**
```bash
python -m venv .venv
source .venv/bin/activate
```
### 2. Install dependencies
- Azure Functions Core Tools 4.x install from the official docs so you can run `func start` locally.
- Azurite storage emulator the sample uses `AzureWebJobsStorage=UseDevelopmentStorage=true`; start Azurite before launching the app.
- Durable Task local backend `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` expects the Durable Task scheduler listening on `http://localhost:8080` (start the Durable Functions emulator if it is not already running).
- Python dependencies from this folder, run `pip install -r requirements.txt` (or the equivalent in your active virtual environment).
- Environment variables update `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME` in `local.settings.json` with your Azure OpenAI resource details; keep the other values as provided unless you are using custom infrastructure.
## Running the Sample
With the environment configured and the Functions host running, you can interact
with the Joker agent using the provided `demo.http` file or any HTTP client. For
example:
```bash
curl -X POST http://localhost:7071/api/agents/Joker/run \
-H "Content-Type: text/plain" \
-d "Tell me a short joke about cloud computing."
```
The agent responds with a JSON payload that includes the generated joke.
@@ -0,0 +1,26 @@
### Joker Agent Sample Interactions
@baseUrl = http://localhost:7071
@agentName = Joker
@agentRoute = {{baseUrl}}/api/agents/{{agentName}}
@healthRoute = {{baseUrl}}/api/health
### Health Check
GET {{healthRoute}}
### Ask for a joke (JSON payload)
POST {{agentRoute}}/run
Content-Type: application/json
{
"message": "Add a security element to it.",
"sessionId": "session-003",
"waitForCompletion": true
}
### Ask for a joke (plain text payload)
POST {{agentRoute}}/run
Give me a programming joke about race conditions.
### Retrieve conversation state
GET {{agentRoute}}/session-001
@@ -0,0 +1,61 @@
"""Azure Functions single-agent sample showcasing how to host a single Azure OpenAI agent.
The sample reads the required endpoint and deployment environment variables, configures the Azure OpenAI chat client (using either an API key or Azure CLI credentials), and registers a joke-telling agent with an Azure Functions app that can optionally expose a health check.
Summary: Demonstrates configuring and deploying a single 'Joker' agent via Azure Functions."""
import logging
import os
from typing import Any
from azure.identity import AzureCliCredential
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.azurefunctions import AgentFunctionApp
logger = logging.getLogger(__name__)
AZURE_OPENAI_ENDPOINT_ENV = "AZURE_OPENAI_ENDPOINT"
AZURE_OPENAI_DEPLOYMENT_ENV = "AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"
AZURE_OPENAI_API_KEY_ENV = "AZURE_OPENAI_API_KEY"
def _build_client_kwargs() -> dict[str, Any]:
"""Construct Azure OpenAI client options."""
endpoint = os.getenv(AZURE_OPENAI_ENDPOINT_ENV)
if not endpoint:
raise RuntimeError(f"{AZURE_OPENAI_ENDPOINT_ENV} environment variable is required.")
deployment = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
if not deployment:
raise RuntimeError(f"{AZURE_OPENAI_DEPLOYMENT_ENV} environment variable is required.")
logger.info("[SingleAgent] Using deployment '%s' at '%s'", deployment, endpoint)
client_kwargs: dict[str, Any] = {
"endpoint": endpoint,
"deployment_name": deployment,
}
api_key = os.getenv(AZURE_OPENAI_API_KEY_ENV)
if api_key:
client_kwargs["api_key"] = api_key
else:
client_kwargs["credential"] = AzureCliCredential()
return client_kwargs
def _create_agent() -> Any:
"""Create the Joker agent."""
client_kwargs = _build_client_kwargs()
return AzureOpenAIChatClient(**client_kwargs).create_agent(
name="Joker",
instructions="You are good at telling jokes.",
)
app = AgentFunctionApp(agents=[_create_agent()], enable_health_check=True)
@@ -0,0 +1,7 @@
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
@@ -0,0 +1,10 @@
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "python",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>"
}
}
@@ -0,0 +1,2 @@
agent-framework-azurefunctions
azure-identity