Skip to content

Python: Add async context managers to chat clients - #9748

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 3 commits into
mainfrom
async-chat-client-lifecycle
Oct 28, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 3 commits into
mainfrom
async-chat-client-lifecycle

Conversation

@eavanvalkenburg

Copy link
Copy Markdown
Member

Motivation & Context

Python chat clients need a consistent, optional lifecycle for releasing connections without closing caller-owned SDK clients, transports, or credentials. This enables deterministic cleanup for standalone clients and for clients managed by an agent context, while preserving direct requests and existing custom implementations.

Description & Review Guide

  • What are the major changes? Add default async context methods and a no-op close() to BaseChatClient, with provider-specific ownership-aware cleanup for OpenAI/Azure OpenAI, Anthropic and its hosted variants, Bedrock, Gemini, Ollama, Mistral, Foundry, and Foundry Local. AG-UI and TypeSafe retain their existing context support. Update lifecycle documentation and consumer guidance.
  • What is the impact of these changes? Both async with client and explicit await client.close() release connector-owned connections. Direct requests remain supported, and SupportsChatGetResponse gains no requirements. RawAgent.open() already enters client contexts and its exit stack already releases them, so no agent implementation change is necessary. Foundry closes its independently created OpenAI connection even when its project client is supplied; caller-owned project clients and credentials remain open.
  • What do you want reviewers to focus on? Resource ownership across derived SDK clients and timeout-configured copies; cleanup order and failure handling; compatibility with custom clients and existing context overrides; and the documented lifetime boundary when sharing connections across agents. Credentials supplied by callers must remain caller-owned.

Added regression coverage for context entry/exit, normal and exceptional cleanup, cancellation, early stream exit, repeated cleanup, injected-resource preservation, agent-managed inherited contexts, and cleanup when another resource fails.

Affected non-integration regression suites: 2,386 passed, 12 skipped, 153 deselected. Existing experimental/deprecation warnings remain. Exact command:

uv run --directory python --group dev pytest packages/core/tests/core/{test_clients,test_agents}.py packages/{openai,anthropic,bedrock,gemini,ollama,mistral,foundry,foundry_local}/tests packages/ag-ui/tests/ag_ui/test_ag_ui_client.py --import-mode=importlib -m 'not integration' -n 0 --no-cov -o addopts= -q

Existing TypeSafe compatibility suite: 55 passed.

uv run --directory python --with-editable packages/typesafe --group dev pytest packages/typesafe/tests/typesafe/test_chat_client.py -q --no-cov -n 0 --import-mode=importlib -m 'not integration'

Core source typing: passed, zero errors or warnings.

uv run --directory python poe pyright -P core

Live-service integration tests, the complete repository suite, and distribution builds were not run.

Related Issue

No linked issue. Opened as an unlinked draft at the author's explicit request; issue agreement has not been recorded, and the corresponding checklist item remains unchecked.

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: Copilot assisted with the implementation plan, implementation, regression tests, documentation, validation, and this PR description. The author reviewed the approach and requested the compatibility and conditional-import behavior.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI balanced review requested due to automatic review settings October 27, 2026 09:23
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Oct 27, 2026
@github-code-quality

github-code-quality Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: Python

Python / code-coverage/python

main 10a94cf async-chat-client-li... d49dca0 +/-
92% 92% 0%
View coverage of the 10 most impacted files.
File main 10a94cf async-chat-client-li... d49dca0 +/-
packages/openai...enai/_shared.py 92% 92% 0%
packages/core/a...ork/_clients.py 93% 93% 0%
packages/openai..._chat_client.py 94% 94% 0%
packages/gemini..._chat_client.py 96% 96% 0%
packages/openai...etion_client.py 95% 95% 0%
packages/anthro..._chat_client.py 96% 96% 0%
packages/bedroc..._chat_client.py 96% 96% 0%
packages/foundr...undry/_agent.py 93% 94% +1%
packages/foundr..._chat_client.py 92% 93% +1%
packages/gemini.../_sdk_client.py 98% 100% +2%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 27, 2026 14:31 UTC

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.

🔵 Needs a closer look

The draft makes existing agent contexts close connector-owned SDK connections, changing the public ownership contract without recorded maintainer agreement.

No open issues 🎉

What changed in this PR

Adds an optional, consistent lifecycle to Python chat clients so standalone clients and agent contexts can release connector-owned connections while preserving caller-owned resources and existing custom implementations.

Changes:

  • Adds default async context methods and a no-op close() to BaseChatClient, with ownership-aware cleanup across providers.
  • Adds regression coverage for cleanup, injected-resource preservation, repeated closure, failure handling, and agent-managed contexts.
  • Documents connection ownership, credential lifetimes, and limits on sharing connections across agent contexts.
File Description
skills/​agent-framework-builder-python/​references/​integrations.md Documents lifecycle and resource ownership guidance.
python/​README.md Adds client and credential lifecycle examples.
python/​packages/​openai/​tests/​openai/​test_openai_chat_client.py Tests OpenAI and Azure OpenAI cleanup ownership.
python/​packages/​openai/​agent_framework_openai/​_chat_completion_client.py Adds ownership-aware chat completion client cleanup.
python/​packages/​openai/​agent_framework_openai/​_chat_client.py Adds ownership-aware Responses client cleanup.
python/​packages/​ollama/​tests/​test_ollama_chat_client.py Tests owned versus injected transport cleanup.
python/​packages/​ollama/​agent_framework_ollama/​_chat_client.py Closes connector-owned Ollama HTTP transports.
python/​packages/​mistral/​tests/​mistral/​test_mistral_chat_client.py Tests lifecycle ownership and cleanup failure handling.
python/​packages/​mistral/​AGENTS.md Updates the example to use an async context.
python/​packages/​mistral/​agent_framework_mistral/​_chat_client.py Guards repeated cleanup and ensures synchronous transport cleanup is attempted.
python/​packages/​gemini/​tests/​test_gemini_client.py Tests ownership and cleanup when async closure fails.
python/​packages/​gemini/​agent_framework_gemini/​_chat_client.py Closes both owned SDK transports.
python/​packages/​foundry/​tests/​foundry/​test_foundry_agent.py Tests derived connection ownership, timeout copies, and cleanup failures.
python/​packages/​foundry/​README.md Documents Foundry connection and credential ownership.
python/​packages/​foundry/​agent_framework_foundry/​_chat_client.py Closes derived OpenAI and owned project connections.
python/​packages/​foundry/​agent_framework_foundry/​_agent.py Extends agent chat client cleanup to owned OpenAI connections.
python/​packages/​foundry_local/​tests/​test_foundry_local_client.py Tests connection closure without stopping the local service.
python/​packages/​foundry_local/​agent_framework_foundry_local/​_foundry_local_client.py Marks the internally created OpenAI client as owned.
python/​packages/​core/​tests/​core/​test_clients.py Tests default lifecycle, exceptions, cancellation, and early stream exit.
python/​packages/​core/​tests/​core/​test_agents.py Tests inherited client contexts and protocol-only compatibility.
python/​packages/​core/​AGENTS.md Clarifies the base client contract and lifecycle behavior.
python/​packages/​core/​agent_framework/​_clients.py Adds default async context methods and no-op cleanup.
python/​packages/​bedrock/​tests/​test_bedrock_client.py Tests runtime client ownership during cleanup.
python/​packages/​bedrock/​agent_framework_bedrock/​_chat_client.py Closes owned runtime clients through a worker thread.
python/​packages/​anthropic/​tests/​test_anthropic_provider_clients.py Tests cleanup ownership across Anthropic provider variants.
python/​packages/​anthropic/​agent_framework_anthropic/​_vertex_client.py Preserves ownership of internally created Vertex clients.
python/​packages/​anthropic/​agent_framework_anthropic/​_foundry_client.py Preserves ownership of internally created Foundry clients.
python/​packages/​anthropic/​agent_framework_anthropic/​_chat_client.py Adds ownership-aware Anthropic SDK cleanup.
python/​packages/​anthropic/​agent_framework_anthropic/​_bedrock_client.py Preserves ownership of internally created Bedrock clients.

Review effort: Balanced

Comment thread python/packages/core/tests/core/test_clients.py Fixed

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): de44e9878e18
Model: gpt-5.6-sol

Overview

The PR adds a consistent async lifecycle to chat clients, with strong provider-specific ownership guards and broad tests for cleanup, cancellation, repeated close, and injected-resource preservation. The remaining compatibility risk is that activating inherited context management can call and await a pre-existing synchronous close() method on a custom BaseChatClient subclass.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_clients.py

Comment thread python/packages/core/agent_framework/_clients.py Outdated
@eavanvalkenburg

Copy link
Copy Markdown
Member Author

Regarding the ownership/maintainer-agreement review: closing connector-created connections when an agent context exits is intentional; injected SDK clients and credentials remain caller-owned. Shared-client lifetime limitations are documented. This PR remains a draft with no linked agreed issue, as explicitly recorded in the PR description; no maintainer agreement is being claimed. The synchronous-close compatibility finding is fixed in d49dca0, with details in the resolved inline thread.

Comment thread python/packages/core/tests/core/test_agents.py
Comment thread python/packages/core/tests/core/test_clients.py
Comment thread python/packages/core/tests/core/test_clients.py
Comment thread python/packages/core/agent_framework/_clients.py
Comment thread python/packages/core/tests/core/test_clients.py

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 2

Result: No findings
Scope: 2 net-new commit(s): 8b2a429bcd7b, d49dca0b363b
Model: gpt-5.6-sol

Overview

This incremental update preserves inherited async context management for legacy BaseChatClient subclasses whose close() method is synchronous, while continuing to await asynchronous implementations. The isawaitable() guard, cancellation coverage, body-exception preservation checks, and agent-level lifecycle tests establish the intended compatibility behavior without changing protocol-only clients. No publishable Critical, High, or Medium issue was found in the reviewed range.

Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.

Merged via the queue into main with commit 20458fa Oct 28, 2026
61 checks passed
@eavanvalkenburg
Eduard van Valkenburg (eavanvalkenburg) deleted the async-chat-client-lifecycle branch October 28, 2026 06:31

This branch was previously deployed

1 inactive deployment
github-app-auth — d49dca0b Deployed Oct 27, 2026 by eavanvalkenburg via team_check #7761
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants