mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-13 13:36:15 +00:00
feat(perplexity): expose search_results
in chat model (#31468)
Description The Perplexity chat model already returns a search_results field, but LangChain dropped it when mapping Perplexity responses to additional_kwargs. This patch adds "search_results" to the allowed attribute lists in both _stream and _generate, so downstream code can access it just like images, citations, or related_questions. Dependencies None. The change is purely internal; no new imports or optional dependencies required. https://community.perplexity.ai/t/new-feature-search-results-field-with-richer-metadata/398 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Mason Daugherty <github@mdrxy.com>
This commit is contained in:
@@ -74,7 +74,7 @@ class ChatPerplexity(BaseChatModel):
|
||||
|
||||
Key init args - completion params:
|
||||
model: str
|
||||
Name of the model to use. e.g. "llama-3.1-sonar-small-128k-online"
|
||||
Name of the model to use. e.g. "sonar"
|
||||
temperature: float
|
||||
Sampling temperature to use. Default is 0.7
|
||||
max_tokens: Optional[int]
|
||||
@@ -95,11 +95,9 @@ class ChatPerplexity(BaseChatModel):
|
||||
Instantiate:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.chat_models import ChatPerplexity
|
||||
from langchain_perplexity import ChatPerplexity
|
||||
|
||||
llm = ChatPerplexity(
|
||||
model="llama-3.1-sonar-small-128k-online", temperature=0.7
|
||||
)
|
||||
llm = ChatPerplexity(model="sonar", temperature=0.7)
|
||||
|
||||
Invoke:
|
||||
.. code-block:: python
|
||||
@@ -147,7 +145,7 @@ class ChatPerplexity(BaseChatModel):
|
||||
""" # noqa: E501
|
||||
|
||||
client: Any = None #: :meta private:
|
||||
model: str = "llama-3.1-sonar-small-128k-online"
|
||||
model: str = "sonar"
|
||||
"""Model name."""
|
||||
temperature: float = 0.7
|
||||
"""What sampling temperature to use."""
|
||||
@@ -325,7 +323,7 @@ class ChatPerplexity(BaseChatModel):
|
||||
additional_kwargs = {}
|
||||
if first_chunk:
|
||||
additional_kwargs["citations"] = chunk.get("citations", [])
|
||||
for attr in ["images", "related_questions"]:
|
||||
for attr in ["images", "related_questions", "search_results"]:
|
||||
if attr in chunk:
|
||||
additional_kwargs[attr] = chunk[attr]
|
||||
|
||||
@@ -376,7 +374,7 @@ class ChatPerplexity(BaseChatModel):
|
||||
usage_metadata = None
|
||||
|
||||
additional_kwargs = {}
|
||||
for attr in ["citations", "images", "related_questions"]:
|
||||
for attr in ["citations", "images", "related_questions", "search_results"]:
|
||||
if hasattr(response, attr):
|
||||
additional_kwargs[attr] = getattr(response, attr)
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Optional, cast
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from langchain_core.messages import AIMessageChunk, BaseMessageChunk
|
||||
from langchain_core.messages import AIMessageChunk, BaseMessage
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from langchain_perplexity import ChatPerplexity
|
||||
@@ -58,9 +58,9 @@ def test_perplexity_stream_includes_citations(mocker: MockerFixture) -> None:
|
||||
llm.client.chat.completions, "create", return_value=mock_stream
|
||||
)
|
||||
stream = llm.stream("Hello langchain")
|
||||
full: Optional[BaseMessageChunk] = None
|
||||
full: Optional[BaseMessage] = None
|
||||
for i, chunk in enumerate(stream):
|
||||
full = chunk if full is None else full + chunk
|
||||
full = chunk if full is None else cast(BaseMessage, full + chunk)
|
||||
assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"]
|
||||
if i == 0:
|
||||
assert chunk.additional_kwargs["citations"] == [
|
||||
@@ -110,9 +110,9 @@ def test_perplexity_stream_includes_citations_and_images(mocker: MockerFixture)
|
||||
llm.client.chat.completions, "create", return_value=mock_stream
|
||||
)
|
||||
stream = llm.stream("Hello langchain")
|
||||
full: Optional[BaseMessageChunk] = None
|
||||
full: Optional[BaseMessage] = None
|
||||
for i, chunk in enumerate(stream):
|
||||
full = chunk if full is None else full + chunk
|
||||
full = chunk if full is None else cast(BaseMessage, full + chunk)
|
||||
assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"]
|
||||
if i == 0:
|
||||
assert chunk.additional_kwargs["citations"] == [
|
||||
@@ -169,9 +169,9 @@ def test_perplexity_stream_includes_citations_and_related_questions(
|
||||
llm.client.chat.completions, "create", return_value=mock_stream
|
||||
)
|
||||
stream = llm.stream("Hello langchain")
|
||||
full: Optional[BaseMessageChunk] = None
|
||||
full: Optional[BaseMessage] = None
|
||||
for i, chunk in enumerate(stream):
|
||||
full = chunk if full is None else full + chunk
|
||||
full = chunk if full is None else cast(BaseMessage, full + chunk)
|
||||
assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"]
|
||||
if i == 0:
|
||||
assert chunk.additional_kwargs["citations"] == [
|
||||
@@ -193,3 +193,61 @@ def test_perplexity_stream_includes_citations_and_related_questions(
|
||||
}
|
||||
|
||||
patcher.assert_called_once()
|
||||
|
||||
|
||||
def test_perplexity_stream_includes_citations_and_search_results(
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
"""Test that the stream method exposes `search_results` via additional_kwargs."""
|
||||
llm = ChatPerplexity(model="test", timeout=30, verbose=True)
|
||||
|
||||
mock_chunk_0 = {
|
||||
"choices": [{"delta": {"content": "Hello "}, "finish_reason": None}],
|
||||
"citations": ["example.com/a", "example.com/b"],
|
||||
"search_results": [
|
||||
{"title": "Mock result", "url": "https://example.com/result", "date": None}
|
||||
],
|
||||
}
|
||||
mock_chunk_1 = {
|
||||
"choices": [{"delta": {"content": "Perplexity"}, "finish_reason": None}],
|
||||
"citations": ["example.com/a", "example.com/b"],
|
||||
"search_results": [
|
||||
{"title": "Mock result", "url": "https://example.com/result", "date": None}
|
||||
],
|
||||
}
|
||||
mock_chunks: list[dict[str, Any]] = [mock_chunk_0, mock_chunk_1]
|
||||
mock_stream = MagicMock()
|
||||
mock_stream.__iter__.return_value = mock_chunks
|
||||
patcher = mocker.patch.object(
|
||||
llm.client.chat.completions, "create", return_value=mock_stream
|
||||
)
|
||||
stream = llm.stream("Hello langchain")
|
||||
full: Optional[BaseMessage] = None
|
||||
for i, chunk in enumerate(stream):
|
||||
full = chunk if full is None else cast(BaseMessage, full + chunk)
|
||||
assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"]
|
||||
if i == 0:
|
||||
assert chunk.additional_kwargs["citations"] == [
|
||||
"example.com/a",
|
||||
"example.com/b",
|
||||
]
|
||||
assert chunk.additional_kwargs["search_results"] == [
|
||||
{
|
||||
"title": "Mock result",
|
||||
"url": "https://example.com/result",
|
||||
"date": None,
|
||||
}
|
||||
]
|
||||
else:
|
||||
assert "citations" not in chunk.additional_kwargs
|
||||
assert "search_results" not in chunk.additional_kwargs
|
||||
assert isinstance(full, AIMessageChunk)
|
||||
assert full.content == "Hello Perplexity"
|
||||
assert full.additional_kwargs == {
|
||||
"citations": ["example.com/a", "example.com/b"],
|
||||
"search_results": [
|
||||
{"title": "Mock result", "url": "https://example.com/result", "date": None}
|
||||
],
|
||||
}
|
||||
|
||||
patcher.assert_called_once()
|
||||
|
Reference in New Issue
Block a user