Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from __future__ import annotations

import os
import warnings
from dataclasses import asdict, dataclass
from typing import Any, Literal
from urllib.parse import urlparse
Expand Down Expand Up @@ -728,7 +729,7 @@ def with_ovhcloud(
@staticmethod
def with_perplexity(
*,
model: str | PerplexityChatModels = "llama-3.1-sonar-small-128k-chat",
model: str | PerplexityChatModels = "sonar-pro",
api_key: str | None = None,
base_url: str = "https://api.perplexity.ai",
client: openai.AsyncClient | None = None,
Expand All @@ -744,9 +745,21 @@ def with_perplexity(
"""
Create a new instance of PerplexityAI LLM.

``api_key`` must be set to your TogetherAI API key, either using the argument or by setting
.. deprecated::
This helper uses Sonar Chat Completions. Install
``livekit-plugins-perplexity`` and use ``perplexity.responses.LLM``
for the Perplexity Agent API.

``api_key`` must be set to your Perplexity API key, either using the argument or by setting
the ``PERPLEXITY_API_KEY`` environmental variable.
"""
warnings.warn(
"`openai.LLM.with_perplexity()` uses Sonar Chat Completions and is deprecated. "
"Install `livekit-plugins-perplexity` and use `perplexity.responses.LLM` for "
"the Perplexity Agent API.",
DeprecationWarning,
stacklevel=2,
)

api_key = api_key or os.environ.get("PERPLEXITY_API_KEY")
if api_key is None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,10 @@
]

PerplexityChatModels = Literal[
"llama-3.1-sonar-small-128k-online",
"llama-3.1-sonar-small-128k-chat",
"llama-3.1-sonar-large-128k-online",
"llama-3.1-sonar-large-128k-chat",
"llama-3.1-8b-instruct",
"llama-3.1-70b-instruct",
"sonar",
"sonar-pro",
"sonar-reasoning-pro",
"sonar-deep-research",
]

DeepSeekChatModels = Literal[
Expand Down
25 changes: 12 additions & 13 deletions livekit-plugins/livekit-plugins-perplexity/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Perplexity plugin for LiveKit Agents

Support for [Perplexity](https://www.perplexity.ai/) LLMs via the OpenAI-compatible
chat completions endpoint at `https://api.perplexity.ai`.
Support for [Perplexity](https://www.perplexity.ai/) models through the Agent API.

See [https://docs.livekit.io/agents/models/llm/perplexity/](https://docs.livekit.io/agents/models/llm/perplexity/) for more information.

Expand All @@ -21,20 +20,21 @@ You'll need an API key from Perplexity. It can be passed directly or set as the
```python
from livekit.plugins import perplexity

llm = perplexity.LLM(
model="sonar-pro",
llm = perplexity.responses.LLM(
model="perplexity/sonar",
# api_key picked up from PERPLEXITY_API_KEY if omitted
)
```

The plugin reuses the OpenAI plugin's chat completions transport with
`base_url="https://api.perplexity.ai"` and forwards an `X-Pplx-Integration`
attribution header on every outgoing request.
The Responses LLM uses `base_url="https://api.perplexity.ai/v1"`, disables
websocket transport, and sends an `X-Pplx-Integration` attribution header on
its OpenAI-compatible client.

## Agent API usage
## Migrating from Chat Completions

Perplexity's Agent API is compatible with OpenAI's Responses API and is
available through the `perplexity.responses` submodule.
The `perplexity.LLM` class and `openai.LLM.with_perplexity()` use Sonar Chat
Completions and are deprecated. Replace either legacy path with the Responses
LLM:

```python
from livekit.plugins import perplexity
Expand All @@ -45,6 +45,5 @@ llm = perplexity.responses.LLM(
)
```

The Responses LLM uses `base_url="https://api.perplexity.ai/v1"`, disables
websocket transport, and sends the same `X-Pplx-Integration` attribution header
on its OpenAI-compatible client.
See Perplexity's [migration guide](https://docs.perplexity.ai/docs/agent-api/migrate-from-sonar/overview)
for request and model changes when moving from Sonar to the Agent API.
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Perplexity plugin for LiveKit Agents
"""Perplexity plugin for LiveKit Agents.

Wraps Perplexity's OpenAI-compatible chat completions endpoint at
``https://api.perplexity.ai`` so it can be used as a drop-in LLM for LiveKit
voice agents.
Use ``perplexity.responses.LLM`` for Perplexity's Agent API. The legacy
``perplexity.LLM`` class uses Sonar Chat Completions and is deprecated.
"""

from livekit.agents import Plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from __future__ import annotations

import os
import warnings

import httpx
import openai
Expand Down Expand Up @@ -52,9 +53,19 @@ def __init__(
"""
Create a new instance of Perplexity LLM.

.. deprecated::
This client uses Sonar Chat Completions. Use
``perplexity.responses.LLM`` for the Perplexity Agent API.

``api_key`` must be set to your Perplexity API key, either using the argument or by
setting the ``PERPLEXITY_API_KEY`` environmental variable.
"""
warnings.warn(
"`perplexity.LLM` uses Sonar Chat Completions and is deprecated. "
"Use `perplexity.responses.LLM` for the Perplexity Agent API.",
DeprecationWarning,
stacklevel=2,
)
api_key = api_key if is_given(api_key) else os.environ.get("PERPLEXITY_API_KEY", "")
if not api_key:
raise ValueError(
Expand Down
37 changes: 33 additions & 4 deletions tests/test_plugin_perplexity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,44 @@

import pytest

from livekit.plugins import openai
from livekit.plugins.perplexity import LLM, __version__
from livekit.plugins.perplexity.llm import PERPLEXITY_BASE_URL

pytestmark = pytest.mark.plugin("perplexity")

MIGRATION_TARGET = "perplexity.responses.LLM"


def _create_legacy_llm() -> LLM:
with pytest.warns(DeprecationWarning):
return LLM()


def test_legacy_plugin_llm_warns_with_migration_path(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-key")
with pytest.warns(DeprecationWarning) as warning_info:
LLM()

assert MIGRATION_TARGET in str(warning_info[0].message)


def test_legacy_openai_factory_uses_supported_default_and_warns(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-key")
with pytest.warns(DeprecationWarning) as warning_info:
llm = openai.LLM.with_perplexity()

assert llm.model == "sonar-pro"
assert MIGRATION_TARGET in str(warning_info[0].message)


def test_default_model_and_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-key")
llm = LLM()
llm = _create_legacy_llm()
assert llm.model == "sonar-pro"
assert PERPLEXITY_BASE_URL == "https://api.perplexity.ai"
# AsyncClient stores the configured base URL on _base_url.
Expand All @@ -20,18 +49,18 @@ def test_default_model_and_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
def test_attribution_header_attached(monkeypatch: pytest.MonkeyPatch) -> None:
"""X-Pplx-Integration must be attached on every outgoing chat request."""
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-key")
llm = LLM()
llm = _create_legacy_llm()
extra_headers = llm._opts.extra_headers
assert extra_headers is not None
assert extra_headers["X-Pplx-Integration"] == f"livekit-agents/{__version__}"


def test_missing_api_key_raises(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("PERPLEXITY_API_KEY", raising=False)
with pytest.raises(ValueError, match="PERPLEXITY_API_KEY"):
with pytest.warns(DeprecationWarning), pytest.raises(ValueError, match="PERPLEXITY_API_KEY"):
LLM()


def test_provider_name(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-key")
assert LLM().provider == "Perplexity"
assert _create_legacy_llm().provider == "Perplexity"
2 changes: 1 addition & 1 deletion tests/test_plugin_perplexity_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def test_default_model_base_url_and_transport(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-key")
llm = responses.LLM()
assert llm.model == "sonar-pro"
assert llm.model == "perplexity/sonar"
assert llm._opts.use_websocket is False
assert PERPLEXITY_RESPONSES_BASE_URL == "https://api.perplexity.ai/v1"
assert str(llm._client.base_url).startswith("https://api.perplexity.ai/v1")
Expand Down