Skip to content

Commit 0aa8d30

Browse files
authored
Python: Add more samples for Azure Functions (#1980)
* Move all samples * fix comments * remove dead lines * Make samples simpler
1 parent 40b6def commit 0aa8d30

40 files changed

+2155
-46
lines changed

python/samples/getting_started/azure_functions/01_single_agent/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ source .venv/bin/activate
3232
- Azurite storage emulator – the sample uses `AzureWebJobsStorage=UseDevelopmentStorage=true`; start Azurite before launching the app.
3333
- 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).
3434
- Python dependencies – from this folder, run `pip install -r requirements.txt` (or the equivalent in your active virtual environment).
35-
- 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.
35+
- Copy `local.settings.json.template` to `local.settings.json` and update the values for `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME` (and optionally `AZURE_OPENAI_API_KEY`) with your Azure OpenAI resource details; keep the other values as provided unless you are using custom infrastructure.
3636

3737
## Running the Sample
3838

@@ -47,3 +47,17 @@ curl -X POST http://localhost:7071/api/agents/Joker/run \
4747
```
4848

4949
The agent responds with a JSON payload that includes the generated joke.
50+
51+
## Expected Output
52+
53+
When you send a POST request with plain-text input, the Functions host responds with an HTTP 202 and queues the request for the durable agent entity. A typical response body looks like the following:
54+
55+
```json
56+
{
57+
"status": "accepted",
58+
"response": "Agent request accepted",
59+
"message": "Tell me a short joke about cloud computing.",
60+
"conversation_id": "<guid>",
61+
"correlation_id": "<guid>"
62+
}
63+
```

python/samples/getting_started/azure_functions/01_single_agent/demo.http

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Content-Type: application/json
1414
{
1515
"message": "Add a security element to it.",
1616
"sessionId": "session-003",
17-
"waitForCompletion": true
17+
"waitForCompletion": false
1818
}
1919

2020
### Ask for a joke (plain text payload)
Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,38 @@
1-
"""Azure Functions single-agent sample showcasing how to host a single Azure OpenAI agent.
1+
"""Host a single Azure OpenAI-powered agent inside Azure Functions.
22
3-
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.
3+
Components used in this sample:
4+
- AzureOpenAIChatClient to call the Azure OpenAI chat deployment.
5+
- AgentFunctionApp to expose HTTP endpoints via the Durable Functions extension.
46
5-
Summary: Demonstrates configuring and deploying a single 'Joker' agent via Azure Functions."""
7+
Prerequisites: set `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME` (plus `AZURE_OPENAI_API_KEY` or Azure CLI authentication) before starting the Functions host."""
68

7-
import logging
8-
import os
99
from typing import Any
1010

11-
from azure.identity import AzureCliCredential
1211
from agent_framework.azure import AzureOpenAIChatClient
1312
from agent_framework.azurefunctions import AgentFunctionApp
1413

15-
16-
logger = logging.getLogger(__name__)
17-
18-
19-
AZURE_OPENAI_ENDPOINT_ENV = "AZURE_OPENAI_ENDPOINT"
20-
AZURE_OPENAI_DEPLOYMENT_ENV = "AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"
21-
AZURE_OPENAI_API_KEY_ENV = "AZURE_OPENAI_API_KEY"
22-
23-
24-
def _build_client_kwargs() -> dict[str, Any]:
25-
"""Construct Azure OpenAI client options."""
26-
27-
endpoint = os.getenv(AZURE_OPENAI_ENDPOINT_ENV)
28-
if not endpoint:
29-
raise RuntimeError(f"{AZURE_OPENAI_ENDPOINT_ENV} environment variable is required.")
30-
31-
deployment = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
32-
if not deployment:
33-
raise RuntimeError(f"{AZURE_OPENAI_DEPLOYMENT_ENV} environment variable is required.")
34-
35-
logger.info("[SingleAgent] Using deployment '%s' at '%s'", deployment, endpoint)
36-
37-
client_kwargs: dict[str, Any] = {
38-
"endpoint": endpoint,
39-
"deployment_name": deployment,
40-
}
41-
42-
api_key = os.getenv(AZURE_OPENAI_API_KEY_ENV)
43-
if api_key:
44-
client_kwargs["api_key"] = api_key
45-
else:
46-
client_kwargs["credential"] = AzureCliCredential()
47-
48-
return client_kwargs
49-
50-
14+
# 1. Instantiate the agent with the chosen deployment and instructions.
5115
def _create_agent() -> Any:
5216
"""Create the Joker agent."""
5317

54-
client_kwargs = _build_client_kwargs()
55-
return AzureOpenAIChatClient(**client_kwargs).create_agent(
18+
return AzureOpenAIChatClient().create_agent(
5619
name="Joker",
5720
instructions="You are good at telling jokes.",
5821
)
5922

6023

24+
# 2. Register the agent with AgentFunctionApp so Azure Functions exposes the required triggers.
6125
app = AgentFunctionApp(agents=[_create_agent()], enable_health_check=True)
26+
27+
"""
28+
Expected output when invoking `POST /api/agents/Joker/run` with plain-text input:
29+
30+
HTTP/1.1 202 Accepted
31+
{
32+
"status": "accepted",
33+
"response": "Agent request accepted",
34+
"message": "Tell me a short joke about cloud computing.",
35+
"conversation_id": "<guid>",
36+
"correlation_id": "<guid>"
37+
}
38+
"""
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Multi-Agent Sample
2+
3+
This sample demonstrates how to use the Durable Extension for Agent Framework to create an Azure Functions app that hosts multiple AI agents and provides direct HTTP API access for interactive conversations with each agent.
4+
5+
## Key Concepts Demonstrated
6+
7+
- Using the Microsoft Agent Framework to define multiple AI agents with unique names and instructions.
8+
- Registering multiple agents with the Function app and running them using HTTP.
9+
- Conversation management (via session IDs) for isolated interactions per agent.
10+
- Two different methods for registering agents: list-based initialization and incremental addition.
11+
12+
## Environment Setup
13+
14+
### 1. Create and activate a virtual environment
15+
16+
**Windows (PowerShell):**
17+
```powershell
18+
python -m venv .venv
19+
.venv\Scripts\Activate.ps1
20+
```
21+
22+
**Linux/macOS:**
23+
```bash
24+
python -m venv .venv
25+
source .venv/bin/activate
26+
```
27+
28+
### 2. Install dependencies
29+
30+
See the [README.md](../README.md) file in the parent directory for more information on how to configure the environment, including how to install and run common sample dependencies.
31+
32+
### 3. Configure local settings
33+
34+
Copy `local.settings.json.template` to `local.settings.json`, then set the Azure OpenAI values (`AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`, and optionally `AZURE_OPENAI_API_KEY`) to match your environment.
35+
36+
## Running the Sample
37+
38+
With the environment setup and function app running, you can test the sample by sending HTTP requests to the different agent endpoints.
39+
40+
You can use the `demo.http` file to send messages to the agents, or a command line tool like `curl` as shown below:
41+
42+
### Test the Weather Agent
43+
44+
Bash (Linux/macOS/WSL):
45+
46+
```bash
47+
curl -X POST http://localhost:7071/api/agents/WeatherAgent/run \
48+
-H "Content-Type: application/json" \
49+
-d '{"message": "What is the weather in Seattle?"}'
50+
```
51+
52+
PowerShell:
53+
54+
```powershell
55+
Invoke-RestMethod -Method Post `
56+
-Uri http://localhost:7071/api/agents/WeatherAgent/run `
57+
-ContentType application/json `
58+
-Body '{"message": "What is the weather in Seattle?"}'
59+
```
60+
61+
Expected response:
62+
```json
63+
{
64+
"status": "accepted",
65+
"response": "Agent request accepted",
66+
"message": "What is the weather in Seattle?",
67+
"conversation_id": "<guid>",
68+
"correlation_id": "<guid>"
69+
}
70+
```
71+
72+
### Test the Math Agent
73+
74+
Bash (Linux/macOS/WSL):
75+
76+
```bash
77+
curl -X POST http://localhost:7071/api/agents/MathAgent/run \
78+
-H "Content-Type: application/json" \
79+
-d '{"message": "Calculate a 20% tip on a $50 bill"}'
80+
```
81+
82+
PowerShell:
83+
84+
```powershell
85+
Invoke-RestMethod -Method Post `
86+
-Uri http://localhost:7071/api/agents/MathAgent/run `
87+
-ContentType application/json `
88+
-Body '{"message": "Calculate a 20% tip on a $50 bill"}'
89+
```
90+
91+
Expected response:
92+
```json
93+
{
94+
"status": "accepted",
95+
"response": "Agent request accepted",
96+
"message": "Calculate a 20% tip on a $50 bill",
97+
"conversation_id": "<guid>",
98+
"correlation_id": "<guid>"
99+
}
100+
```
101+
102+
### Check Health
103+
104+
Bash (Linux/macOS/WSL):
105+
106+
```bash
107+
curl http://localhost:7071/api/health
108+
```
109+
110+
PowerShell:
111+
112+
```powershell
113+
Invoke-RestMethod -Uri http://localhost:7071/api/health
114+
```
115+
116+
Expected response:
117+
```json
118+
{
119+
"status": "healthy",
120+
"agents": [
121+
{"name": "WeatherAgent", "type": "ChatAgent"},
122+
{"name": "MathAgent", "type": "ChatAgent"}
123+
],
124+
"agent_count": 2
125+
}
126+
```
127+
128+
## Code Structure
129+
130+
The sample demonstrates two ways to register multiple agents:
131+
132+
### Option 1: Pass list of agents during initialization
133+
```python
134+
app = AgentFunctionApp(agents=[weather_agent, math_agent])
135+
```
136+
137+
### Option 2: Add agents incrementally (commented in sample)
138+
```python
139+
app = AgentFunctionApp()
140+
app.add_agent(weather_agent)
141+
app.add_agent(math_agent)
142+
```
143+
144+
Each agent automatically gets:
145+
- `POST /api/agents/{agent_name}/run` - Send messages to the agent
146+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
### DAFx Multi-Agent Function App - HTTP Samples
2+
### Use with the VS Code REST Client extension or any HTTP client
3+
###
4+
### API Structure:
5+
### - POST /api/agents/{agentName}/run -> Send a message to an agent
6+
### - GET /api/health -> Health check and agent metadata
7+
8+
### Variables
9+
@baseUrl = http://localhost:7071
10+
@weatherAgentName = WeatherAgent
11+
@mathAgentName = MathAgent
12+
@weatherAgentRoute = {{baseUrl}}/api/agents/{{weatherAgentName}}
13+
@mathAgentRoute = {{baseUrl}}/api/agents/{{mathAgentName}}
14+
@healthRoute = {{baseUrl}}/api/health
15+
16+
### Health Check
17+
# Confirms the Azure Functions app is running and both agents are registered
18+
# Expected response:
19+
# {
20+
# "status": "healthy",
21+
# "agents": [
22+
# {"name": "WeatherAgent", "type": "AzureOpenAIAssistantsAgent"},
23+
# {"name": "MathAgent", "type": "AzureOpenAIAssistantsAgent"}
24+
# ],
25+
# "agent_count": 2
26+
# }
27+
GET {{healthRoute}}
28+
29+
###
30+
31+
### Weather Agent - Current Conditions
32+
# Tests the Weather agent's tool-assisted response path
33+
# Expected response: { "response": "The weather in Seattle...", "status": "success" }
34+
POST {{weatherAgentRoute}}/run
35+
Content-Type: application/json
36+
37+
{
38+
"message": "What is the weather in Seattle?",
39+
"sessionId": "weather-user-001",
40+
"waitForCompletion": true
41+
}
42+
43+
###
44+
45+
46+
### Math Agent - Tip Calculation
47+
# Exercises the Math agent with a calculation request
48+
# Expected response: { "response": "A 20% tip on a $50 bill is $10...", "status": "success" }
49+
POST {{mathAgentRoute}}/run
50+
Content-Type: application/json
51+
52+
{
53+
"message": "Calculate a 20% tip on a $50 bill",
54+
"sessionId": "math-user-001"
55+
}
56+
57+
###
58+

0 commit comments

Comments
 (0)