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
@@ -46,6 +46,7 @@ from _tools import (
|
||||
validate_payment_method,
|
||||
)
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorResponse,
|
||||
AgentResponseUpdate,
|
||||
Executor,
|
||||
@@ -56,7 +57,7 @@ from agent_framework import (
|
||||
executor,
|
||||
handler,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.identity.aio import DefaultAzureCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -74,9 +75,10 @@ async def start_executor(input: str, ctx: WorkflowContext[list[Message]]) -> Non
|
||||
class ResearchLead(Executor):
|
||||
"""Aggregates and summarizes travel planning findings from all specialized agents."""
|
||||
|
||||
def __init__(self, client: AzureOpenAIResponsesClient, id: str = "travel-planning-coordinator"):
|
||||
def __init__(self, client: FoundryChatClient, id: str = "travel-planning-coordinator"):
|
||||
# Use default_options to persist conversation history for evaluation.
|
||||
self.agent = client.as_agent(
|
||||
self.agent = Agent(
|
||||
client=client,
|
||||
id="travel-planning-coordinator",
|
||||
instructions=(
|
||||
"You are the final coordinator. You will receive responses from multiple agents: "
|
||||
@@ -143,13 +145,13 @@ class ResearchLead(Executor):
|
||||
|
||||
|
||||
async def run_workflow_with_response_tracking(
|
||||
query: str, client: AzureOpenAIResponsesClient | None = None, deployment_name: str | None = None
|
||||
query: str, client: FoundryChatClient | None = None, deployment_name: str | None = None
|
||||
) -> dict:
|
||||
"""Run multi-agent workflow and track conversation IDs, response IDs, and interaction sequence.
|
||||
|
||||
Args:
|
||||
query: The user query to process through the multi-agent workflow
|
||||
client: Optional AzureOpenAIResponsesClient instance
|
||||
client: Optional FoundryChatClient instance
|
||||
deployment_name: Optional model deployment name for the workflow agents
|
||||
|
||||
Returns:
|
||||
@@ -159,12 +161,12 @@ async def run_workflow_with_response_tracking(
|
||||
try:
|
||||
async with DefaultAzureCredential() as credential:
|
||||
project_client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
async with project_client:
|
||||
client = AzureOpenAIResponsesClient(project_client=project_client, deployment_name=deployment_name)
|
||||
client = FoundryChatClient(project_client=project_client, model=deployment_name)
|
||||
return await _run_workflow_with_client(query, client)
|
||||
except Exception as e:
|
||||
print(f"Error during workflow execution: {e}")
|
||||
@@ -173,7 +175,7 @@ async def run_workflow_with_response_tracking(
|
||||
return await _run_workflow_with_client(query, client)
|
||||
|
||||
|
||||
async def _run_workflow_with_client(query: str, client: AzureOpenAIResponsesClient) -> dict:
|
||||
async def _run_workflow_with_client(query: str, client: FoundryChatClient) -> dict:
|
||||
"""Execute workflow with given client and track all interactions."""
|
||||
|
||||
# Initialize tracking variables - use lists to track multiple responses per agent
|
||||
@@ -205,16 +207,17 @@ async def _run_workflow_with_client(query: str, client: AzureOpenAIResponsesClie
|
||||
}
|
||||
|
||||
|
||||
async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
async def _create_workflow(client: FoundryChatClient):
|
||||
"""Create the multi-agent travel planning workflow with specialized agents.
|
||||
|
||||
Uses a single shared AzureOpenAIResponsesClient for all agents.
|
||||
Uses a single shared FoundryChatClient for all agents.
|
||||
"""
|
||||
|
||||
final_coordinator = ResearchLead(client=client, id="final-coordinator")
|
||||
|
||||
# Agent 1: Travel Request Handler (initial coordinator)
|
||||
travel_request_handler = client.as_agent(
|
||||
travel_request_handler = Agent(
|
||||
client=client,
|
||||
id="travel-request-handler",
|
||||
instructions=(
|
||||
"You receive user travel queries and relay them to specialized agents. Extract key information: destination, dates, budget, and preferences. Pass this information forward clearly to the next agents."
|
||||
@@ -223,7 +226,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 2: Hotel Search Executor
|
||||
hotel_search_agent = client.as_agent(
|
||||
hotel_search_agent = Agent(
|
||||
client=client,
|
||||
id="hotel-search-agent",
|
||||
instructions=(
|
||||
"You are a hotel search specialist. Your task is ONLY to search for and provide hotel information. Use search_hotels to find options, get_hotel_details for specifics, and check_availability to verify rooms. Output format: List hotel names, prices per night, total cost for the stay, locations, ratings, amenities, and addresses. IMPORTANT: Only provide hotel information without additional commentary."
|
||||
@@ -233,7 +237,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 3: Flight Search Executor
|
||||
flight_search_agent = client.as_agent(
|
||||
flight_search_agent = Agent(
|
||||
client=client,
|
||||
id="flight-search-agent",
|
||||
instructions=(
|
||||
"You are a flight search specialist. Your task is ONLY to search for and provide flight information. Use search_flights to find options, get_flight_details for specifics, and check_availability for seats. Output format: List flight numbers, airlines, departure/arrival times, prices, durations, and cabin class. IMPORTANT: Only provide flight information without additional commentary."
|
||||
@@ -243,7 +248,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 4: Activity Search Executor
|
||||
activity_search_agent = client.as_agent(
|
||||
activity_search_agent = Agent(
|
||||
client=client,
|
||||
id="activity-search-agent",
|
||||
instructions=(
|
||||
"You are an activities specialist. Your task is ONLY to search for and provide activity information. Use search_activities to find options for activities. Output format: List activity names, descriptions, prices, durations, ratings, and categories. IMPORTANT: Only provide activity information without additional commentary."
|
||||
@@ -253,7 +259,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 5: Booking Confirmation Executor
|
||||
booking_confirmation_agent = client.as_agent(
|
||||
booking_confirmation_agent = Agent(
|
||||
client=client,
|
||||
id="booking-confirmation-agent",
|
||||
instructions=(
|
||||
"You confirm bookings. Use check_hotel_availability and check_flight_availability to verify slots, then confirm_booking to finalize. Provide ONLY: confirmation numbers, booking references, and confirmation status."
|
||||
@@ -263,7 +270,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 6: Booking Payment Executor
|
||||
booking_payment_agent = client.as_agent(
|
||||
booking_payment_agent = Agent(
|
||||
client=client,
|
||||
id="booking-payment-agent",
|
||||
instructions=(
|
||||
"You process payments. Use validate_payment_method to verify payment, then process_payment to complete transactions. Provide ONLY: payment confirmation status, transaction IDs, and payment amounts."
|
||||
@@ -273,7 +281,8 @@ async def _create_workflow(client: AzureOpenAIResponsesClient):
|
||||
)
|
||||
|
||||
# Agent 7: Booking Information Aggregation Executor
|
||||
booking_info_aggregation_agent = client.as_agent(
|
||||
booking_info_aggregation_agent = Agent(
|
||||
client=client,
|
||||
id="booking-info-aggregation-agent",
|
||||
instructions=(
|
||||
"You aggregate hotel and flight search results. Receive options from search agents and organize them. Provide: top 2-3 hotel options with prices and top 2-3 flight options with prices in a structured format."
|
||||
@@ -356,7 +365,7 @@ async def create_and_run_workflow(deployment_name: str | None = None):
|
||||
query = example_queries[0]
|
||||
print(f"Query: {query}\n")
|
||||
|
||||
result = await run_workflow_with_response_tracking(query, deployment_name=deployment_name)
|
||||
result = await run_workflow_with_response_tracking(query, model=deployment_name)
|
||||
|
||||
# Create output data structure
|
||||
output_data = {"agents": {}, "query": result["query"], "output": result.get("output", "")}
|
||||
|
||||
@@ -9,7 +9,7 @@ import time
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from azure.ai.projects import AIProjectClient
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.identity import AzureCliCredential
|
||||
from create_workflow import create_and_run_workflow
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -33,8 +33,8 @@ This script:
|
||||
|
||||
def create_openai_client() -> OpenAI:
|
||||
project_client = AIProjectClient(
|
||||
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
credential=DefaultAzureCredential(),
|
||||
endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
return project_client.get_openai_client()
|
||||
|
||||
@@ -58,7 +58,7 @@ async def run_workflow(deployment_name: str | None = None) -> dict[str, Any]:
|
||||
print("Executing multi-agent travel planning workflow...")
|
||||
print("This may take a few minutes...")
|
||||
|
||||
workflow_data = await create_and_run_workflow(deployment_name=deployment_name)
|
||||
workflow_data = await create_and_run_workflow(model=deployment_name)
|
||||
|
||||
print("Workflow execution completed")
|
||||
return workflow_data
|
||||
@@ -216,7 +216,7 @@ async def main():
|
||||
print_section("Travel Planning Workflow Evaluation")
|
||||
|
||||
print_section("Step 1: Running Workflow")
|
||||
workflow_data = await run_workflow(deployment_name=workflow_agent_model)
|
||||
workflow_data = await run_workflow(model=workflow_agent_model)
|
||||
|
||||
print_section("Step 2: Response Data Summary")
|
||||
display_response_summary(workflow_data)
|
||||
@@ -225,7 +225,7 @@ async def main():
|
||||
fetch_agent_responses(openai_client, workflow_data, agents_to_evaluate)
|
||||
|
||||
print_section("Step 4: Creating Evaluation")
|
||||
eval_object = create_evaluation(openai_client, deployment_name=eval_model)
|
||||
eval_object = create_evaluation(openai_client, model=eval_model)
|
||||
|
||||
print_section("Step 5: Running Evaluation")
|
||||
eval_run = run_evaluation(openai_client, eval_object, workflow_data, agents_to_evaluate)
|
||||
|
||||
Reference in New Issue
Block a user