chore(perplexity): bump perplexityai to 0.34.1 (#37710)

## Description
Bumps `langchain-perplexity` to require the Perplexity SDK release with
fixed Responses streaming and removes the temporary SSE shim workaround.

## Release Note
`langchain-perplexity` now requires `perplexityai>=0.34.1` for Responses
API streaming.

## Test Plan
- [x] `NO_COLOR=1 uv run --group test pytest
tests/unit_tests/test_chat_models_responses.py --disable-socket
--allow-unix-socket`

_Opened collaboratively by Mason Daugherty and open-swe._

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
This commit is contained in:
open-swe[bot]
2026-05-27 16:41:15 -04:00
committed by GitHub
parent b26b0ff23b
commit 8951e5666f
3 changed files with 11 additions and 98 deletions

View File

@@ -329,97 +329,6 @@ def _convert_responses_to_chat_result(response: Any) -> ChatResult:
return ChatResult(generations=[ChatGeneration(message=message)])
def _normalize_perplexity_sse(sse: Any) -> dict[str, Any] | None:
"""Decode a Perplexity SSE frame to a typed-payload dict, or skip it.
Returns `None` for frames that should be skipped without breaking the
stream (empty data, non-dict JSON, decode errors). Uses the SSE
`event:` field as the authoritative event-type discriminator — payloads
that disagree with the SSE frame name are realigned, because the SSE
name is the only source the API guarantees.
"""
data = getattr(sse, "data", None)
if not data:
return None
try:
payload = sse.json()
except (TypeError, ValueError):
logger.warning(
"Discarding Perplexity SSE event with non-JSON data; event=%r data=%r",
getattr(sse, "event", None),
data[:200],
)
return None
if not isinstance(payload, dict):
logger.debug(
"Discarding Perplexity SSE event with non-dict payload; event=%r type=%s",
getattr(sse, "event", None),
type(payload).__name__,
)
return None
sse_event = getattr(sse, "event", None)
if sse_event:
# The SSE frame name is authoritative — never let a mismatched
# `type` in the JSON body silently reclassify the event (e.g. a
# `response.failed` mis-tagged as `response.completed`).
payload["type"] = sse_event
return payload
def _iter_perplexity_sse_events(stream: Any) -> Iterator[Any]:
"""Yield Perplexity Responses streaming events.
Workaround for an upstream Perplexity Python SDK bug:
`Stream.__stream__` only yields events whose SSE `event:` field is
`None`, but the Agent API tags every event (e.g.
`event: response.completed`). The result is that
`list(client.responses.create(..., stream=True))` returns zero events.
Tracked upstream at:
https://github.com/perplexityai/perplexity-py/issues/53
Real `perplexity.Stream` instances always expose the lower-level
`_iter_events()` SSE iterator; we drop down to it and synthesize event
dicts (`type` taken from the SSE frame name) so they flow through
`_convert_responses_stream_event_to_chunk` — which already handles both
SDK objects and dicts via `_get_attr`. When `_iter_events` is missing
(test fakes that already yield decoded event objects), pass through.
"""
if not hasattr(stream, "_iter_events"):
yield from stream
return
for sse in stream._iter_events():
sse_data = getattr(sse, "data", None)
# Guard the `[DONE]` sentinel against frames with `data=None`
# (keepalive / comment SSE frames) — `None.startswith` would crash.
if sse_data and sse_data.startswith("[DONE]"):
break
payload = _normalize_perplexity_sse(sse)
if payload is None:
continue
yield payload
async def _aiter_perplexity_sse_events(stream: Any) -> AsyncIterator[Any]:
"""Async counterpart of `_iter_perplexity_sse_events`.
See the sync helper for rationale, removal criteria, and the upstream
bug tracking URL.
"""
if not hasattr(stream, "_iter_events"):
async for event in stream:
yield event
return
async for sse in stream._iter_events():
sse_data = getattr(sse, "data", None)
if sse_data and sse_data.startswith("[DONE]"):
break
payload = _normalize_perplexity_sse(sse)
if payload is None:
continue
yield payload
class PerplexityResponsesStreamError(RuntimeError):
"""Raised when a Perplexity Responses (Agent) API stream fails mid-flight.
@@ -1062,7 +971,10 @@ class ChatPerplexity(BaseChatModel):
)
responses_payload["stream"] = True
stream_events = self.client.responses.create(**responses_payload)
for event in _iter_perplexity_sse_events(stream_events):
# Trusts SDK SSE decoding (perplexityai>=0.34.1, upstream issue
# perplexityai-python#53). `_convert_responses_stream_event_to_chunk`
# already handles both SDK objects and dicts via `_get_attr`.
for event in stream_events:
response_chunk = _convert_responses_stream_event_to_chunk(event)
if response_chunk is None:
continue
@@ -1178,7 +1090,8 @@ class ChatPerplexity(BaseChatModel):
stream_events = await self.async_client.responses.create(
**responses_payload
)
async for event in _aiter_perplexity_sse_events(stream_events):
# See sync `_stream` for SDK trust rationale (perplexityai>=0.34.1).
async for event in stream_events:
response_chunk = _convert_responses_stream_event_to_chunk(event)
if response_chunk is None:
continue

View File

@@ -24,7 +24,7 @@ version = "1.3.0"
requires-python = ">=3.10.0,<4.0.0"
dependencies = [
"langchain-core>=1.4.0,<2.0.0",
"perplexityai>=0.32.0,<1.0.0",
"perplexityai>=0.34.1,<1.0.0",
]
[project.urls]

View File

@@ -537,7 +537,7 @@ typing = [
[package.metadata]
requires-dist = [
{ name = "langchain-core", editable = "../../core" },
{ name = "perplexityai", specifier = ">=0.32.0,<1.0.0" },
{ name = "perplexityai", specifier = ">=0.34.1,<1.0.0" },
]
[package.metadata.requires-dev]
@@ -973,7 +973,7 @@ wheels = [
[[package]]
name = "perplexityai"
version = "0.32.1"
version = "0.34.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -983,9 +983,9 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/09/02/73f460c85a5ec533a97fd1ff34fa729a009b4a217a4a87d8da946b6e1c52/perplexityai-0.32.1.tar.gz", hash = "sha256:b03503498591d06c4d50b666f7f7469875d3586f664c29416aae9012ae7a64d1", size = 135741, upload-time = "2026-04-21T04:35:40.345Z" }
sdist = { url = "https://files.pythonhosted.org/packages/4c/9e/253b60c5a51913a0a1e4eddccb02078e4cdb496cab994ad4bdd1e83a8f65/perplexityai-0.34.1.tar.gz", hash = "sha256:8e4b47d52c1d2c0d259eead6941dc60896045070bf0794bcbab8a96e428e06ef", size = 138767, upload-time = "2026-05-27T02:55:02.096Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d6/11/5c164f114311bc2e2350202393e7c5bd25bb156b5230a1edf5a2b2f4ba04/perplexityai-0.32.1-py3-none-any.whl", hash = "sha256:e5017d245fd8966cf79657edc03a93078d867708542b491b38152618f91e369b", size = 130223, upload-time = "2026-04-21T04:35:38.786Z" },
{ url = "https://files.pythonhosted.org/packages/05/47/246e7e7463df53a9e37cc7427296c8ee5c18183c5217fb3df3a088dfb652/perplexityai-0.34.1-py3-none-any.whl", hash = "sha256:6dcf94e73ec22bc8c98724e6999aa8a371eb441cc7ddd7e44c13e326e06d37b8", size = 131948, upload-time = "2026-05-27T02:55:01.024Z" },
]
[[package]]