Repository navigation
Python: Add async context managers to chat clients - #9748
Conversation
Code coverage overviewLanguages: Python Python / code-coverage/python
View coverage of the 10 most impacted files.
ℹ️ Calculations represent line coverage. Results may change as calculations are updated. Updated |
There was a problem hiding this comment.
🔵 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()toBaseChatClient, 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
There was a problem hiding this comment.
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
|
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. |
There was a problem hiding this comment.
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.
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
close()toBaseChatClient, 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.async with clientand explicitawait client.close()release connector-owned connections. Direct requests remain supported, andSupportsChatGetResponsegains 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.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= -qExisting 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.
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
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
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.