Skip to content

fix: support Haystack 3.0's lazy OpenAI client initialization in OpenAI-inheriting generators#3536

Merged
julian-risch merged 7 commits into
mainfrom
fix/lazy-openai-client-compat
Jul 6, 2026
Merged

fix: support Haystack 3.0's lazy OpenAI client initialization in OpenAI-inheriting generators#3536
julian-risch merged 7 commits into
mainfrom
fix/lazy-openai-client-compat

Conversation

@julian-risch

@julian-risch julian-risch commented Jul 2, 2026

Copy link
Copy Markdown
Member

Related Issues

  • Part of deepset-ai/haystack-private#446

Haystack 3.0 no longer creates the OpenAI clients in __init__: self.client/self.async_client are None until warm_up()/warm_up_async(), the _is_warmed_up flag is gone, and a missing API key raises at warm-up instead of at init. This breaks the ten integrations that subclass OpenAIChatGenerator/OpenAIGenerator/OpenAIResponsesChatGenerator/the OpenAI embedders.

Proposed Changes:

Source changes

  • mistral, openrouter (run/run_async overrides): replace the removed if not self._is_warmed_up: self.warm_up() with an unconditional self.warm_up() (idempotent on both versions); in run_async, call warm_up_async() when it exists (3.0) so the async client is created on the running event loop; narrow the now-Optional clients for mypy with the same assert-after-warm-up idiom that haystack 3.0's own run() uses.
  • perplexity chat generator: only wrap the clients with the Perplexity attribution headers in __init__ when they exist (2.x); add warm_up/warm_up_async overrides that apply the headers when 3.0 creates the clients.
  • perplexity embedders: the attribution header is injected via http_client_kwargs, but serialization must keep the user-provided value — under 3.0 the client is built from self.http_client_kwargs at warm-up, losing the header. Keep the enriched kwargs on self.http_client_kwargs (so both 2.x init and 3.0 warm-up build the clients with the header) and store the original user-provided value as self._http_client_kwargs, which to_dict() serializes.

Test changes (aimlapi, cometapi, meta_llama, mistral, nvidia, openrouter, orcarouter, perplexity, stackit, togetherai)

  • test_init_default/test_init_with_parameters: no longer assert on client attributes; they check component.api_key.resolve_value() and the other init attributes. Client creation is covered by a new dedicated test_warm_up (calls warm_up(), harmless on 2.x, required on 3.0).
  • test_init_fail_wo_api_key: runs init and warm_up() inside pytest.raises(ValueError), accepting the error from either version's raise point. The error message is identical on both. test_from_dict_fail_wo_env_var is removed as redundant with this test.
  • test_init_default_asynctest_warm_up_async: async test that await component.warm_up_async() when available, since 3.0 only creates the async client there.

How did you test it?

  • Haystack 2.x: unit tests pass.
  • Haystack v3 branch (installed git+https://github.com/deepset-ai/haystack.git@v3 into the test envs; for the seven suites that also need the ToolInvoker import guard I verified on a local merge with test: guard ToolInvoker imports so chat-generator tests run under Haystack 3.0 #3535):
    • test:types is clean in all ten — the _is_warmed_up/OpenAI | None mypy errors from the sweep are gone.
    • All lazy-client test failures are fixed. The only remaining v3 unit failures are addressed in other opened PRs.

Notes for the reviewer

Checklist

🤖 Generated with Claude Code

Haystack 3.0 no longer creates the OpenAI clients in __init__: they are
None until warm_up()/warm_up_async(), and a missing API key raises at
warm-up instead of init. Adapt the OpenAI-inheriting integrations while
keeping 2.x behavior:

- mistral, openrouter: replace the removed _is_warmed_up flag with an
  unconditional idempotent warm_up() (warm_up_async in run_async when
  available) and narrow the now-Optional clients for mypy.
- perplexity: only wrap the clients with attribution headers in __init__
  when they exist (2.x); override warm_up/warm_up_async to inject the
  headers when 3.0 creates the clients.
- tests: warm the component up before asserting on client attributes and
  accept the API-key error from either __init__ (2.x) or warm_up (3.0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (meta_llama)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (orcarouter)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (nvidia)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (openrouter)

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  integrations/openrouter/src/haystack_integrations/components/generators/openrouter/chat
  chat_generator.py 434
Project Total  

This report was generated by python-coverage-comment-action

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (cometapi)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (stackit)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (togetherai)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (aimlapi)

This PR does not seem to contain any modification to coverable code.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (mistral)

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  integrations/mistral/src/haystack_integrations/components/generators/mistral/chat
  chat_generator.py 389-390, 432, 463-464
Project Total  

This report was generated by python-coverage-comment-action

@julian-risch julian-risch marked this pull request as ready for review July 2, 2026 15:22
@julian-risch julian-risch requested a review from a team as a code owner July 2, 2026 15:22
@julian-risch julian-risch requested review from sjrl and removed request for a team July 2, 2026 15:22
…dder

perplexity's ruff config forbids assert outside tests (S101); raise a
RuntimeError instead when the client is missing, which narrows the
Optional type for mypy just as well.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report (perplexity)

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  integrations/perplexity/src/haystack_integrations/components/embedders/perplexity
  document_embedder.py
  text_embedder.py
  integrations/perplexity/src/haystack_integrations/components/generators/perplexity/chat
  chat_generator.py
Project Total  

This report was generated by python-coverage-comment-action

Review feedback on #3536: drop the warm-up dev comments in run(), and replace
the assert / raise-RuntimeError narrowing with type: ignore[union-attr]
comments, consistent with how the repo usually silences mypy for attributes
that are Optional only with haystack-ai >= 3.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@anakin87 anakin87 self-requested a review July 6, 2026 08:13

@anakin87 anakin87 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I've left some general considerations on tests.

(I would probably have created separate PRs for each integration where we modify the source code.) Anyway, the important thing is releasing the integrations once merged to make them v3-ready)

Comment thread integrations/aimlapi/tests/test_aimlapi_chat_generator.py Outdated
Comment thread integrations/aimlapi/tests/test_aimlapi_chat_generator.py Outdated
Comment thread integrations/aimlapi/tests/test_aimlapi_chat_generator.py
…undant from_dict env-var tests

- test_init_default/test_init_with_parameters no longer call warm_up();
  they assert on api_key.resolve_value() instead of client attributes
- client creation is covered by a new dedicated test_warm_up
- test_from_dict_fail_wo_env_var removed (redundant with test_init_fail_wo_api_key)
- test_init_default_async renamed to test_warm_up_async (it tests client creation)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@julian-risch julian-risch requested a review from anakin87 July 6, 2026 10:21
julian-risch and others added 3 commits July 6, 2026 15:49
…client_kwargs from an internal attribute

Per review suggestion: keep the attribution-enriched http_client_kwargs on
self.http_client_kwargs so haystack-ai >= 3.0 builds the clients with the
header at warm-up, and store the original user-provided value under
self._http_client_kwargs for to_dict().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@anakin87 anakin87 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I've also aligned the Perplexity Chat Generator in 1540d41 and be173b7

It's now ready to be merged

@julian-risch julian-risch merged commit a5907d1 into main Jul 6, 2026
78 of 84 checks passed
@julian-risch julian-risch deleted the fix/lazy-openai-client-compat branch July 6, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants