Skip to content

Python: Reduce Azure chat client import overhead - #4744

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 2 commits into
microsoft:mainfrom
eavanvalkenburg:edvan/azure-chat-import-latency
Mar 18, 2026
Merged

Python: Reduce Azure chat client import overhead#4744
Eduard van Valkenburg (eavanvalkenburg) merged 2 commits into
microsoft:mainfrom
eavanvalkenburg:edvan/azure-chat-import-latency

Conversation

@eavanvalkenburg

@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) commented Mar 17, 2026

Copy link
Copy Markdown
Member

Motivation and Context

Latency benchmarking on current main showed import_azure_openai_chat_client regressed relative to the latest comparable historical baseline. Profiling pointed at agent_framework.azure._chat_client eagerly importing heavier OpenAI runtime modules and top-level package surfaces during module import. This change narrows that import path and helps address the latency work tracked in #2752.

Description

  • move AsyncAzureOpenAI behind TYPE_CHECKING so it is not imported at module load time
  • stop eagerly importing OpenAI Choice and ChunkChoice runtime types
  • import OpenAIChatOptions directly from agent_framework.openai._chat_client instead of the heavier agent_framework.openai package surface
  • update _parse_text_from_openai to read message and delta via getattr, preserving the existing parsing behavior without those runtime type imports

This keeps the change isolated to import-time behavior and avoids changing the public Azure chat client API surface. The change was exercised with the non-integration Azure chat client test suite.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 17, 2026 13:50
@markwallace-microsoft Mark Wallace (markwallace-microsoft) added the python Usage: [Issues, PRs], Target: Python label Mar 17, 2026
@github-actions github-actions Bot changed the title Reduce Azure chat client import overhead Python: Reduce Azure chat client import overhead Mar 17, 2026
@markwallace-microsoft

Mark Wallace (markwallace-microsoft) commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework/azure
   _chat_client.py87396%322–323, 334
TOTAL24004263889% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
5242 20 💤 0 ❌ 0 🔥 1m 24s ⏱️

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reduces Python import-time overhead for the Azure OpenAI chat client by avoiding eager imports of heavier OpenAI runtime types and the broader agent_framework.openai package surface, while keeping runtime parsing behavior consistent.

Changes:

  • Move AsyncAzureOpenAI import behind TYPE_CHECKING to avoid import-time cost.
  • Stop eagerly importing OpenAI Choice / ChunkChoice runtime types and update _parse_text_from_openai to use attribute-based access (getattr).
  • Import OpenAIChatOptions directly from agent_framework.openai._chat_client rather than the heavier agent_framework.openai namespace.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread python/packages/core/agent_framework/azure/_chat_client.py Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 3 | Confidence: 89%

✓ Correctness

The diff makes two changes: (1) moves openai type imports behind TYPE_CHECKING or removes them, and (2) replaces the isinstance-based dispatch in _parse_text_from_openai with a getattr-based duck-typing approach. Both changes are logically correct. The from __future__ import annotations import ensures AsyncAzureOpenAI can safely move to TYPE_CHECKING. The getattr fallback from message to delta correctly mirrors the original isinstance logic since Choice has message and ChunkChoice has delta (and not the other). The re-export path change for OpenAIChatOptions is valid as it's defined in _chat_client.py and also re-exported from __init__.py. No correctness issues found.

✗ Test Coverage

The diff changes _parse_text_from_openai in AzureOpenAIChatClient from isinstance-based dispatch to duck-typing via getattr. Existing tests cover the non-streaming path (via test_azure_on_your_data*) and streaming with None delta (via test_streaming_with_none_delta), but all exercise the method indirectly through the full get_response flow. There is no direct unit test for the Azure override of _parse_text_from_openai, and notably the refusal branch inside this override has zero test coverage—the existing test_parse_text_with_refusal only tests the base RawOpenAIChatClient. Adding targeted tests for the Azure-specific method would catch any regressions from the isinstance-to-getattr refactor.

✗ Design Approach

The diff makes three changes: (1) moves AsyncAzureOpenAI to the TYPE_CHECKING block (correct), (2) consolidates OpenAIChatOptions import from the public agent_framework.openai package into the private agent_framework.openai._chat_client module, and (3) replaces the typed choice: Choice | ChunkChoice override signature with choice: Any and substitutes isinstance(choice, Choice) discrimination with getattr duck-typing. The getattr approach at runtime is fine given the imports are being removed, but widening the signature to Any is unnecessary and the wrong tradeoff: since the file already has from __future__ import annotations (PEP 563), adding Choice and ChunkChoice to the TYPE_CHECKING block would let the type annotation remain fully typed without any runtime import cost. The import of OpenAIChatOptions from a private module (_chat_client) instead of the public package API is also a leaky abstraction, though less severe.

Flagged Issues

  • The Azure-specific _parse_text_from_openai override changed dispatch logic (isinstance → getattr fallback) but has no direct unit test. The refusal branch (if hasattr(message, 'refusal') and message.refusal) in this override is completely untested. A regression in the getattr fallback (e.g., a ChunkChoice that unexpectedly has a message attribute) or the refusal path would go undetected.
  • The override signature _parse_text_from_openai(self, choice: Any) unnecessarily discards the type contract established by the base class (Choice | ChunkChoice). Because from __future__ import annotations is already present in this file, annotations are never evaluated at runtime — Choice and ChunkChoice only need to be added to the TYPE_CHECKING block (not imported unconditionally) to keep the typed signature. Using Any here silences the type checker and breaks the override contract without any actual benefit.

Suggestions

  • Add a direct unit test for AzureOpenAIChatClient._parse_text_from_openai covering: (1) a Choice-like object with only message, (2) a ChunkChoice-like object with only delta, (3) an object with both message=None and delta (verifying fallback), (4) the refusal path, and (5) an object with neither attribute (returns None).
  • OpenAIChatOptions is part of the public API of agent_framework.openai (listed in __all__). The diff changes its import to come from the internal agent_framework.openai._chat_client module, bypassing the public surface. Import it from the public package (from agent_framework.openai import OpenAIChatOptions) to avoid coupling to implementation internals.

Automated review by eavanvalkenburg's agents

Comment thread python/packages/core/agent_framework/azure/_chat_client.py
Comment thread python/packages/core/agent_framework/azure/_chat_client.py
Comment thread python/packages/core/agent_framework/azure/_chat_client.py
…i tests

- Move Choice and ChunkChoice imports under TYPE_CHECKING to avoid
  runtime import cost (from __future__ annotations is already present)
- Restore proper typed signature (Choice | ChunkChoice) instead of Any
- Add direct unit tests for _parse_text_from_openai covering:
  - Choice with message content
  - ChunkChoice with delta content
  - Refusal branch for both Choice and ChunkChoice
  - No content/no refusal returning None
  - None delta (async content filtering) returning None

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merged via the queue into microsoft:main with commit 192a283 Mar 18, 2026
31 checks passed
@eavanvalkenburg
Eduard van Valkenburg (eavanvalkenburg) deleted the edvan/azure-chat-import-latency branch June 30, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants