Python: Add Perplexity connector (chat completion)#13943
Python: Add Perplexity connector (chat completion)#13943jliounis wants to merge 1 commit intomicrosoft:mainfrom
Conversation
Adds a Perplexity connector under semantic_kernel.connectors.ai.perplexity that targets the OpenAI-compatible chat completions endpoint at https://api.perplexity.ai/chat/completions. The connector reuses the openai SDK with a custom base_url and an X-Pplx-Integration attribution header (semantic-kernel/<version>) sent on every outgoing request. Default model is sonar-pro. Supports streaming and the Perplexity-specific search filters (search_recency_filter, search_domain_filter, return_images, return_related_questions). This mirrors the existing third-party connector patterns (NVIDIA NIM, Mistral AI, DeepSeek) used elsewhere in the repo. - semantic_kernel/connectors/ai/perplexity/{settings,prompt_execution_settings,services} - tests/unit/connectors/ai/perplexity (16 unit tests) - samples/concepts/setup: register Services.PERPLEXITY in chat_completion_services - samples/concepts/setup/ALL_SETTINGS.md: document new env vars Signed-off-by: james-pplx <james-pplx@users.noreply.github.com>
|
@james-pplx please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
1 similar comment
|
@james-pplx please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
There was a problem hiding this comment.
Pull request overview
Adds a new Perplexity (OpenAI-compatible) chat completion connector to the Python SDK, including env-var settings, prompt execution settings, a chat completion service (with attribution header), unit tests, and sample/docs updates.
Changes:
- Introduces
semantic_kernel.connectors.ai.perplexity(settings, prompt execution settings, chat completion service w/ streaming + attribution header). - Adds unit tests for Perplexity settings, prompt execution settings, and chat completion behavior (mocked
AsyncOpenAI). - Updates samples and settings documentation to include the new
PERPLEXITY_*environment variables and service registry entry.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| python/semantic_kernel/connectors/ai/perplexity/init.py | Exposes Perplexity connector public API. |
| python/semantic_kernel/connectors/ai/perplexity/settings/perplexity_settings.py | Adds env-var driven settings (PERPLEXITY_*) with defaults. |
| python/semantic_kernel/connectors/ai/perplexity/settings/init.py | Package init. |
| python/semantic_kernel/connectors/ai/perplexity/prompt_execution_settings/perplexity_prompt_execution_settings.py | Adds OpenAI-shaped chat settings plus Perplexity search filters. |
| python/semantic_kernel/connectors/ai/perplexity/prompt_execution_settings/init.py | Package init. |
| python/semantic_kernel/connectors/ai/perplexity/services/perplexity_chat_completion.py | Implements Perplexity chat completion service using AsyncOpenAI + attribution header. |
| python/semantic_kernel/connectors/ai/perplexity/services/init.py | Package init. |
| python/tests/unit/connectors/ai/perplexity/conftest.py | Test fixture for Perplexity env vars. |
| python/tests/unit/connectors/ai/perplexity/init.py | Test package init. |
| python/tests/unit/connectors/ai/perplexity/settings/init.py | Test package init. |
| python/tests/unit/connectors/ai/perplexity/services/init.py | Test package init. |
| python/tests/unit/connectors/ai/perplexity/test_perplexity_prompt_execution_settings.py | Unit tests for settings defaults/validation/serialization aliasing. |
| python/tests/unit/connectors/ai/perplexity/settings/test_perplexity_settings.py | Unit tests for env loading and defaults. |
| python/tests/unit/connectors/ai/perplexity/services/test_perplexity_chat_completion.py | Unit tests for init, header merging, request pass-through, and error handling. |
| python/samples/concepts/setup/chat_completion_services.py | Adds Services.PERPLEXITY + factory method for sample setup. |
| python/samples/concepts/setup/ALL_SETTINGS.md | Documents PERPLEXITY_* env vars in the settings matrix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| response_format: ( | ||
| dict[Literal["type"], Literal["text", "json_object"]] | dict[str, Any] | type[BaseModel] | type | None | ||
| ) = None |
There was a problem hiding this comment.
response_format allows passing Python types (e.g., type[BaseModel]/type), but this connector doesn’t implement the OpenAI handler’s structured-output conversion (turning those into a JSON-schema response_format dict) before calling AsyncOpenAI.chat.completions.create. As a result, providing a type here will likely raise a serialization/validation error at request time. Consider either (a) restricting response_format to only the dict shapes Perplexity/OpenAI expects, or (b) adding conversion logic similar to OpenAIHandler._handle_structured_output and a validator that sets structured_json_response.
| base_url=settings.get("base_url"), | ||
| service_id=settings.get("service_id"), | ||
| default_headers=settings.get("default_headers"), | ||
| env_file_path=settings.get("env_file_path"), |
There was a problem hiding this comment.
from_dict accepts env_file_encoding via the constructor, but it isn’t passed through here. This makes from_dict unable to fully reconstruct an instance when a non-default encoding is used.
| env_file_path=settings.get("env_file_path"), | |
| env_file_path=settings.get("env_file_path"), | |
| env_file_encoding=settings.get("env_file_encoding"), |
| def to_dict(self) -> dict[str, str]: | ||
| """Create a dict of the service settings.""" | ||
| client_settings = { | ||
| "api_key": self.client.api_key, |
There was a problem hiding this comment.
to_dict() doesn’t include base_url even though the connector supports overriding it and from_dict() expects it. This breaks round-tripping for custom endpoints; consider adding base_url: str(self.client.base_url) to the serialized settings.
| "api_key": self.client.api_key, | |
| "api_key": self.client.api_key, | |
| "base_url": str(self.client.base_url), |
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 92%
✓ Correctness
The Perplexity connector adds several Perplexity-specific fields (
search_domain_filter,search_recency_filter,return_images,return_related_questions) andtop_ktoPerplexityChatPromptExecutionSettings. These fields are not valid parameters of the openai SDK'sAsyncCompletions.create()method (verified against openai v2.33.0 which has no**kwargs). When any of these fields are set,prepare_settings_dict()includes them in the output dict andclient.chat.completions.create(**settings.prepare_settings_dict())will raiseTypeError: create() got an unexpected keyword argument. The testtest_search_filters_passed_throughpasses only becausecreate()is mocked. The fix is to overrideprepare_settings_dict()to move these non-OpenAI fields into theextra_bodydict, which the SDK forwards to the HTTP request body — the same pattern the NVIDIA connector uses for itsnvextfield.
✓ Security Reliability
This PR adds a Perplexity AI chat completion connector following established patterns from the NVIDIA, DepSeek, and OpenAI connectors. The implementation is clean from a security and reliability standpoint: API keys use Pydantic SecretStr, settings load from environment variables with proper prefix isolation, the AsyncOpenAI client is configured with validated credentials, and error handling wraps API failures in ServiceResponseException. The to_dict() method exposes the raw API key (via self.client.api_key), but this is the same pattern used by every other connector in the codebase (NVIDIA, Azure, OpenAI). No injection risks, unsafe deserialization, secrets in code, resource leaks, or unhandled failure modes were found.
✓ Test Coverage
The PR adds a Perplexity AI connector with reasonable unit test coverage for initialization, settings, attribution headers, non-streaming chat completion, Perplexity-specific search filters, and error handling. However, several significant code paths lack test coverage: (1) streaming chat completion (
_inner_get_streaming_chat_message_contents) is a non-trivial async generator with its own content-creation logic but has no tests — other connectors like Bedrock and Anthropic test streaming; (2) the Perplexity-specific citations metadata extraction in_get_metadata_from_chat_responseis untested; (3)from_dict/to_dictround-tripping is untested despiteto_dictcontaining custom merge logic. The test patterns (conftest fixtures, parametrize indirect) are consistent with existing connectors.
✓ Design Approach
The Perplexity connector is aimed at an OpenAI-compatible chat endpoint, but the current implementation narows Semantic Kernel’s existing chat contract in two important ways: it hand-rolls request serialization in a way that only preserves plain-text transcripts, and it introduces a provider-specific
nfield instead of the SDK’s canonicalnumber_of_responsessurface. Both are design-level compatibility regressions rather than isolated bugs.
Suggestions
- Add a test for streaming chat completion (
get_streaming_chat_message_contents). The_inner_get_streaming_chat_message_contentsmethod is a separate async-generator code path constructingStreamingChatMessageContentwithStreamingTextContentitems—none of this logic is exercised by current tests. A mockAsyncStream[ChatCompletionChunk]yielding one or two chunks would cover this path, consistent with Bedrock and Anthropic connector tests.
Automated review by james-pplx's agents
| temperature: Annotated[float | None, Field(ge=0.0, le=2.0)] = None | ||
| top_p: Annotated[float | None, Field(ge=0.0, le=1.0)] = None | ||
| top_k: Annotated[int | None, Field(ge=0)] = None | ||
| n: Annotated[int | None, Field(ge=1)] = None |
There was a problem hiding this comment.
Using the provider-wire field name n here leaks transport shape into the SDK surface. The shared conversion path only copies fields that exist on the target settings type, so callers setting the canonical number_of_responses will silently lose it for Perplexity. The existing OpenAI-compatible settings keep the SK field name and alias it to n for serialization.
| n: Annotated[int | None, Field(ge=1)] = None | |
| number_of_responses: Annotated[int | None, Field(ge=1, serialization_alias="n")] = None |
Summary
Adds a Perplexity chat completion connector to the Python SDK, mirroring the existing third-party connector patterns (NVIDIA NIM, DeepSeek, Mistral AI). The Perplexity Agent API is OpenAI-compatible, so this connector reuses the
openaiPython SDK with a custombase_url(https://api.perplexity.ai) and an attribution header sent on every request.sonar-proWhat's included
semantic_kernel/connectors/ai/perplexity/settings/perplexity_settings.py—PERPLEXITY_*env-var-driven Pydantic settings (mirrorsNvidiaSettings/MistralAISettings)prompt_execution_settings/perplexity_prompt_execution_settings.py— OpenAI-shaped chat parameters plus Perplexity-specific search filters (search_recency_filter,search_domain_filter,return_images,return_related_questions)services/perplexity_chat_completion.py—PerplexityChatCompletionservice supporting streaming and non-streaming, decorated@experimentaltests/unit/connectors/ai/perplexity/— 16 unit tests covering settings, prompt execution settings, and the chat completion service (mockedAsyncOpenAI)samples/concepts/setup/chat_completion_services.py— newServices.PERPLEXITYregistry entrysamples/concepts/setup/ALL_SETTINGS.md— documents the newPERPLEXITY_*env varsAttribution header
Every outgoing request carries
X-Pplx-Integration: semantic-kernel/<package-version>, set as a default header on theAsyncOpenAIclient. A unit test asserts this is present.Notes
openai(already a core dep) withbase_urloverridden.sonar-prois the only user-facing model in docs/samples.Test plan
uv run ruff check— passesuv run ruff format --check— passesuv run mypy semantic_kernel/connectors/ai/perplexity/— Success: no issues found in 7 source filesuv run pytest tests/unit/connectors/ai/perplexity/ -v— 20 passedhttps://api.perplexity.ai/chat/completions(not run here — requires a realPERPLEXITY_API_KEY)