Skip to content

Add Perplexity integration package - #3262

Merged
sjrl merged 11 commits into
deepset-ai:mainfrom
jliounis:add-perplexity-search
May 13, 2026
Merged

Add Perplexity integration package#3262
sjrl merged 11 commits into
deepset-ai:mainfrom
jliounis:add-perplexity-search

Conversation

@jliounis

@jliounis jliounis commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a new perplexity-haystack integration package with Perplexity-backed Haystack components:

  • PerplexityWebSearch for sync and async Search API calls that return Haystack Document objects with search result metadata
  • PerplexityChatGenerator for chat completion generation, including streaming, tools, response formats, request customization, serialization, and attribution headers
  • PerplexityTextEmbedder and PerplexityDocumentEmbedder for embedding text and documents with supported Perplexity embedding models
  • Package metadata, pydoc configuration, labeler configuration, and a dedicated Perplexity integration CI workflow
  • Unit coverage for initialization, serialization, request shaping, attribution headers, error paths, async behavior, chat generation, and embedders

Testing

  • hatch run fmt
  • hatch run fmt-check
  • hatch run test:types
  • hatch run test:unit — 49 passed, 2 deselected

Adds a new integration package providing a `PerplexityWebSearch`
component that wraps the Perplexity Search API, returning Haystack
Documents built from search result snippets, titles and URLs.

Mirrors the existing Tavily websearch integration in layout, lint
and test conventions.

Signed-off-by: james-pplx <james-pplx@users.noreply.github.com>
@jliounis
jliounis requested a review from a team as a code owner April 30, 2026 14:52
@jliounis
jliounis requested review from sjrl and removed request for a team April 30, 2026 14:52
@CLAassistant

CLAassistant commented Apr 30, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added topic:CI type:documentation Improvements or additions to documentation labels Apr 30, 2026
@sjrl

sjrl commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

@jliounis thanks for opening the PR! It's looking in good shape. I'll be able to give it more in depth review next week.

If by then you could sign the CLA agreement #3262 (comment) that would be greatly appreciated.

Comment thread integrations/perplexity/CHANGELOG.md Outdated
Comment thread integrations/perplexity/tests/test_perplexity_websearch.py
@sjrl sjrl self-assigned this May 8, 2026
@jliounis

jliounis commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @sjrl! Addressed all three:

  • Removed integrations/perplexity/CHANGELOG.md (auto-generated).
  • Made PerplexityWebSearch.__init__ keyword-only with * after self.
  • Added an assertion in the serialization test that api_key is properly serialized via Secret.to_dict().

hatch run test:unit, fmt-check, and test:types are all green locally.

@jliounis

Copy link
Copy Markdown
Contributor Author

Following the audit of OpenAI-compatible providers in this repo, I've extended this PR to add:

  • PerplexityChatGenerator — subclasses OpenAIChatGenerator, points at https://api.perplexity.ai, defaults to sonar-pro. Mirrors the existing OpenRouter integration.
  • PerplexityTextEmbedder / PerplexityDocumentEmbedder — subclass OpenAITextEmbedder / OpenAIDocumentEmbedder, mirror the Mistral pattern.

All three components carry the same X-Pplx-Integration attribution header as PerplexityWebSearch. Tests added for all three; full hatch run test:unit is at 49 passing / 97% coverage and CI is green on Python 3.10 and 3.14 across Linux/Windows/macOS. Happy to split into a follow-up PR if you'd prefer — let me know.

I also opened deepset-ai/haystack-integrations#478 to add a catalog entry on haystack.deepset.ai/integrations once this lands.

@jliounis

jliounis commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

Switched PerplexityChatGenerator to target Perplexity's Agent API.

  • Now subclasses OpenAIResponsesChatGenerator (Responses API shape) instead of OpenAIChatGenerator.
  • Hits POST /v1/agent via the OpenAI SDK's client.responses.create() (Perplexity accepts /v1/responses as an alias).
  • Default model is now openai/gpt-5.4; api_base_url defaults to https://api.perplexity.ai/v1.
  • X-Pplx-Integration attribution header still wired through.
  • Tests rewritten to mock responses.create and assert the Responses path, default model, default base URL, and serialization. All requested hatch jobs are green locally on the runner's Python 3.12.7 environment.

Embedders and PerplexityWebSearch unchanged.

@sjrl

sjrl commented May 12, 2026

Copy link
Copy Markdown
Contributor

Looking good! A few more comments

  • To get our CLA check to pass could you overwrite the author of this commit e3792a2 (the first commit in your PR) it looks like it's still using your old github account/handle.
  • Could you also add an integration test for each of the new components? This way we can test it end 2 end in our CI once we have API keys set up.

Add @pytest.mark.integration-guarded end-to-end tests (sync + async) for:
- PerplexityChatGenerator
- PerplexityDocumentEmbedder
- PerplexityTextEmbedder

PerplexityWebSearch integration tests already exist.

All integration tests are skipped unless PERPLEXITY_API_KEY is set so they
won't impact regular unit-test CI runs.
@jliounis
jliounis force-pushed the add-perplexity-search branch from 7a5374d to 1d3268e Compare May 12, 2026 12:53
@jliounis

Copy link
Copy Markdown
Contributor Author

@sjrl can you PTAL again?

Looking good! A few more comments

  • To get our CLA check to pass could you overwrite the author of this commit e3792a2 (the first commit in your PR) it looks like it's still using your old github account/handle.
  • Could you also add an integration test for each of the new components? This way we can test it end 2 end in our CI once we have API keys set up.

@sjrl can you PTAL again?

@jliounis

Copy link
Copy Markdown
Contributor Author

Thanks for the review @sjrl — both addressed in the latest push (HEAD now 1d3268e):

  1. CLA check / commit author: rewrote the first commit (e3792a2) so it's authored by my current GitHub account (jliounis, james.liounis@perplexity.ai) instead of the old james-pplx handle. All 7 commits on the branch are now under the same identity, so the CLA check should pick it up on this push.

  2. Integration tests: added end-to-end (@pytest.mark.integration) tests for each new component, gated on PERPLEXITY_API_KEY so they're skipped in regular CI until you have the key wired up:

    • TestPerplexityChatGeneratorInference — sync + async (test_perplexity_chat_generator.py)
    • TestPerplexityDocumentEmbedderInference — sync + async (test_perplexity_document_embedder.py)
    • TestPerplexityTextEmbedderInference — sync + async (test_perplexity_text_embedder.py)
    • PerplexityWebSearch already had test_run_integration + test_run_async_integration in test_perplexity_websearch.py — left those as-is.

    They follow the same pattern as the Cohere integration tests and can be run with hatch run test:integration.

Happy to amend further if you'd like different assertions or model coverage in the live tests.

Comment thread integrations/perplexity/README.md
@sjrl

sjrl commented May 12, 2026

Copy link
Copy Markdown
Contributor

I was able to partially run the integration tests locally with our own api key. The web search and the chat generator tests passed but the embedder ones failed currently. Could you double check that they pass for you?

@sjrl

sjrl commented May 12, 2026

Copy link
Copy Markdown
Contributor

Also please take a look at my comments starting here #3262 (comment) it looks those haven't been addressed yet.

…view)

Drop installation/usage code samples — code examples will live in the
dedicated docs page that deepset will publish post-merge. Mirrors the
mistral integration README layout.
@jliounis

Copy link
Copy Markdown
Contributor Author

Done — README trimmed in 0a6e420 to match the minimal mistral structure (PyPI badges, integration page link, changelog link, contributing section). All installation/usage code samples removed; they'll move to the dedicated docs page post-merge.

1 similar comment
@jliounis

Copy link
Copy Markdown
Contributor Author

Done — README trimmed in 0a6e420 to match the minimal mistral structure (PyPI badges, integration page link, changelog link, contributing section). All installation/usage code samples removed; they'll move to the dedicated docs page post-merge.

- embedders: fix api_base_url default to include /v1 (was hitting
  https://api.perplexity.ai/embeddings -> 404; correct endpoint is
  https://api.perplexity.ai/v1/embeddings). This is why the embedder
  integration tests failed for sjrl.
- embedders: add explicit from_dict classmethod on both
  PerplexityDocumentEmbedder and PerplexityTextEmbedder for correct
  typing (r3224097561, r3224106489).
- chat generator: use generate_qualified_class_name() instead of
  hardcoded class path in to_dict (r3224126336); drop redundant
  data['type'] override in from_dict (r3224130257); drop unused
  module-level path constants (r3224143875).
- websearch: drop unreachable 'client failed to initialize'
  RuntimeError branches in run / run_async — warm_up always runs first
  (r3224158183, r3224160907).
- websearch: tighten return-type hint on run / run_async to
  dict[str, list[Document] | list[str]] (r3224178510, r3224179725).
- tests: bump api_base_url assertions in embedder unit tests to /v1;
  drop obsolete 'raises_runtime_error_when_warm_up_fails' websearch
  tests; update chat-generator to_dict assertion to the
  fully-qualified module path produced by generate_qualified_class_name.
@jliounis

Copy link
Copy Markdown
Contributor Author

Thanks for catching that and for running the live tests @sjrl — both fixed in 9a683c9.

Embedder integration test failures: root cause + fix

The embedders defaulted to api_base_url="https://api.perplexity.ai", but the Perplexity embeddings endpoint actually lives at /v1/embeddings:

curl -sS -o /dev/null -w "%{http_code}\n" -X POST https://api.perplexity.ai/embeddings   ...  -> 404
curl -sS -o /dev/null -w "%{http_code}\n" -X POST https://api.perplexity.ai/v1/embeddings ...  -> 401 (auth, expected)

The OpenAI SDK appends /embeddings, so the old default was producing https://api.perplexity.ai/embeddings (404) instead of https://api.perplexity.ai/v1/embeddings. Default for both PerplexityDocumentEmbedder and PerplexityTextEmbedder is now https://api.perplexity.ai/v1 (matches the chat generator). Embedder unit-test assertions were updated accordingly. Integration tests against the live API now hit the correct endpoint — sorry for missing that.

Inline review comments

  • r3224097561 — added from_dict to PerplexityDocumentEmbedder.
  • r3224106489 — added from_dict to PerplexityTextEmbedder.
  • r3224105426 / r3224108116 — left the three self.<attr> = ... lines as-is per your "fine to leave" note; happy to drop them if you'd prefer.
  • r3224126336to_dict now uses generate_qualified_class_name(type(self)).
  • r3224130257 — removed the redundant data["type"] = ... line in from_dict.
  • r3224143875 — removed _PERPLEXITY_COMPONENT_PATH and _PERPLEXITY_INTERNAL_COMPONENT_PATH module constants.
  • r3224158183 / r3224160907 — dropped the post-warm_up if self._client is None guard blocks in both run and run_async, plus the corresponding (now-obsolete) unit tests.
  • r3224178510 / r3224179725 — return types tightened to dict[str, list[Document] | list[str]].

All 42 unit tests pass locally; integration tests should now succeed end-to-end against a live PERPLEXITY_API_KEY.

@jliounis

Copy link
Copy Markdown
Contributor Author

@sjrl are we good now?

@sjrl

sjrl commented May 13, 2026

Copy link
Copy Markdown
Contributor

Please fix the typing issues https://github.com/deepset-ai/haystack-core-integrations/actions/runs/25751675459/job/75629349722?pr=3262#step:7:87

Fine to just have type ignore on the affected lines

@sjrl sjrl 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.

Looks good! Just need a fix for the typing issue

@sjrl sjrl mentioned this pull request May 13, 2026
9 tasks
jliounis added 2 commits May 13, 2026 10:27
After removing the redundant 'if self._client is None: raise RuntimeError'
blocks, mypy can no longer narrow the type since warm_up()'s side effect
is invisible to it. Add # type: ignore[union-attr] on the .post() calls
in run / run_async (per sjrl review).
@jliounis

Copy link
Copy Markdown
Contributor Author

Fixed in 3e8ab67 — added # type: ignore[union-attr] to the .post() calls in run / run_async (mypy can't see through warm_up()'s side effect now that the explicit None guards are gone).

@jliounis

jliounis commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

@sjrl can you please merge? And can we please add this one?

@sjrl
sjrl merged commit c0152b0 into deepset-ai:main May 13, 2026
18 checks passed
@jliounis

Copy link
Copy Markdown
Contributor Author

@sjrl We now need an integration PR too :) deepset-ai/haystack-integrations#478

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration:perplexity topic:CI type:documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants