Skip to content

Python: Reduce Azure chat client import overhead#4744

Merged
eavanvalkenburg merged 2 commits intomicrosoft:mainfrom
eavanvalkenburg:edvan/azure-chat-import-latency
Mar 18, 2026
Merged

Python: Reduce Azure chat client import overhead#4744
eavanvalkenburg merged 2 commits intomicrosoft:mainfrom
eavanvalkenburg:edvan/azure-chat-import-latency

Conversation

@eavanvalkenburg
Copy link
Member

@eavanvalkenburg eavanvalkenburg commented Mar 17, 2026

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
@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
Copy link
Member

markwallace-microsoft commented Mar 17, 2026

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 ⏱️

Copy link
Contributor

Copilot AI left a comment

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.

Copy link
Member Author

@eavanvalkenburg eavanvalkenburg left a comment

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

…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>
@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue Mar 18, 2026
Merged via the queue into microsoft:main with commit 192a283 Mar 18, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants