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:
Eduard van Valkenburg
2026-03-25 10:56:29 +01:00
committed by GitHub
Unverified
parent 4b533608b6
commit 5e056b672e
485 changed files with 9784 additions and 12084 deletions
@@ -22,11 +22,15 @@ import os
from pathlib import Path
from typing import Any
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework import Agent
from agent_framework.declarative import WorkflowFactory
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from pydantic import BaseModel, Field
# Copyright (c) Microsoft. All rights reserved.
# Pricing data for the order calculation
ITEM_PRICES = {
"pizza": {"small": 10.99, "medium": 14.99, "large": 18.99, "default": 14.99},
@@ -198,14 +202,15 @@ def format_order_confirmation(order_data: dict[str, Any], order_calculation: dic
async def main():
"""Run the agent to function tool workflow."""
# Create Azure OpenAI Responses client
chat_client = AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
chat_client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
# Create the order analysis agent with structured output
order_analysis_agent = chat_client.as_agent(
order_analysis_agent = Agent(
client=chat_client,
name="OrderAnalysisAgent",
instructions=ORDER_ANALYSIS_INSTRUCTIONS,
default_options={"response_format": OrderAnalysis},
@@ -27,12 +27,13 @@ import os
import uuid
from pathlib import Path
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework import Agent
from agent_framework.declarative import (
AgentExternalInputRequest,
AgentExternalInputResponse,
WorkflowFactory,
)
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
from pydantic import BaseModel, Field
@@ -168,49 +169,55 @@ async def main() -> None:
plugin = TicketingPlugin()
# Create Azure OpenAI client
client = AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
# This sample has been tested only on `gpt-5.1` and may not work as intended on other models
# This sample is known to fail on `gpt-5-mini` reasoning input (GH issue #4059)
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
# Create agents with structured outputs
self_service_agent = client.as_agent(
self_service_agent = Agent(
client=client,
name="SelfServiceAgent",
instructions=SELF_SERVICE_INSTRUCTIONS,
default_options={"response_format": SelfServiceResponse},
)
ticketing_agent = client.as_agent(
ticketing_agent = Agent(
client=client,
name="TicketingAgent",
instructions=TICKETING_INSTRUCTIONS,
tools=plugin.get_functions(),
default_options={"response_format": TicketingResponse},
)
routing_agent = client.as_agent(
routing_agent = Agent(
client=client,
name="TicketRoutingAgent",
instructions=TICKET_ROUTING_INSTRUCTIONS,
tools=[plugin.get_ticket],
default_options={"response_format": RoutingResponse},
)
windows_support_agent = client.as_agent(
windows_support_agent = Agent(
client=client,
name="WindowsSupportAgent",
instructions=WINDOWS_SUPPORT_INSTRUCTIONS,
tools=[plugin.get_ticket],
default_options={"response_format": SupportResponse},
)
resolution_agent = client.as_agent(
resolution_agent = Agent(
client=client,
name="TicketResolutionAgent",
instructions=RESOLUTION_INSTRUCTIONS,
tools=[plugin.resolve_ticket],
)
escalation_agent = client.as_agent(
escalation_agent = Agent(
client=client,
name="TicketEscalationAgent",
instructions=ESCALATION_INSTRUCTIONS,
tools=[plugin.get_ticket, plugin.send_notification],
@@ -25,14 +25,14 @@ import asyncio
import os
from pathlib import Path
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework import Agent
from agent_framework.declarative import WorkflowFactory
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
from pydantic import BaseModel, Field
# Load environment variables from .env file
load_dotenv()
# Copyright (c) Microsoft. All rights reserved.
# Agent Instructions
RESEARCH_INSTRUCTIONS = """In order to help begin addressing the user request, please answer the following pre-survey to the best of your ability.
@@ -124,45 +124,52 @@ class ManagerResponse(BaseModel):
async def main() -> None:
"""Run the deep research workflow."""
# Create Azure OpenAI client
client = AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
# Create agents
research_agent = client.as_agent(
research_agent = Agent(
client=client,
name="ResearchAgent",
instructions=RESEARCH_INSTRUCTIONS,
)
planner_agent = client.as_agent(
planner_agent = Agent(
client=client,
name="PlannerAgent",
instructions=PLANNER_INSTRUCTIONS,
)
manager_agent = client.as_agent(
manager_agent = Agent(
client=client,
name="ManagerAgent",
instructions=MANAGER_INSTRUCTIONS,
default_options={"response_format": ManagerResponse},
)
summary_agent = client.as_agent(
summary_agent = Agent(
client=client,
name="SummaryAgent",
instructions=SUMMARY_INSTRUCTIONS,
)
knowledge_agent = client.as_agent(
knowledge_agent = Agent(
client=client,
name="KnowledgeAgent",
instructions=KNOWLEDGE_INSTRUCTIONS,
)
coder_agent = client.as_agent(
coder_agent = Agent(
client=client,
name="CoderAgent",
instructions=CODER_INSTRUCTIONS,
)
weather_agent = client.as_agent(
weather_agent = Agent(
client=client,
name="WeatherAgent",
instructions=WEATHER_INSTRUCTIONS,
)
@@ -11,8 +11,8 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Annotated, Any
from agent_framework import FileCheckpointStorage, tool
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework import Agent, FileCheckpointStorage, tool
from agent_framework.foundry import FoundryChatClient
from agent_framework_declarative import ExternalInputRequest, ExternalInputResponse, WorkflowFactory
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
@@ -69,12 +69,13 @@ def get_item_price(name: Annotated[str, Field(description="Menu item name")]) ->
async def main():
# Create agent with tools
client = AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
menu_agent = client.as_agent(
menu_agent = Agent(
client=client,
name="MenuAgent",
instructions="Answer questions about menu items, specials, and prices.",
tools=[get_menu, get_specials, get_item_price],
@@ -16,13 +16,13 @@ import asyncio
import os
from pathlib import Path
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework import Agent
from agent_framework.declarative import WorkflowFactory
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Copyright (c) Microsoft. All rights reserved.
ANALYST_INSTRUCTIONS = """You are a product analyst. Analyze the given product and identify:
1. Key features and benefits
@@ -54,21 +54,24 @@ Return the final polished version."""
async def main() -> None:
"""Run the marketing workflow with real Azure AI agents."""
client = AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
analyst_agent = client.as_agent(
analyst_agent = Agent(
client=client,
name="AnalystAgent",
instructions=ANALYST_INSTRUCTIONS,
)
writer_agent = client.as_agent(
writer_agent = Agent(
client=client,
name="WriterAgent",
instructions=WRITER_INSTRUCTIONS,
)
editor_agent = client.as_agent(
editor_agent = Agent(
client=client,
name="EditorAgent",
instructions=EDITOR_INSTRUCTIONS,
)
@@ -12,26 +12,19 @@ async def main() -> None:
"""Run the simple greeting workflow."""
# Create a workflow factory
factory = WorkflowFactory()
# Load the workflow from YAML
workflow_path = Path(__file__).parent / "workflow.yaml"
workflow = factory.create_workflow_from_yaml_path(workflow_path)
print(f"Loaded workflow: {workflow.name}")
print("-" * 40)
# Run with default name
print("\nRunning with default name:")
result = await workflow.run({})
for output in result.get_outputs():
print(f" Output: {output}")
# Run with a custom name
print("\nRunning with custom name 'Alice':")
result = await workflow.run({"name": "Alice"})
for output in result.get_outputs():
print(f" Output: {output}")
print("\n" + "-" * 40)
print("Workflow completed!")
@@ -15,7 +15,7 @@ The workflow loops until the teacher gives congratulations or max turns reached.
Prerequisites:
- Azure OpenAI deployment with chat completion capability
- Environment variables:
AZURE_AI_PROJECT_ENDPOINT: Your Azure AI Foundry Agent Service (V2) project endpoint
FOUNDRY_PROJECT_ENDPOINT: Your Azure AI Foundry Agent Service (V2) project endpoint
AZURE_AI_MODEL_DEPLOYMENT_NAME: Your model deployment name
"""
@@ -23,13 +23,13 @@ import asyncio
import os
from pathlib import Path
from agent_framework.azure import AzureOpenAIResponsesClient
from agent_framework import Agent
from agent_framework.declarative import WorkflowFactory
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Copyright (c) Microsoft. All rights reserved.
STUDENT_INSTRUCTIONS = """You are a curious math student working on understanding mathematical concepts.
When given a problem:
@@ -56,19 +56,21 @@ Focus on building understanding, not just getting the right answer."""
async def main() -> None:
"""Run the student-teacher workflow with real Azure AI agents."""
# Create chat client
client = AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
# Create student and teacher agents
student_agent = client.as_agent(
student_agent = Agent(
client=client,
name="StudentAgent",
instructions=STUDENT_INSTRUCTIONS,
)
teacher_agent = client.as_agent(
teacher_agent = Agent(
client=client,
name="TeacherAgent",
instructions=TEACHER_INSTRUCTIONS,
)