mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Python: Provider-leading client design & OpenAI package extraction (#4818)
* Python: Provider-leading client design & OpenAI package extraction Major refactoring of the Python Agent Framework client architecture: - Extract OpenAI clients into new `agent-framework-openai` package - Core package no longer depends on openai, azure-identity, azure-ai-projects - Rename clients for discoverability: OpenAIResponsesClient → OpenAIChatClient, OpenAIChatClient → OpenAIChatCompletionClient - Unify `model_id`/`deployment_name`/`model_deployment_name` → `model` param - New FoundryChatClient for Azure AI Foundry Responses API - New FoundryAgent/FoundryAgentClient for connecting to pre-configured Foundry agents - Remove OpenAIBase/OpenAIConfigMixin from non-deprecated client MRO - Deprecate AzureOpenAI* clients, AzureAIClient, OpenAIAssistantsClient - Reorganize samples: azure_openai+azure_ai+azure_ai_agent → azure/ - ADR-0020: Provider-Leading Client Design Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: missing Agent imports in samples, .model_id → .model in foundry_local sample Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: CI failures — mypy errors, coverage targets, sample imports - azure-ai mypy: add type ignores for TypedDict total=, model arg, forward ref - Coverage: replace core.azure/openai targets with openai package target - project_provider: add type annotation for opts dict Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: populate openai .pyi stub, fix broken README links, coverage targets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fixes * updated observabilitty * reset azure init.pyi * fix errors * updated adr number * fix foundry local * fixed not renamed docstrings and comments, and added deprecated markers to old classes * fix tests and pyprojects * fix test vars * updated function tests * update durable * updated test setup for functions * Fix Foundry auth in workflow samples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stabilize Python integration workflows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update hosting samples for Foundry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Trigger full CI rerun Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Trigger CI rerun again Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * trigger rerun * trigger rerun * fix for litellm * undo durabletask changes * Move Foundry APIs into foundry namespace Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Foundry pyproject formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Split provider samples by Foundry surface Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Restore hosting sample requirements Also fix the Foundry Local sample link after the provider sample move. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updated tests * udpated foundry integration tests * removed dist from azurefunctions tests * Use separate Foundry clients for concurrent agents Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix client setup in azfunc and durable * disabled two tests * updated setup for some function and durable tests * improved azure openai setup with new clients * ignore deprecated * fixes * skip 11 * remove openai assistants int tests --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
4b533608b6
commit
5e056b672e
@@ -10,7 +10,7 @@ from a2a.server.request_handlers.default_request_handler import DefaultRequestHa
|
||||
from a2a.server.tasks.inmemory_task_store import InMemoryTaskStore
|
||||
from agent_definitions import AGENT_CARD_FACTORIES, AGENT_FACTORIES
|
||||
from agent_executor import AgentFrameworkExecutor
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -35,8 +35,8 @@ Usage:
|
||||
uv run python a2a_server.py --agent-type logistics --port 5002
|
||||
|
||||
Environment variables:
|
||||
AZURE_AI_PROJECT_ENDPOINT — Your Azure AI Foundry project endpoint
|
||||
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME — Model deployment name (e.g. gpt-4o)
|
||||
FOUNDRY_PROJECT_ENDPOINT — Your Azure AI Foundry project endpoint
|
||||
FOUNDRY_MODEL — Model deployment name (e.g. gpt-4o)
|
||||
"""
|
||||
|
||||
|
||||
@@ -66,21 +66,21 @@ def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
# Validate environment
|
||||
project_endpoint = os.getenv("AZURE_AI_PROJECT_ENDPOINT")
|
||||
deployment_name = os.getenv("AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME")
|
||||
project_endpoint = os.getenv("FOUNDRY_PROJECT_ENDPOINT")
|
||||
deployment_name = os.getenv("FOUNDRY_MODEL")
|
||||
|
||||
if not project_endpoint:
|
||||
print("Error: AZURE_AI_PROJECT_ENDPOINT environment variable is not set.")
|
||||
print("Error: FOUNDRY_PROJECT_ENDPOINT environment variable is not set.")
|
||||
sys.exit(1)
|
||||
if not deployment_name:
|
||||
print("Error: AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME environment variable is not set.")
|
||||
print("Error: FOUNDRY_MODEL environment variable is not set.")
|
||||
sys.exit(1)
|
||||
|
||||
# Create the LLM client
|
||||
credential = AzureCliCredential()
|
||||
client = AzureOpenAIResponsesClient(
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
deployment_name=deployment_name,
|
||||
model=deployment_name,
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from invoice_data import query_by_invoice_id, query_by_transaction_id, query_inv
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -54,26 +54,29 @@ Quantity: 900
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def create_invoice_agent(client: AzureOpenAIResponsesClient) -> Agent:
|
||||
def create_invoice_agent(client: FoundryChatClient) -> Agent:
|
||||
"""Create an invoice agent backed by the given client with query tools."""
|
||||
return client.as_agent(
|
||||
return Agent(
|
||||
client=client,
|
||||
name="InvoiceAgent",
|
||||
instructions=INVOICE_INSTRUCTIONS,
|
||||
tools=[query_invoices, query_by_transaction_id, query_by_invoice_id],
|
||||
)
|
||||
|
||||
|
||||
def create_policy_agent(client: AzureOpenAIResponsesClient) -> Agent:
|
||||
def create_policy_agent(client: FoundryChatClient) -> Agent:
|
||||
"""Create a policy agent backed by the given client."""
|
||||
return client.as_agent(
|
||||
return Agent(
|
||||
client=client,
|
||||
name="PolicyAgent",
|
||||
instructions=POLICY_INSTRUCTIONS,
|
||||
)
|
||||
|
||||
|
||||
def create_logistics_agent(client: AzureOpenAIResponsesClient) -> Agent:
|
||||
def create_logistics_agent(client: FoundryChatClient) -> Agent:
|
||||
"""Create a logistics agent backed by the given client."""
|
||||
return client.as_agent(
|
||||
return Agent(
|
||||
client=client,
|
||||
name="LogisticsAgent",
|
||||
instructions=LOGISTICS_INSTRUCTIONS,
|
||||
)
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Host a single Azure OpenAI-powered agent inside Azure Functions.
|
||||
"""Host a single Foundry-powered agent inside Azure Functions.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient to call the Azure OpenAI chat deployment.
|
||||
- FoundryChatClient to call the Foundry deployment.
|
||||
- AgentFunctionApp to expose HTTP endpoints via the Durable Functions extension.
|
||||
|
||||
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."""
|
||||
Prerequisites: set `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, and sign in
|
||||
with Azure CLI before starting the Functions host."""
|
||||
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# 1. Instantiate the agent with the chosen deployment and instructions.
|
||||
def _create_agent() -> Any:
|
||||
"""Create the Joker agent."""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
return Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
name="Joker",
|
||||
instructions="You are good at telling jokes.",
|
||||
)
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Host multiple Azure OpenAI agents inside a single Azure Functions app.
|
||||
"""Host multiple Foundry-powered agents inside a single Azure Functions app.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient to create agents bound to a shared Azure OpenAI deployment.
|
||||
- FoundryChatClient to create agents bound to a shared Foundry deployment.
|
||||
- AgentFunctionApp to register multiple agents and expose dedicated HTTP endpoints.
|
||||
- Custom tool functions to demonstrate tool invocation from different agents.
|
||||
|
||||
Prerequisites: set `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`, plus either
|
||||
`AZURE_OPENAI_API_KEY` or authenticate with Azure CLI before starting the Functions host."""
|
||||
Prerequisites: set `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, and sign in with Azure CLI before starting the Functions host."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -59,15 +60,21 @@ def calculate_tip(bill_amount: float, tip_percentage: float = 15.0) -> dict[str,
|
||||
|
||||
|
||||
# 1. Create multiple agents, each with its own instruction set and tools.
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
weather_agent = client.as_agent(
|
||||
weather_agent = Agent(
|
||||
client=client,
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather assistant. Provide current weather information.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
math_agent = client.as_agent(
|
||||
math_agent = Agent(
|
||||
client=client,
|
||||
name="MathAgent",
|
||||
instructions="You are a helpful math assistant. Help users with calculations like tip calculations.",
|
||||
tools=[calculate_tip],
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
This sample demonstrates how to implement reliable streaming for durable agents using Redis Streams.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient to create the travel planner agent with tools.
|
||||
- FoundryChatClient to create the travel planner agent with tools.
|
||||
- AgentFunctionApp with a Redis-based callback for persistent streaming.
|
||||
- Custom HTTP endpoint to resume streaming from any point using cursor-based pagination.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI (`az login`) for `AzureCliCredential`
|
||||
- Redis running (docker run -d --name redis -p 6379:6379 redis:latest)
|
||||
- DTS and Azurite running (see parent README)
|
||||
"""
|
||||
@@ -21,14 +22,14 @@ from datetime import timedelta
|
||||
|
||||
import azure.functions as func
|
||||
import redis.asyncio as aioredis
|
||||
from agent_framework import AgentResponseUpdate
|
||||
from agent_framework import Agent, AgentResponseUpdate
|
||||
from agent_framework.azure import (
|
||||
AgentCallbackContext,
|
||||
AgentFunctionApp,
|
||||
AgentResponseCallbackProtocol,
|
||||
AzureOpenAIChatClient,
|
||||
)
|
||||
from azure.identity import AzureCliCredential
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from redis_stream_response_handler import RedisStreamResponseHandler, StreamChunk
|
||||
from tools import get_local_events, get_weather_forecast
|
||||
@@ -155,7 +156,12 @@ redis_callback = RedisStreamCallback()
|
||||
# Create the travel planner agent
|
||||
def create_travel_agent():
|
||||
"""Create the TravelPlanner agent with tools."""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
return Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
name="TravelPlanner",
|
||||
instructions="""You are an expert travel planner who creates detailed, personalized travel itineraries.
|
||||
When asked to plan a trip, you should:
|
||||
|
||||
+3
-4
@@ -5,10 +5,9 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>",
|
||||
"REDIS_CONNECTION_STRING": "redis://localhost:6379",
|
||||
"REDIS_STREAM_TTL_MINUTES": "10"
|
||||
"REDIS_STREAM_TTL_MINUTES": "10",
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
azure-identity
|
||||
|
||||
# Redis client
|
||||
redis
|
||||
|
||||
+14
-10
@@ -3,26 +3,24 @@
|
||||
"""Chain two runs of a single agent inside a Durable Functions orchestration.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient to construct the writer agent hosted by Agent Framework.
|
||||
- FoundryChatClient to construct the writer agent hosted by Agent Framework.
|
||||
- AgentFunctionApp to surface HTTP and orchestration triggers via the Azure Functions extension.
|
||||
- Durable Functions orchestration to run sequential agent invocations on the same conversation session.
|
||||
|
||||
Prerequisites: configure `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`, and either
|
||||
`AZURE_OPENAI_API_KEY` or authenticate with Azure CLI before starting the Functions host."""
|
||||
Prerequisites: configure `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, and sign in with Azure CLI before starting the Functions host."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
|
||||
import azure.functions as func
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.durable_functions import DurableOrchestrationClient, DurableOrchestrationContext
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -38,7 +36,13 @@ def _create_writer_agent() -> Any:
|
||||
"when given an improved sentence you polish it further."
|
||||
)
|
||||
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=WRITER_AGENT_NAME,
|
||||
instructions=instructions,
|
||||
)
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
+19
-10
@@ -3,23 +3,24 @@
|
||||
"""Fan out concurrent runs across two agents inside a Durable Functions orchestration.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient to create domain-specific agents hosted by Agent Framework.
|
||||
- FoundryChatClient to create domain-specific agents hosted by Agent Framework.
|
||||
- AgentFunctionApp to expose orchestration and HTTP triggers.
|
||||
- Durable Functions orchestration that executes agent calls in parallel and aggregates results.
|
||||
|
||||
Prerequisites: configure `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`, and either
|
||||
`AZURE_OPENAI_API_KEY` or authenticate with Azure CLI before starting the Functions host."""
|
||||
Prerequisites: configure `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, and sign in with Azure CLI before starting the Functions host."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Generator
|
||||
from typing import Any, cast
|
||||
|
||||
import azure.functions as func
|
||||
from agent_framework import AgentResponse
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
from agent_framework import Agent, AgentResponse
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.durable_functions import DurableOrchestrationClient, DurableOrchestrationContext
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -34,14 +35,22 @@ CHEMIST_AGENT_NAME = "ChemistAgent"
|
||||
|
||||
# 2. Instantiate both agents that the orchestration will run concurrently.
|
||||
def _create_agents() -> list[Any]:
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
physicist = client.as_agent(
|
||||
physicist = Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
name=PHYSICIST_AGENT_NAME,
|
||||
instructions="You are an expert in physics. You answer questions from a physics perspective.",
|
||||
)
|
||||
|
||||
chemist = client.as_agent(
|
||||
chemist = Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
name=CHEMIST_AGENT_NAME,
|
||||
instructions="You are an expert in chemistry. You answer questions from a chemistry perspective.",
|
||||
)
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
+16
-12
@@ -3,29 +3,27 @@
|
||||
"""Route email requests through conditional orchestration with two agents.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient agents for spam detection and email drafting.
|
||||
- FoundryChatClient agents for spam detection and email drafting.
|
||||
- AgentFunctionApp with Durable orchestration, activity, and HTTP triggers.
|
||||
- Pydantic models that validate payloads and agent JSON responses.
|
||||
|
||||
Prerequisites: set `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`,
|
||||
and either `AZURE_OPENAI_API_KEY` or sign in with Azure CLI before running the
|
||||
Prerequisites: set `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, and sign in with Azure CLI before running the
|
||||
Functions host."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Generator, Mapping
|
||||
from typing import Any
|
||||
|
||||
import azure.functions as func
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.durable_functions import DurableOrchestrationClient, DurableOrchestrationContext
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 1. Define agent names shared across the orchestration.
|
||||
@@ -49,14 +47,20 @@ class EmailPayload(BaseModel):
|
||||
|
||||
# 2. Instantiate both agents so they can be registered with AgentFunctionApp.
|
||||
def _create_agents() -> list[Any]:
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
spam_agent = client.as_agent(
|
||||
spam_agent = Agent(
|
||||
client=client,
|
||||
name=SPAM_AGENT_NAME,
|
||||
instructions="You are a spam detection assistant that identifies spam emails.",
|
||||
)
|
||||
|
||||
email_agent = client.as_agent(
|
||||
email_agent = Agent(
|
||||
client=client,
|
||||
name=EMAIL_AGENT_NAME,
|
||||
instructions="You are an email assistant that helps users draft responses to emails with professionalism.",
|
||||
)
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
+3
-3
@@ -6,11 +6,11 @@ output or a maximum number of attempts is reached.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Complete the common setup instructions in `../README.md` to prepare the virtual environment, install dependencies, and configure Azure OpenAI and storage settings.
|
||||
Complete the common setup instructions in `../README.md` to prepare the virtual environment, install dependencies, and configure Foundry and storage settings.
|
||||
|
||||
## What It Shows
|
||||
- Identical environment variable usage (`AZURE_OPENAI_ENDPOINT`,
|
||||
`AZURE_OPENAI_DEPLOYMENT`) and HTTP surface area (`/api/hitl/...`).
|
||||
- Identical environment variable usage (`FOUNDRY_PROJECT_ENDPOINT`,
|
||||
`FOUNDRY_MODEL`) and HTTP surface area (`/api/hitl/...`).
|
||||
- Durable orchestrations that pause for external events while maintaining
|
||||
deterministic state (`context.wait_for_external_event` + timed cancellation).
|
||||
- Activity functions that encapsulate the out-of-band operations such as notifying
|
||||
|
||||
+14
-10
@@ -3,29 +3,27 @@
|
||||
"""Iterate on generated content with a human-in-the-loop Durable orchestration.
|
||||
|
||||
Components used in this sample:
|
||||
- AzureOpenAIChatClient for a single writer agent that emits structured JSON.
|
||||
- FoundryChatClient for a single writer agent that emits structured JSON.
|
||||
- AgentFunctionApp with Durable orchestration, HTTP triggers, and activity triggers.
|
||||
- External events that pause the workflow until a human decision arrives or times out.
|
||||
|
||||
Prerequisites: configure `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`, and
|
||||
either `AZURE_OPENAI_API_KEY` or sign in with Azure CLI before running `func start`."""
|
||||
Prerequisites: configure `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, and sign in with Azure CLI before running `func start`."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Generator, Mapping
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
import azure.functions as func
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.durable_functions import DurableOrchestrationClient, DurableOrchestrationContext
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 1. Define orchestration constants used throughout the workflow.
|
||||
@@ -57,7 +55,13 @@ def _create_writer_agent() -> Any:
|
||||
"Return your response as JSON with 'title' and 'content' fields."
|
||||
)
|
||||
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=WRITER_AGENT_NAME,
|
||||
instructions=instructions,
|
||||
)
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<AZURE_OPENAI_CHAT_DEPLOYMENT_NAME>",
|
||||
"AZURE_OPENAI_API_KEY": "<AZURE_OPENAI_API_KEY>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
@@ -30,14 +30,13 @@ See the [README.md](../README.md) file in the parent directory for complete setu
|
||||
|
||||
## Configuration
|
||||
|
||||
Update your `local.settings.json` with your Azure OpenAI credentials:
|
||||
Update your `local.settings.json` with your Foundry project settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"Values": {
|
||||
"AZURE_OPENAI_ENDPOINT": "https://your-resource.openai.azure.com/",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "your-deployment-name",
|
||||
"AZURE_OPENAI_KEY": "your-api-key-if-not-using-rbac"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "https://your-project.services.ai.azure.com/api/projects/your-project",
|
||||
"FOUNDRY_MODEL": "your-deployment-name"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Example showing how to configure AI agents with different trigger configurations.
|
||||
"""Example showing how to configure AI agents with different trigger configurations.
|
||||
|
||||
This sample demonstrates how to configure agents to be accessible as both HTTP endpoints
|
||||
and Model Context Protocol (MCP) tools, enabling flexible integration patterns for AI agent
|
||||
@@ -18,37 +17,48 @@ This sample creates three agents with different trigger configurations:
|
||||
- PlantAdvisor: Both HTTP and MCP tool triggers enabled
|
||||
|
||||
Required environment variables:
|
||||
- AZURE_OPENAI_ENDPOINT: Your Azure OpenAI endpoint
|
||||
- AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: Your Azure OpenAI deployment name
|
||||
- FOUNDRY_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint
|
||||
- FOUNDRY_MODEL: Your Azure AI Foundry deployment name
|
||||
|
||||
Authentication uses AzureCliCredential (Azure Identity).
|
||||
"""
|
||||
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
import os
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Create Azure OpenAI Chat Client
|
||||
# Create Foundry chat client
|
||||
# This uses AzureCliCredential for authentication (requires 'az login')
|
||||
client = AzureOpenAIChatClient()
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
# Define three AI agents with different roles
|
||||
# Agent 1: Joker - HTTP trigger only (default)
|
||||
agent1 = client.as_agent(
|
||||
agent1 = Agent(
|
||||
client=client,
|
||||
name="Joker",
|
||||
instructions="You are good at telling jokes.",
|
||||
)
|
||||
|
||||
# Agent 2: StockAdvisor - MCP tool trigger only
|
||||
agent2 = client.as_agent(
|
||||
agent2 = Agent(
|
||||
client=client,
|
||||
name="StockAdvisor",
|
||||
instructions="Check stock prices.",
|
||||
)
|
||||
|
||||
# Agent 3: PlantAdvisor - Both HTTP and MCP tool triggers
|
||||
agent3 = client.as_agent(
|
||||
agent3 = Agent(
|
||||
client=client,
|
||||
name="PlantAdvisor",
|
||||
instructions="Recommend plants.",
|
||||
description="Get plant recommendations.",
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
"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>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ SharedState allows executors to pass large payloads (like email content) by refe
|
||||
```json
|
||||
{
|
||||
"Values": {
|
||||
"AZURE_OPENAI_ENDPOINT": "https://your-resource.openai.azure.com/",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "gpt-4o"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "https://your-project.services.ai.azure.com/api/projects/your-project",
|
||||
"FOUNDRY_MODEL": "gpt-4o"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+22
-27
@@ -13,8 +13,8 @@ Show how to:
|
||||
- Compose agent backed executors with function style executors and yield the final output when the workflow completes.
|
||||
|
||||
Prerequisites:
|
||||
- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables.
|
||||
- Authentication via azure-identity. Use DefaultAzureCredential and run az login before executing the sample.
|
||||
- Configure `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL` for FoundryChatClient.
|
||||
- Authentication uses `AzureCliCredential`; run `az login` before executing the sample.
|
||||
- Familiarity with WorkflowBuilder, executors, conditional edges, and streaming runs.
|
||||
"""
|
||||
|
||||
@@ -25,6 +25,7 @@ from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
Message,
|
||||
@@ -33,18 +34,17 @@ from agent_framework import (
|
||||
WorkflowContext,
|
||||
executor,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework_azurefunctions import AgentFunctionApp
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from typing_extensions import Never
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Environment variable names
|
||||
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"
|
||||
FOUNDRY_PROJECT_ENDPOINT_ENV = "FOUNDRY_PROJECT_ENDPOINT"
|
||||
AZURE_OPENAI_DEPLOYMENT_ENV = "FOUNDRY_MODEL"
|
||||
|
||||
EMAIL_STATE_PREFIX = "email:"
|
||||
CURRENT_EMAIL_ID_KEY = "current_email_id"
|
||||
@@ -172,35 +172,29 @@ async def handle_spam(detection: DetectionResult, ctx: WorkflowContext[Never, st
|
||||
|
||||
|
||||
def _build_client_kwargs() -> dict[str, Any]:
|
||||
"""Build Azure OpenAI client configuration from environment variables."""
|
||||
endpoint = os.getenv(AZURE_OPENAI_ENDPOINT_ENV)
|
||||
if not endpoint:
|
||||
raise RuntimeError(f"{AZURE_OPENAI_ENDPOINT_ENV} environment variable is required.")
|
||||
"""Build Foundry chat client configuration from environment variables."""
|
||||
project_endpoint = os.getenv(FOUNDRY_PROJECT_ENDPOINT_ENV)
|
||||
if not project_endpoint:
|
||||
raise RuntimeError(f"{FOUNDRY_PROJECT_ENDPOINT_ENV} environment variable is required.")
|
||||
|
||||
deployment = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
|
||||
if not deployment:
|
||||
model = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
|
||||
if not model:
|
||||
raise RuntimeError(f"{AZURE_OPENAI_DEPLOYMENT_ENV} environment variable is required.")
|
||||
|
||||
client_kwargs: dict[str, Any] = {
|
||||
"endpoint": endpoint,
|
||||
"deployment_name": deployment,
|
||||
return {
|
||||
"project_endpoint": project_endpoint,
|
||||
"model": model,
|
||||
"credential": AzureCliCredential(),
|
||||
}
|
||||
|
||||
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_workflow() -> Workflow:
|
||||
"""Create the email classification workflow with conditional routing."""
|
||||
client_kwargs = _build_client_kwargs()
|
||||
chat_client = AzureOpenAIChatClient(**client_kwargs)
|
||||
chat_client = FoundryChatClient(**client_kwargs)
|
||||
|
||||
spam_detection_agent = chat_client.as_agent(
|
||||
spam_detection_agent = Agent(
|
||||
client=chat_client,
|
||||
instructions=(
|
||||
"You are a spam detection assistant that identifies spam emails. "
|
||||
"Always return JSON with fields is_spam (bool) and reason (string)."
|
||||
@@ -209,7 +203,8 @@ def _create_workflow() -> Workflow:
|
||||
name="spam_detection_agent",
|
||||
)
|
||||
|
||||
email_assistant_agent = chat_client.as_agent(
|
||||
email_assistant_agent = Agent(
|
||||
client=chat_client,
|
||||
instructions=(
|
||||
"You are an email assistant that helps users draft responses to emails with professionalism. "
|
||||
"Return JSON with a single field 'response' containing the drafted reply."
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "python",
|
||||
"AZURE_OPENAI_ENDPOINT": "<Your Azure OpenAI endpoint>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<Your Azure OpenAI chat deployment name>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,2 +1,15 @@
|
||||
agent-framework-azurefunctions
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
azure-identity
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# Azure OpenAI Configuration
|
||||
AZURE_OPENAI_ENDPOINT=https://<your-resource-name>.openai.azure.com/
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=<your-deployment-name>
|
||||
AZURE_OPENAI_API_KEY=<your-api-key>
|
||||
# Foundry Configuration
|
||||
FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com/api/projects/your-project
|
||||
FOUNDRY_MODEL=<your-deployment-name>
|
||||
|
||||
+25
-24
@@ -14,6 +14,11 @@ Key architectural points:
|
||||
|
||||
This approach allows using the rich structure of `WorkflowBuilder` while leveraging
|
||||
the statefulness and durability of `DurableAIAgent`s.
|
||||
|
||||
Prerequisites:
|
||||
- Configure `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`
|
||||
- Sign in with Azure CLI (`az login`) for `AzureCliCredential`
|
||||
- Ensure Azurite and the Durable Task Scheduler emulator are running
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -22,6 +27,7 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorResponse,
|
||||
Case,
|
||||
Default,
|
||||
@@ -31,17 +37,16 @@ from agent_framework import (
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework_azurefunctions import AgentFunctionApp
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from typing_extensions import Never
|
||||
|
||||
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"
|
||||
FOUNDRY_PROJECT_ENDPOINT_ENV = "FOUNDRY_PROJECT_ENDPOINT"
|
||||
AZURE_OPENAI_DEPLOYMENT_ENV = "FOUNDRY_MODEL"
|
||||
SPAM_AGENT_NAME = "SpamDetectionAgent"
|
||||
EMAIL_AGENT_NAME = "EmailAssistantAgent"
|
||||
|
||||
@@ -87,27 +92,21 @@ class EmailPayload(BaseModel):
|
||||
|
||||
|
||||
def _build_client_kwargs() -> dict[str, Any]:
|
||||
endpoint = os.getenv(AZURE_OPENAI_ENDPOINT_ENV)
|
||||
if not endpoint:
|
||||
raise RuntimeError(f"{AZURE_OPENAI_ENDPOINT_ENV} environment variable is required.")
|
||||
"""Build Foundry chat client configuration from environment variables."""
|
||||
project_endpoint = os.getenv(FOUNDRY_PROJECT_ENDPOINT_ENV)
|
||||
if not project_endpoint:
|
||||
raise RuntimeError(f"{FOUNDRY_PROJECT_ENDPOINT_ENV} environment variable is required.")
|
||||
|
||||
deployment = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
|
||||
if not deployment:
|
||||
model = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
|
||||
if not model:
|
||||
raise RuntimeError(f"{AZURE_OPENAI_DEPLOYMENT_ENV} environment variable is required.")
|
||||
|
||||
client_kwargs: dict[str, Any] = {
|
||||
"endpoint": endpoint,
|
||||
"deployment_name": deployment,
|
||||
return {
|
||||
"project_endpoint": project_endpoint,
|
||||
"model": model,
|
||||
"credential": AzureCliCredential(),
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
# Executors for non-AI activities (defined at module level)
|
||||
class SpamHandlerExecutor(Executor):
|
||||
@@ -165,15 +164,17 @@ def is_spam_detected(message: Any) -> bool:
|
||||
def _create_workflow() -> Workflow:
|
||||
"""Create the workflow definition."""
|
||||
client_kwargs = _build_client_kwargs()
|
||||
chat_client = AzureOpenAIChatClient(**client_kwargs)
|
||||
chat_client = FoundryChatClient(**client_kwargs)
|
||||
|
||||
spam_agent = chat_client.as_agent(
|
||||
spam_agent = Agent(
|
||||
client=chat_client,
|
||||
name=SPAM_AGENT_NAME,
|
||||
instructions=SPAM_DETECTION_INSTRUCTIONS,
|
||||
default_options={"response_format": SpamDetectionResult},
|
||||
)
|
||||
|
||||
email_agent = chat_client.as_agent(
|
||||
email_agent = Agent(
|
||||
client=chat_client,
|
||||
name=EMAIL_AGENT_NAME,
|
||||
instructions=EMAIL_ASSISTANT_INSTRUCTIONS,
|
||||
default_options={"response_format": EmailResponse},
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "https://<your-resource-name>.openai.azure.com/",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<your-deployment-name>",
|
||||
"AZURE_OPENAI_API_KEY": "<your-api-key>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -1,3 +1,15 @@
|
||||
agent-framework-azurefunctions
|
||||
agent-framework
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
azure-identity
|
||||
|
||||
@@ -10,5 +10,4 @@ TASKHUB_NAME=default
|
||||
|
||||
# Azure OpenAI Configuration
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
|
||||
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=your-deployment-name
|
||||
AZURE_OPENAI_API_KEY=your-api-key
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME=your-deployment-name
|
||||
|
||||
@@ -98,7 +98,8 @@ The sample can run locally without Azure Functions infrastructure using DevUI:
|
||||
cp .env.template .env
|
||||
```
|
||||
|
||||
2. Configure `.env` with your Azure OpenAI credentials
|
||||
2. Configure `.env` with your Azure OpenAI credentials (`AZURE_OPENAI_ENDPOINT` and
|
||||
`AZURE_OPENAI_DEPLOYMENT_NAME`)
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
|
||||
@@ -19,6 +19,11 @@ Key architectural points:
|
||||
- Different agents run in parallel when they're in the same iteration
|
||||
- Activities (executors) also run in parallel when pending together
|
||||
- Mixed agent/executor fan-outs execute concurrently
|
||||
|
||||
Prerequisites:
|
||||
- Configure `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_DEPLOYMENT_NAME`
|
||||
- Sign in with Azure CLI (`az login`) for `AzureCliCredential`
|
||||
- Ensure Azurite and the Durable Task Scheduler emulator are running
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -28,6 +33,7 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorResponse,
|
||||
Executor,
|
||||
Workflow,
|
||||
@@ -36,18 +42,14 @@ from agent_framework import (
|
||||
executor,
|
||||
handler,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework_azurefunctions import AgentFunctionApp
|
||||
from azure.identity import AzureCliCredential
|
||||
from agent_framework.azure import AgentFunctionApp
|
||||
from agent_framework.openai import OpenAIChatCompletionClient
|
||||
from azure.identity.aio import AzureCliCredential, get_bearer_token_provider
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Never
|
||||
|
||||
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"
|
||||
|
||||
# Agent names
|
||||
SENTIMENT_AGENT_NAME = "SentimentAnalysisAgent"
|
||||
KEYWORD_AGENT_NAME = "KeywordExtractionAgent"
|
||||
@@ -334,30 +336,6 @@ class MixedResultCollector(Executor):
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def _build_client_kwargs() -> dict[str, Any]:
|
||||
"""Build Azure OpenAI client kwargs from environment variables."""
|
||||
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.")
|
||||
|
||||
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_workflow() -> Workflow:
|
||||
"""Create the parallel workflow definition.
|
||||
|
||||
@@ -381,11 +359,16 @@ def _create_workflow() -> Workflow:
|
||||
└─> statistics_processor ─┤
|
||||
└──> final_report
|
||||
"""
|
||||
client_kwargs = _build_client_kwargs()
|
||||
chat_client = AzureOpenAIChatClient(**client_kwargs)
|
||||
credential = AzureCliCredential()
|
||||
|
||||
chat_client = OpenAIChatCompletionClient(
|
||||
model=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
|
||||
api_key=get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default"),
|
||||
)
|
||||
|
||||
# Create agents for parallel analysis
|
||||
sentiment_agent = chat_client.as_agent(
|
||||
sentiment_agent = Agent(
|
||||
client=chat_client,
|
||||
name=SENTIMENT_AGENT_NAME,
|
||||
instructions=(
|
||||
"You are a sentiment analysis expert. Analyze the sentiment of the given text. "
|
||||
@@ -395,7 +378,8 @@ def _create_workflow() -> Workflow:
|
||||
default_options={"response_format": SentimentResult},
|
||||
)
|
||||
|
||||
keyword_agent = chat_client.as_agent(
|
||||
keyword_agent = Agent(
|
||||
client=chat_client,
|
||||
name=KEYWORD_AGENT_NAME,
|
||||
instructions=(
|
||||
"You are a keyword extraction expert. Extract important keywords and categories "
|
||||
@@ -406,7 +390,8 @@ def _create_workflow() -> Workflow:
|
||||
)
|
||||
|
||||
# Create summary agent for Pattern 3 (mixed parallel)
|
||||
summary_agent = chat_client.as_agent(
|
||||
summary_agent = Agent(
|
||||
client=chat_client,
|
||||
name=SUMMARY_AGENT_NAME,
|
||||
instructions=(
|
||||
"You are a summarization expert. Given analysis results (sentiment and keywords), "
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "https://<your-resource-name>.openai.azure.com/",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<your-deployment-name>",
|
||||
"AZURE_OPENAI_API_KEY": "<your-api-key>"
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_DEPLOYMENT_NAME": "<AZURE_OPENAI_DEPLOYMENT_NAME>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
agent-framework-azurefunctions
|
||||
agent-framework
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-openai
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/openai # OpenAI support - dependency for Azure OpenAI chat samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
azure-identity
|
||||
|
||||
@@ -88,12 +88,12 @@ When running on Durable Functions, the HITL pattern maps to:
|
||||
cp local.settings.json.sample local.settings.json
|
||||
```
|
||||
|
||||
2. Update `local.settings.json` with your Azure OpenAI credentials:
|
||||
2. Update `local.settings.json` with your Foundry project settings:
|
||||
```json
|
||||
{
|
||||
"Values": {
|
||||
"AZURE_OPENAI_ENDPOINT": "https://your-resource.openai.azure.com/",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "gpt-4o"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "https://your-project.services.ai.azure.com/api/projects/your-project",
|
||||
"FOUNDRY_MODEL": "gpt-4o"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -18,7 +18,7 @@ Key architectural points:
|
||||
- Durable Functions provides durability while waiting for human input
|
||||
|
||||
Prerequisites:
|
||||
- Azure OpenAI configured with required environment variables
|
||||
- Configure `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`
|
||||
- Durable Task Scheduler connection string
|
||||
- Authentication via Azure CLI (az login)
|
||||
"""
|
||||
@@ -30,6 +30,7 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
Executor,
|
||||
@@ -40,18 +41,17 @@ from agent_framework import (
|
||||
handler,
|
||||
response_handler,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework_azurefunctions import AgentFunctionApp
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from typing_extensions import Never
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Environment variable names
|
||||
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"
|
||||
FOUNDRY_PROJECT_ENDPOINT_ENV = "FOUNDRY_PROJECT_ENDPOINT"
|
||||
AZURE_OPENAI_DEPLOYMENT_ENV = "FOUNDRY_MODEL"
|
||||
|
||||
# Agent names
|
||||
CONTENT_ANALYZER_AGENT_NAME = "ContentAnalyzerAgent"
|
||||
@@ -307,28 +307,21 @@ class PublishExecutor(Executor):
|
||||
|
||||
|
||||
def _build_client_kwargs() -> dict[str, Any]:
|
||||
"""Build Azure OpenAI client configuration from environment variables."""
|
||||
endpoint = os.getenv(AZURE_OPENAI_ENDPOINT_ENV)
|
||||
if not endpoint:
|
||||
raise RuntimeError(f"{AZURE_OPENAI_ENDPOINT_ENV} environment variable is required.")
|
||||
"""Build Foundry chat client configuration from environment variables."""
|
||||
project_endpoint = os.getenv(FOUNDRY_PROJECT_ENDPOINT_ENV)
|
||||
if not project_endpoint:
|
||||
raise RuntimeError(f"{FOUNDRY_PROJECT_ENDPOINT_ENV} environment variable is required.")
|
||||
|
||||
deployment = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
|
||||
if not deployment:
|
||||
model = os.getenv(AZURE_OPENAI_DEPLOYMENT_ENV)
|
||||
if not model:
|
||||
raise RuntimeError(f"{AZURE_OPENAI_DEPLOYMENT_ENV} environment variable is required.")
|
||||
|
||||
client_kwargs: dict[str, Any] = {
|
||||
"endpoint": endpoint,
|
||||
"deployment_name": deployment,
|
||||
return {
|
||||
"project_endpoint": project_endpoint,
|
||||
"model": model,
|
||||
"credential": AzureCliCredential(),
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
class InputRouterExecutor(Executor):
|
||||
"""Routes incoming content submission to the analysis agent."""
|
||||
@@ -379,10 +372,11 @@ class InputRouterExecutor(Executor):
|
||||
def _create_workflow() -> Workflow:
|
||||
"""Create the content moderation workflow with HITL."""
|
||||
client_kwargs = _build_client_kwargs()
|
||||
chat_client = AzureOpenAIChatClient(**client_kwargs)
|
||||
chat_client = FoundryChatClient(**client_kwargs)
|
||||
|
||||
# Create the content analysis agent
|
||||
content_analyzer_agent = chat_client.as_agent(
|
||||
content_analyzer_agent = Agent(
|
||||
client=chat_client,
|
||||
name=CONTENT_ANALYZER_AGENT_NAME,
|
||||
instructions=CONTENT_ANALYZER_INSTRUCTIONS,
|
||||
default_options={"response_format": ContentAnalysisResult},
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "python",
|
||||
"AZURE_OPENAI_ENDPOINT": "<Your Azure OpenAI endpoint>",
|
||||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME": "<Your Azure OpenAI chat deployment name>"
|
||||
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
|
||||
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,15 @@
|
||||
agent-framework-azurefunctions
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-azurefunctions
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - dependency of azurefunctions
|
||||
-e ../../../../packages/azurefunctions # Azure Functions integration - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
azure-identity
|
||||
|
||||
@@ -11,7 +11,7 @@ All of these samples are set up to run in Azure Functions. Azure Functions has a
|
||||
|
||||
- Install [Azurite storage emulator](https://learn.microsoft.com/en-us/azure/storage/common/storage-install-azurite?toc=%2Fazure%2Fstorage%2Fblobs%2Ftoc.json&bc=%2Fazure%2Fstorage%2Fblobs%2Fbreadcrumb%2Ftoc.json&tabs=visual-studio%2Cblob-storage)
|
||||
|
||||
- Create an [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-foundry/models/openai) resource. Note the Azure OpenAI endpoint, deployment name, and the key (or ensure you can authenticate with `AzureCliCredential`).
|
||||
- Create an [Azure AI Foundry project](https://learn.microsoft.com/azure/ai-foundry/) with an OpenAI model deployment. Note the Foundry project endpoint and deployment name, and ensure you can authenticate with `AzureCliCredential`.
|
||||
|
||||
- Install a tool to execute HTTP calls, for example the [REST Client extension](https://marketplace.visualstudio.com/items?itemName=humao.rest-client)
|
||||
|
||||
@@ -39,8 +39,7 @@ source .venv/bin/activate
|
||||
|
||||
- Install Python dependencies – from the sample directory, run `pip install -r requirements.txt` (or the equivalent in your active virtual environment).
|
||||
|
||||
- Copy `local.settings.json.template` to `local.settings.json`, then update `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME` for Azure OpenAI authentication. The samples use `AzureCliCredential` by default, so ensure you're logged in via `az login`.
|
||||
- Alternatively, you can use API key authentication by setting `AZURE_OPENAI_API_KEY` and updating the code to use `AzureOpenAIChatClient()` without the credential parameter.
|
||||
- Copy `local.settings.json.template` to `local.settings.json`, then update `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`. The samples use `AzureCliCredential`, so ensure you're logged in via `az login`.
|
||||
- Keep `TASKHUB_NAME` set to `default` unless you plan to change the durable task hub name.
|
||||
|
||||
- Run the command `func start` from the root of the sample
|
||||
|
||||
@@ -7,8 +7,8 @@ registered agents, demonstrating how to interact with agents from external proce
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with the agent registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -17,7 +17,7 @@ import logging
|
||||
import os
|
||||
|
||||
from agent_framework.azure import DurableAIAgentClient
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
|
||||
@@ -48,7 +48,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
dts_client = DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Single Agent Sample - Durable Task Integration (Combined Worker + Client)
|
||||
|
||||
This sample demonstrates running both the worker and client in a single process.
|
||||
The worker is started first to register the agent, then client operations are
|
||||
performed against the running worker.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
|
||||
To run this sample:
|
||||
python sample.py
|
||||
"""
|
||||
@@ -30,27 +27,21 @@ logger = logging.getLogger(__name__)
|
||||
def main():
|
||||
"""Main entry point - runs both worker and client in single process."""
|
||||
logger.debug("Starting Durable Task Agent Sample (Combined Worker + Client)...")
|
||||
|
||||
silent_handler = logging.NullHandler()
|
||||
|
||||
# Create and start the worker using helper function and context manager
|
||||
with get_worker(log_handler=silent_handler) as dts_worker:
|
||||
# Register agents using helper function
|
||||
setup_worker(dts_worker)
|
||||
|
||||
# Start the worker
|
||||
dts_worker.start()
|
||||
logger.debug("Worker started and listening for requests...")
|
||||
|
||||
# Create the client using helper function
|
||||
agent_client = get_client(log_handler=silent_handler)
|
||||
|
||||
try:
|
||||
# Run client interactions using helper function
|
||||
run_client(agent_client)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error during agent interaction: {e}")
|
||||
|
||||
logger.debug("Sample completed. Worker shutting down...")
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ This worker registers agents as durable entities and continuously listens for re
|
||||
The worker should run as a background service, processing incoming agent requests.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
@@ -16,8 +16,10 @@ import logging
|
||||
import os
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentWorker
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework.azure import DurableAIAgentWorker
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
|
||||
@@ -35,7 +37,12 @@ def create_joker_agent() -> Agent:
|
||||
Returns:
|
||||
Agent: The configured Joker agent
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
return Agent(
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
),
|
||||
name="Joker",
|
||||
instructions="You are good at telling jokes.",
|
||||
)
|
||||
@@ -60,7 +67,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -8,8 +8,8 @@ each with their own specialized capabilities and tools.
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with both agents registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -18,7 +18,7 @@ import logging
|
||||
import os
|
||||
|
||||
from agent_framework.azure import DurableAIAgentClient
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
|
||||
@@ -49,7 +49,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
dts_client = DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Multi-Agent Sample - Durable Task Integration (Combined Worker + Client)
|
||||
|
||||
This sample demonstrates running both the worker and client in a single process
|
||||
for multiple agents with different tools. The worker registers two agents
|
||||
(WeatherAgent and MathAgent), each with their own specialized capabilities.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
|
||||
To run this sample:
|
||||
python sample.py
|
||||
"""
|
||||
@@ -30,26 +27,21 @@ logger = logging.getLogger(__name__)
|
||||
def main():
|
||||
"""Main entry point - runs both worker and client in single process."""
|
||||
logger.debug("Starting Durable Task Multi-Agent Sample (Combined Worker + Client)...")
|
||||
|
||||
silent_handler = logging.NullHandler()
|
||||
# Create and start the worker using helper function and context manager
|
||||
with get_worker(log_handler=silent_handler) as dts_worker:
|
||||
# Register agents using helper function
|
||||
setup_worker(dts_worker)
|
||||
|
||||
# Start the worker
|
||||
dts_worker.start()
|
||||
logger.debug("Worker started and listening for requests...")
|
||||
|
||||
# Create the client using helper function
|
||||
agent_client = get_client(log_handler=silent_handler)
|
||||
|
||||
try:
|
||||
# Run client interactions using helper function
|
||||
run_client(agent_client)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error during agent interaction: {e}")
|
||||
|
||||
logger.debug("Sample completed. Worker shutting down...")
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ with their own specialized tools. This demonstrates how to host multiple agents
|
||||
with different capabilities in a single worker process.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
@@ -17,9 +17,11 @@ import logging
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentWorker
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.azure import DurableAIAgentWorker
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
|
||||
@@ -71,7 +73,13 @@ def create_weather_agent():
|
||||
Returns:
|
||||
Agent: The configured Weather agent with weather tool
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=WEATHER_AGENT_NAME,
|
||||
instructions="You are a helpful weather assistant. Provide current weather information.",
|
||||
tools=[get_weather],
|
||||
@@ -84,7 +92,13 @@ def create_math_agent():
|
||||
Returns:
|
||||
Agent: The configured Math agent with calculation tools
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=MATH_AGENT_NAME,
|
||||
instructions="You are a helpful math assistant. Help users with calculations like tip calculations.",
|
||||
tools=[calculate_tip],
|
||||
@@ -110,7 +124,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -9,7 +9,7 @@ This client demonstrates:
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with the TravelPlanner agent registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Redis must be running
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
@@ -21,7 +21,7 @@ from datetime import timedelta
|
||||
|
||||
import redis.asyncio as aioredis
|
||||
from agent_framework.azure import DurableAIAgentClient
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
from redis_stream_response_handler import RedisStreamResponseHandler
|
||||
@@ -76,7 +76,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
dts_client = DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
azure-identity
|
||||
|
||||
# Redis client
|
||||
redis
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Single Agent Streaming Sample - Durable Task Integration (Combined Worker + Client)
|
||||
|
||||
This sample demonstrates running both the worker and client in a single process
|
||||
with reliable Redis-based streaming for agent responses.
|
||||
|
||||
The worker is started first to register the TravelPlanner agent with Redis streaming
|
||||
callback, then client operations are performed against the running worker.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
- Redis must be running (e.g., docker run -d --name redis -p 6379:6379 redis:latest)
|
||||
|
||||
To run this sample:
|
||||
python sample.py
|
||||
"""
|
||||
@@ -33,27 +29,21 @@ logger = logging.getLogger(__name__)
|
||||
def main():
|
||||
"""Main entry point - runs both worker and client in single process."""
|
||||
logger.debug("Starting Durable Task Agent Sample with Redis Streaming...")
|
||||
|
||||
silent_handler = logging.NullHandler()
|
||||
|
||||
# Create and start the worker using helper function and context manager
|
||||
with get_worker(log_handler=silent_handler) as dts_worker:
|
||||
# Register agents and callbacks using helper function
|
||||
setup_worker(dts_worker)
|
||||
|
||||
# Start the worker
|
||||
dts_worker.start()
|
||||
logger.debug("Worker started and listening for requests...")
|
||||
|
||||
# Create the client using helper function
|
||||
agent_client = get_client(log_handler=silent_handler)
|
||||
|
||||
try:
|
||||
# Run client interactions using helper function
|
||||
run_client(agent_client)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error during agent interaction: {e}")
|
||||
|
||||
logger.debug("Sample completed. Worker shutting down...")
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ This worker registers the TravelPlanner agent with the Durable Task Scheduler
|
||||
and uses RedisStreamCallback to persist streaming responses to Redis for reliable delivery.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
- Start Redis (e.g., docker run -d --name redis -p 6379:6379 redis:latest)
|
||||
"""
|
||||
@@ -22,10 +22,11 @@ from agent_framework import Agent, AgentResponseUpdate
|
||||
from agent_framework.azure import (
|
||||
AgentCallbackContext,
|
||||
AgentResponseCallbackProtocol,
|
||||
AzureOpenAIChatClient,
|
||||
DurableAIAgentWorker,
|
||||
)
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
from redis_stream_response_handler import RedisStreamResponseHandler
|
||||
@@ -153,7 +154,13 @@ def create_travel_agent() -> "Agent":
|
||||
Returns:
|
||||
Agent: The configured TravelPlanner agent with travel planning tools.
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name="TravelPlanner",
|
||||
instructions="""You are an expert travel planner who creates detailed, personalized travel itineraries.
|
||||
When asked to plan a trip, you should:
|
||||
@@ -191,7 +198,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+4
-4
@@ -8,8 +8,8 @@ how conversation context is maintained across multiple agent invocations.
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with the writer agent and orchestration registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -18,7 +18,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
|
||||
# Configure logging
|
||||
@@ -45,7 +45,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
|
||||
+3
-13
@@ -1,22 +1,18 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Single Agent Orchestration Chaining Sample - Durable Task Integration
|
||||
|
||||
This sample demonstrates chaining two invocations of the same agent inside a Durable Task
|
||||
orchestration while preserving the conversation state between runs. The orchestration
|
||||
runs the writer agent sequentially on a shared thread to refine text iteratively.
|
||||
|
||||
Components used:
|
||||
- AzureOpenAIChatClient to construct the writer agent
|
||||
- FoundryChatClient to construct the writer agent
|
||||
- DurableTaskSchedulerWorker and DurableAIAgentWorker for agent hosting
|
||||
- DurableTaskSchedulerClient and orchestration for sequential agent invocations
|
||||
- Thread management to maintain conversation context across invocations
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker emulator)
|
||||
|
||||
To run this sample:
|
||||
python sample.py
|
||||
"""
|
||||
@@ -36,22 +32,17 @@ logger = logging.getLogger(__name__)
|
||||
def main():
|
||||
"""Main entry point - runs both worker and client in single process."""
|
||||
logger.debug("Starting Single Agent Orchestration Chaining Sample...")
|
||||
|
||||
silent_handler = logging.NullHandler()
|
||||
# Create and start the worker using helper function and context manager
|
||||
with get_worker(log_handler=silent_handler) as dts_worker:
|
||||
# Register agents and orchestrations using helper function
|
||||
setup_worker(dts_worker)
|
||||
|
||||
# Start the worker
|
||||
dts_worker.start()
|
||||
logger.debug("Worker started and listening for requests...")
|
||||
|
||||
# Create the client using helper function
|
||||
client = get_client(log_handler=silent_handler)
|
||||
|
||||
logger.debug("CLIENT: Starting orchestration...")
|
||||
|
||||
# Run the client in the same process
|
||||
try:
|
||||
run_client(client)
|
||||
@@ -61,7 +52,6 @@ def main():
|
||||
logger.exception(f"Error during orchestration: {e}")
|
||||
finally:
|
||||
logger.debug("Worker stopping...")
|
||||
|
||||
logger.debug("")
|
||||
logger.debug("Sample completed")
|
||||
|
||||
|
||||
+14
-6
@@ -7,8 +7,8 @@ chaining behavior by running the agent twice sequentially on the same thread,
|
||||
preserving conversation context between invocations.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
@@ -18,8 +18,10 @@ import os
|
||||
from collections.abc import Generator
|
||||
|
||||
from agent_framework import Agent, AgentResponse
|
||||
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework.azure import DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
from durabletask.task import OrchestrationContext, Task
|
||||
@@ -49,7 +51,13 @@ def create_writer_agent() -> "Agent":
|
||||
"when given an improved sentence you polish it further."
|
||||
)
|
||||
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=WRITER_AGENT_NAME,
|
||||
instructions=instructions,
|
||||
)
|
||||
@@ -139,7 +147,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+4
-4
@@ -8,8 +8,8 @@ displays the aggregated results.
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with both agents and orchestration registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -18,7 +18,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
|
||||
# Configure logging
|
||||
@@ -45,7 +45,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
|
||||
+2
-12
@@ -1,19 +1,15 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Multi-Agent Orchestration Sample - Durable Task Integration (Combined Worker + Client)
|
||||
|
||||
This sample demonstrates running both the worker and client in a single process for
|
||||
concurrent multi-agent orchestration. The worker registers two domain-specific agents
|
||||
(physicist and chemist) and an orchestration function that runs them in parallel.
|
||||
|
||||
The orchestration uses OrchestrationAgentExecutor to execute agents concurrently
|
||||
and aggregate their responses.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
|
||||
To run this sample:
|
||||
python sample.py
|
||||
"""
|
||||
@@ -33,30 +29,24 @@ logger = logging.getLogger(__name__)
|
||||
def main():
|
||||
"""Main entry point - runs both worker and client in single process."""
|
||||
logger.debug("Starting Durable Task Multi-Agent Orchestration Sample (Combined Worker + Client)...")
|
||||
|
||||
silent_handler = logging.NullHandler()
|
||||
# Create and start the worker using helper function and context manager
|
||||
with get_worker(log_handler=silent_handler) as dts_worker:
|
||||
# Register agents and orchestrations using helper function
|
||||
setup_worker(dts_worker)
|
||||
|
||||
# Start the worker
|
||||
dts_worker.start()
|
||||
logger.debug("Worker started and listening for requests...")
|
||||
|
||||
# Create the client using helper function
|
||||
client = get_client(log_handler=silent_handler)
|
||||
|
||||
# Define the prompt
|
||||
prompt = "What is temperature?"
|
||||
logger.debug("CLIENT: Starting orchestration...")
|
||||
|
||||
try:
|
||||
# Run the client to start the orchestration
|
||||
run_client(client, prompt)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error during sample execution: {e}")
|
||||
|
||||
logger.debug("Sample completed. Worker shutting down...")
|
||||
|
||||
|
||||
|
||||
+21
-7
@@ -7,8 +7,8 @@ function that runs them concurrently. The orchestration uses OrchestrationAgentE
|
||||
to execute agents in parallel and aggregate their responses.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
@@ -19,8 +19,10 @@ from collections.abc import Generator
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, AgentResponse
|
||||
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework.azure import DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
from durabletask.task import OrchestrationContext, Task, when_all
|
||||
@@ -43,7 +45,13 @@ def create_physicist_agent() -> "Agent":
|
||||
Returns:
|
||||
Agent: The configured Physicist agent
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=PHYSICIST_AGENT_NAME,
|
||||
instructions="You are an expert in physics. You answer questions from a physics perspective.",
|
||||
)
|
||||
@@ -55,7 +63,13 @@ def create_chemist_agent() -> "Agent":
|
||||
Returns:
|
||||
Agent: The configured Chemist agent
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=CHEMIST_AGENT_NAME,
|
||||
instructions="You are an expert in chemistry. You answer questions from a chemistry perspective.",
|
||||
)
|
||||
@@ -138,7 +152,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+5
@@ -14,6 +14,11 @@ This sample demonstrates conditional orchestration logic with two agents that an
|
||||
|
||||
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.
|
||||
|
||||
This sample uses Azure OpenAI credentials:
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`
|
||||
|
||||
## Running the Sample
|
||||
|
||||
With the environment setup, you can run the sample using the combined approach or separate worker and client processes:
|
||||
|
||||
+4
-4
@@ -7,8 +7,8 @@ that uses conditional logic to either handle spam emails or draft professional r
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with both agents, orchestration, and activities registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -16,7 +16,7 @@ import asyncio
|
||||
import logging
|
||||
import os
|
||||
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
|
||||
# Configure logging
|
||||
@@ -43,7 +43,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-openai
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/openai # OpenAI support - dependency for Azure OpenAI chat samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ The orchestration branches based on spam detection results, calling different
|
||||
activity functions to handle spam or send legitimate email responses.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
|
||||
To run this sample:
|
||||
|
||||
+22
-7
@@ -7,8 +7,8 @@ orchestration function that routes execution based on spam detection results. Ac
|
||||
handle side effects (spam handling and email sending).
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
@@ -19,8 +19,11 @@ from collections.abc import Generator
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import Agent, AgentResponse
|
||||
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework.azure import DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from agent_framework.openai import OpenAIChatCompletionClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from azure.identity.aio import get_bearer_token_provider as get_async_bearer_token_provider
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
from durabletask.task import ActivityContext, OrchestrationContext, Task
|
||||
@@ -64,7 +67,13 @@ def create_spam_agent() -> "Agent":
|
||||
Returns:
|
||||
Agent: The configured Spam Detection agent
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
return Agent(
|
||||
client=OpenAIChatCompletionClient(
|
||||
model=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
|
||||
api_key=get_async_bearer_token_provider(
|
||||
AsyncAzureCliCredential(), "https://cognitiveservices.azure.com/.default"
|
||||
),
|
||||
),
|
||||
name=SPAM_AGENT_NAME,
|
||||
instructions="You are a spam detection assistant that identifies spam emails.",
|
||||
)
|
||||
@@ -76,7 +85,13 @@ def create_email_agent() -> "Agent":
|
||||
Returns:
|
||||
Agent: The configured Email Assistant agent
|
||||
"""
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
return Agent(
|
||||
client=OpenAIChatCompletionClient(
|
||||
model=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
|
||||
api_key=get_async_bearer_token_provider(
|
||||
AsyncAzureCliCredential(), "https://cognitiveservices.azure.com/.default"
|
||||
),
|
||||
),
|
||||
name=EMAIL_AGENT_NAME,
|
||||
instructions="You are an email assistant that helps users draft responses to emails with professionalism.",
|
||||
)
|
||||
@@ -220,7 +235,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -7,8 +7,8 @@ by starting an orchestration, sending approval/rejection events, and monitoring
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with the agent, orchestration, and activities registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -18,7 +18,7 @@ import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
|
||||
from durabletask.client import OrchestrationState
|
||||
|
||||
@@ -49,7 +49,7 @@ def get_client(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerClient(
|
||||
host_address=endpoint_url,
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
# Agent Framework packages
|
||||
# To use the deployed version, uncomment the line below and comment out the local installation lines
|
||||
# To use the deployed version, uncomment the lines below and comment out the local installation lines
|
||||
# agent-framework-foundry
|
||||
# agent-framework-durabletask
|
||||
|
||||
# Local installation (for development and testing)
|
||||
# Each package must be listed explicitly because pip doesn't resolve uv workspace sources.
|
||||
# Without explicit entries, pip would fetch transitive dependencies from PyPI instead of local source.
|
||||
-e ../../../../packages/core # Core framework - base dependency for all packages
|
||||
-e ../../../../packages/foundry # Foundry support - dependency for hosted chat/agent samples
|
||||
-e ../../../../packages/durabletask # Durable Task support - the main package for this sample
|
||||
|
||||
# Azure authentication
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Human-in-the-Loop Orchestration Sample - Durable Task Integration
|
||||
|
||||
This sample demonstrates the HITL pattern with a WriterAgent that generates content
|
||||
and waits for human approval. The orchestration handles:
|
||||
- External event waiting (approval/rejection)
|
||||
- Timeout handling
|
||||
- Iterative refinement based on feedback
|
||||
- Activity functions for notifications and publishing
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
|
||||
To run this sample:
|
||||
python sample.py
|
||||
"""
|
||||
@@ -32,28 +29,21 @@ logger = logging.getLogger()
|
||||
def main():
|
||||
"""Main entry point - runs both worker and client in single process."""
|
||||
logger.debug("Starting Durable Task HITL Content Generation Sample (Combined Worker + Client)...")
|
||||
|
||||
silent_handler = logging.NullHandler()
|
||||
# Create and start the worker using helper function and context manager
|
||||
with get_worker(log_handler=silent_handler) as dts_worker:
|
||||
# Register agent, orchestration, and activities using helper function
|
||||
setup_worker(dts_worker)
|
||||
|
||||
# Start the worker
|
||||
dts_worker.start()
|
||||
logger.debug("Worker started and listening for requests...")
|
||||
|
||||
# Create the client using helper function
|
||||
client = get_client(log_handler=silent_handler)
|
||||
|
||||
try:
|
||||
logger.debug("CLIENT: Starting orchestration tests...")
|
||||
|
||||
run_interactive_client(client)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error during sample execution: {e}")
|
||||
|
||||
logger.debug("Sample completed. Worker shutting down...")
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ a human-in-the-loop review workflow. The orchestration pauses for external event
|
||||
(human approval/rejection) with timeout handling, and iterates based on feedback.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
|
||||
(plus AZURE_OPENAI_API_KEY or Azure CLI authentication)
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
@@ -20,8 +20,10 @@ from datetime import timedelta
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import Agent, AgentResponse
|
||||
from agent_framework.azure import AzureOpenAIChatClient, DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from azure.identity import AzureCliCredential, DefaultAzureCredential
|
||||
from agent_framework.azure import DurableAIAgentOrchestrationContext, DurableAIAgentWorker
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
|
||||
from durabletask.task import ActivityContext, OrchestrationContext, Task, when_any # type: ignore
|
||||
@@ -74,7 +76,13 @@ def create_writer_agent() -> "Agent":
|
||||
"Limit response to 300 words or less."
|
||||
)
|
||||
|
||||
return AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
|
||||
_client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AsyncAzureCliCredential(),
|
||||
)
|
||||
return Agent(
|
||||
client=_client,
|
||||
name=WRITER_AGENT_NAME,
|
||||
instructions=instructions,
|
||||
)
|
||||
@@ -298,7 +306,7 @@ def get_worker(
|
||||
logger.debug(f"Using taskhub: {taskhub_name}")
|
||||
logger.debug(f"Using endpoint: {endpoint_url}")
|
||||
|
||||
credential = None if endpoint_url == "http://localhost:8080" else DefaultAzureCredential()
|
||||
credential = None if endpoint_url == "http://localhost:8080" else AzureCliCredential()
|
||||
|
||||
return DurableTaskSchedulerWorker(
|
||||
host_address=endpoint_url,
|
||||
|
||||
@@ -55,22 +55,6 @@ az role assignment create `
|
||||
|
||||
More information on how to configure RBAC permissions for Azure OpenAI can be found in the [Azure OpenAI documentation](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=cli).
|
||||
|
||||
### Setting an API key for the Azure OpenAI service
|
||||
|
||||
As an alternative to configuring Azure RBAC permissions, you can set an API key for the Azure OpenAI service by setting the `AZURE_OPENAI_API_KEY` environment variable.
|
||||
|
||||
Bash (Linux/macOS/WSL):
|
||||
|
||||
```bash
|
||||
export AZURE_OPENAI_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_OPENAI_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
### Start Durable Task Scheduler
|
||||
|
||||
Most samples use the Durable Task Scheduler (DTS) to support hosted agents and durable orchestrations. DTS also allows you to view the status of orchestrations and their inputs and outputs from a web UI.
|
||||
@@ -90,15 +74,15 @@ Each sample reads configuration from environment variables. You'll need to set t
|
||||
Bash (Linux/macOS/WSL):
|
||||
|
||||
```bash
|
||||
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
|
||||
export AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="your-deployment-name"
|
||||
export FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project"
|
||||
export FOUNDRY_MODEL="your-deployment-name"
|
||||
```
|
||||
|
||||
PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
|
||||
$env:AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="your-deployment-name"
|
||||
$env:FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project"
|
||||
$env:FOUNDRY_MODEL="your-deployment-name"
|
||||
```
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
Reference in New Issue
Block a user