Files
agent-framework/docs/decisions/0021-provider-leading-clients.md
Eduard van Valkenburg 5e056b672e 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>
2026-03-25 09:56:29 +00:00

7.0 KiB

status, contact, date, deciders, consulted
status contact date deciders consulted
accepted eavanvalkenburg 2026-03-20 eavanvalkenburg, sphenry, chetantoshnival taochenosu, moonbox3, dmytrostruk, giles17, alliscode

Provider-Leading Client Design & OpenAI Package Extraction

Context and Problem Statement

The agent-framework-core package currently bundles OpenAI and Azure OpenAI client implementations along with their dependencies (openai, azure-identity, azure-ai-projects, packaging). This makes core heavier than necessary for users who don't use OpenAI, and it conflates the core abstractions with a specific provider implementation. Additionally, the current class naming (OpenAIResponsesClient, OpenAIChatClient) is based on the underlying OpenAI API names rather than what users actually want to do, making discoverability harder for newcomers.

Decision Drivers

  • Lightweight core: Core should only contain abstractions, middleware infrastructure, and telemetry — no provider-specific code or dependencies.
  • Discoverability-first: Import namespaces should guide users to the right client. from agent_framework.openai import ... should surface all OpenAI-related clients; from agent_framework.azure import ... should surface Foundry, Azure AI, and other Azure-specific classes.
  • Provider-leading naming: The primary client name should reflect the provider, not the underlying API. The Responses API is now the recommended default for OpenAI, so its client should be called OpenAIChatClient (not OpenAIResponsesClient).
  • Clean separation of concerns: Azure-specific deprecated wrappers belong in the azure-ai package, not in the OpenAI package.

Considered Options

  • Keep OpenAI in core: Simpler but keeps core heavy; doesn't help discoverability.
  • Extract OpenAI with Azure wrappers in the OpenAI package: Keeps Azure OpenAI wrappers alongside OpenAI code, but pollutes the OpenAI package with Azure concerns.
  • Extract OpenAI, place Azure wrappers in azure-ai: Clean separation; the OpenAI package has zero Azure dependencies; deprecated Azure wrappers live in a single file in azure-ai for easy future deletion.

Decision Outcome

Chosen option: "Extract OpenAI, place Azure wrappers in azure-ai", because it achieves the lightest core, cleanest OpenAI package, and the most maintainable deprecation path.

Key changes:

  1. New agent-framework-openai package with dependencies on agent-framework-core, openai, and packaging only.
  2. Class renames: OpenAIResponsesClientOpenAIChatClient (Responses API), OpenAIChatClientOpenAIChatCompletionClient (Chat Completions API). Old names remain as deprecated aliases.
  3. Deprecated classes: OpenAIAssistantsClient, all AzureOpenAI*Client classes, AzureAIClient, AzureAIAgentClient, and AzureAIProjectAgentProvider are marked deprecated.
  4. New FoundryChatClient in azure-ai for Azure AI Foundry Responses API access, built on RawFoundryChatClient(RawOpenAIChatClient).
  5. All deprecated AzureOpenAI* classes consolidated into a single file (_deprecated_azure_openai.py) in the azure-ai package for clean future deletion.
  6. Core's agent_framework.openai and agent_framework.azure namespaces become lazy-loading gateways, preserving backward-compatible import paths while removing hard dependencies.
  7. Unified model parameter replaces model_id (OpenAI), deployment_name (Azure OpenAI), and model_deployment_name (Azure AI) across all client constructors. The term model is intentionally generic: it naturally maps to an OpenAI model name and to an Azure OpenAI deployment name, making it straightforward to use OpenAIChatClient with either OpenAI or Azure OpenAI backends (via AsyncAzureOpenAI). Environment variables are similarly unified (e.g., OPENAI_MODEL instead of separate OPENAI_RESPONSES_MODEL_ID / OPENAI_CHAT_MODEL_ID).
  8. FoundryAgent replaces the pattern of Agent(client=AzureAIClient(...)) for connecting to pre-configured agents in Azure AI Foundry (PromptAgents and HostedAgents). The underlying RawFoundryAgentChatClient is an implementation detail — most users interact only with FoundryAgent. AzureAIAgentClient is separately deprecated as it refers to the V1 Agents Service API. See below for design rationale.

Foundry Agent Design: FoundryAgentClient vs FoundryAgent

The existing AzureAIClient combines two concerns: CRUD lifecycle management (creating/deleting agents on the service) and runtime communication (sending messages via the Responses API). The new design removes CRUD entirely — users connect to agents that already exist in Foundry.

Two approaches were considered:

Option A — FoundryAgentClient only (public ChatClient): Users compose Agent(client=FoundryAgentClient(...), tools=[...]). This follows the universal Agent(client=X) pattern used by every other provider. However, a "client" that wraps a named remote agent (with agent_name as a constructor param) is semantically odd — clients typically wrap a model endpoint, not a specific agent.

Option B — FoundryAgent (Agent subclass) + private _FoundryAgentChatClient and public RawFoundryAgentChatClient: Users write FoundryAgent(agent_name="my-agent", ...) for the common case. Internally, FoundryAgent creates a _FoundryAgentChatClient and passes it to the standard Agent base class. For advanced customization, users pass client_type=RawFoundryAgentChatClient (or a custom subclass) to control the client middleware layers. The Agent(client=RawFoundryAgentChatClient(...)) composition pattern still works for users who prefer it.

Chosen option: Option B, because:

  • The common case (FoundryAgent(...)) is a single object with no boilerplate.
  • client_type= gives full control over client middleware without parameter duplication — the agent forwards connection params to the client internally.
  • RawFoundryAgent(RawAgent) and FoundryAgent(Agent) mirror the established RawAgent/Agent pattern.
  • Runtime validation (only FunctionTool allowed) lives in RawFoundryAgentChatClient._prepare_options, ensuring it applies regardless of how the client is used — through FoundryAgent, Agent(client=...), or any custom composition.

Public classes:

  • RawFoundryAgentChatClient(RawOpenAIChatClient) — Responses API client that injects agent reference and validates tools. Extension point for custom client middleware.
  • RawFoundryAgent(RawAgent) — Agent without agent-level middleware/telemetry.
  • FoundryAgent(AgentTelemetryLayer, AgentMiddlewareLayer, RawFoundryAgent) — Recommended production agent.

Internal (private):

  • _FoundryAgentChatClient — Full client with function invocation, chat middleware, and telemetry layers. Created automatically by FoundryAgent; users customize via client_type=RawFoundryAgentChatClient or a custom subclass.

Deprecated:

  • AzureAIClient — replaced by FoundryAgent (which uses FoundryAgentClient internally).
  • AzureAIAgentClient — refers to V1 Agents Service API, no direct replacement.
  • AzureAIProjectAgentProvider — replaced by FoundryAgent.