fix: avoid stale client references in model and embedding classes#4276
fix: avoid stale client references in model and embedding classes#4276DouweM merged 2 commits intopydantic:mainfrom
Conversation
Summary of ChangesHello @DEVELOPER-DEEVEN, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the robustness of the Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
This PR is missing the required PR template. Please update the PR description to include:
Additionally:
|
|
@DEVELOPER-DEEVEN Sorry for the many AI review comments, we're trying different tools and enabled more than we'd meant to at the same time... There are some good points among them though :) Let me know if you're not sure how to move forward. |
|
no problem @DouweM those comments are quite helpful, ill let you know if I hit any blocker |
|
@DEVELOPER-DEEVEN I agree with the bot's point at #4276 (comment): unless this is really specific to |
|
Addressed the CI blockers in one focused commit.
Branch updated and ready for CI/review. |
50bda53 to
5f333d1
Compare
601b392 to
011e238
Compare
d056e8b to
011fd2e
Compare
|
@DouweM PTAL |
37a40c8 to
f23ef62
Compare
|
Thanks for the review @DouweM — I’ve made commit and aligned with AGENTS.md: no private attribute access, tests live in existing model files, and the GoogleProvider fixture stays real (the swappable subclass is local to the one test). The PR is now focused on the client‑property change only. CI rerunning after a flaky CDN fetch; once green, PTAL. |
|
Sorry for the radio silence here, it's in a good state so I'll get it over the line from here! |
Extend the client-property fix to `BedrockEmbeddingModel`, `OpenAIEmbeddingModel`, `GoogleEmbeddingModel`, and `CohereEmbeddingModel`, which had the same stale-reference pattern. Drop the per-test `_SwappableProvider` subclass + private `_client` mutation in favor of a single `model.client is provider.client` identity check, folded into each file's existing `test_init` where one exists.
There was a problem hiding this comment.
Pull request overview
This PR prevents model instances from holding stale SDK client references by removing client caching at construction time and instead delegating client access to the current provider client.
Changes:
- Replaced cached
clientattributes with@propertyaccessors delegating toself._provider.clientacross multiple model classes. - Updated Gemini/OpenAI/etc. model implementations to rely on the delegated client (and derived properties like
base_url) rather than cached values. - Updated/added tests to assert that models expose the provider’s client.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
pydantic_ai_slim/pydantic_ai/models/openai.py |
Removes cached client fields and adds delegating client properties for OpenAI chat and responses models. |
pydantic_ai_slim/pydantic_ai/models/anthropic.py |
Replaces cached client with a delegating client property. |
pydantic_ai_slim/pydantic_ai/models/groq.py |
Replaces cached client with a delegating client property. |
pydantic_ai_slim/pydantic_ai/models/mistral.py |
Replaces cached client with a delegating client property. |
pydantic_ai_slim/pydantic_ai/models/google.py |
Replaces cached client with a delegating client property. |
pydantic_ai_slim/pydantic_ai/models/gemini.py |
Replaces cached client/base URL with delegating properties. |
pydantic_ai_slim/pydantic_ai/models/cohere.py |
Replaces cached client with a delegating client property. |
pydantic_ai_slim/pydantic_ai/models/huggingface.py |
Replaces cached client with a delegating client property. |
pydantic_ai_slim/pydantic_ai/models/bedrock.py |
Replaces cached Bedrock runtime client with a delegating client property (cast from provider). |
pydantic_ai_slim/pydantic_ai/models/xai.py |
Removes cached client assignment and adds a delegating client property. |
pydantic_ai_slim/pydantic_ai/embeddings/openai.py |
Switches from cached _client to a delegating _client property. |
pydantic_ai_slim/pydantic_ai/embeddings/google.py |
Switches from cached _client to a delegating _client property. |
pydantic_ai_slim/pydantic_ai/embeddings/cohere.py |
Switches from cached _client/_v1_client to delegating properties. |
pydantic_ai_slim/pydantic_ai/embeddings/bedrock.py |
Replaces cached Bedrock runtime client with a delegating client property. |
tests/models/test_openai.py |
Updates init test to keep provider reference and asserts client delegates. |
tests/models/test_openai_responses.py |
Updates test to pass explicit provider and asserts delegation. |
tests/models/test_anthropic.py |
Updates init test to assert delegation. |
tests/models/test_groq.py |
Updates init test to assert delegation. |
tests/models/test_mistral.py |
Updates init test to assert delegation. |
tests/models/test_google.py |
Adds a test asserting delegation. |
tests/models/test_gemini.py |
Adds a test asserting delegation and base_url consistency. |
tests/models/test_cohere.py |
Updates init test to assert delegation. |
tests/models/test_huggingface.py |
Adds a test asserting delegation. |
tests/models/test_bedrock.py |
Adds a test asserting delegation. |
tests/models/test_xai.py |
Updates init test to assert delegation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| provider = OpenAIProvider(api_key='foobar') | ||
| m = OpenAIChatModel('gpt-4o', provider=provider) | ||
| assert m.base_url == 'https://api.openai.com/v1/' | ||
| assert m.client is provider.client | ||
| assert m.client.api_key == 'foobar' |
There was a problem hiding this comment.
The new assertion only checks that model.client equals provider.client immediately after construction, which would also pass with the old implementation that cached provider.client in __init__. To validate the stale-reference fix, extend this test (or add a dedicated one) to mutate/refresh the provider's client after model creation (e.g., swap provider._client/recreate the provider client, or use a small stub Provider whose client property can be reassigned) and assert m.client reflects the updated client.
| if isinstance(provider, str): | ||
| provider = infer_provider(provider) | ||
| self._provider = provider | ||
| self._client = provider.client | ||
|
|
||
| super().__init__(settings=settings) | ||
|
|
||
| @property | ||
| def _client(self) -> AsyncOpenAI: | ||
| return self._provider.client | ||
|
|
There was a problem hiding this comment.
PR description focuses on updating model classes, but this change also alters embedding models by switching from a cached _client attribute to a delegating property. Please update the PR description (and/or add a brief note in release notes if applicable) to reflect the embeddings changes, or confirm they’re intentionally included in this fix.
|
Thanks for the review and for getting this over the line, @DouweM. |
Pre-Review Checklist
make formatandmake typecheck.Pre-Merge Checklist
Summary
Model and embedding classes previously cached a reference to the provider's client via
self.client = provider.client/self._client = provider.clientin__init__. This is a problem in environments where a provider may be re-initialized or swapped after construction (e.g. durable execution frameworks like Temporal, or tests with dependency injection): the model/embedding keeps using the stale client.Changes
clientdataclass field + init assignment with a@propertythat delegates toself._provider.clientin every affected class:OpenAIChatModel,OpenAIResponsesModel,AnthropicModel,GroqModel,MistralModel,GoogleModel,GeminiModel,CohereModel,HuggingFaceModel,BedrockConverseModel,XaiModel.OpenAIEmbeddingModel,GoogleEmbeddingModel,CohereEmbeddingModel,BedrockEmbeddingModel.GeminiModel.base_urlnow derives fromself.client.base_urlinstead of a cachedself._url, matching the other models.assert model.client is provider.clientidentity checks to each model's existingtest_init(or a new minimal test where none existed), replacing an earlier draft that used a_SwappableProvidersubclass + private_clientmutation.