mirror of
https://github.com/hwchase17/langchain.git
synced 2025-07-06 21:20:33 +00:00
community: fix perplexity response parameters not being included in model response (#30440)
This pull request includes enhancements to the `perplexity.py` file in the `chat_models` module, focusing on improving the handling of additional keyword arguments (`additional_kwargs`) in message processing methods. Additionally, new unit tests have been added to ensure the correct inclusion of citations, images, and related questions in the `additional_kwargs`. Issue: resolves https://github.com/langchain-ai/langchain/issues/30439 Enhancements to `perplexity.py`: * [`libs/community/langchain_community/chat_models/perplexity.py`](diffhunk://#diff-d3e4d7b277608683913b53dcfdbd006f0f4a94d110d8b9ac7acf855f1f22207fL208-L212): Modified the `_convert_delta_to_message_chunk`, `_stream`, and `_generate` methods to handle `additional_kwargs`, which include citations, images, and related questions. [[1]](diffhunk://#diff-d3e4d7b277608683913b53dcfdbd006f0f4a94d110d8b9ac7acf855f1f22207fL208-L212) [[2]](diffhunk://#diff-d3e4d7b277608683913b53dcfdbd006f0f4a94d110d8b9ac7acf855f1f22207fL277-L286) [[3]](diffhunk://#diff-d3e4d7b277608683913b53dcfdbd006f0f4a94d110d8b9ac7acf855f1f22207fR324-R331) New unit tests: * [`libs/community/tests/unit_tests/chat_models/test_perplexity.py`](diffhunk://#diff-dab956d79bd7d17a0f5dea3f38ceab0d583b43b63eb1b29138ee9b6b271ba1d9R119-R275): Added new tests `test_perplexity_stream_includes_citations_and_images` and `test_perplexity_stream_includes_citations_and_related_questions` to verify that the `stream` method correctly includes citations, images, and related questions in the `additional_kwargs`.
This commit is contained in:
parent
7664874a0d
commit
75823d580b
@ -345,16 +345,25 @@ class ChatPerplexity(BaseChatModel):
|
|||||||
if len(chunk["choices"]) == 0:
|
if len(chunk["choices"]) == 0:
|
||||||
continue
|
continue
|
||||||
choice = chunk["choices"][0]
|
choice = chunk["choices"][0]
|
||||||
citations = chunk.get("citations", [])
|
|
||||||
|
additional_kwargs = {}
|
||||||
|
if first_chunk:
|
||||||
|
additional_kwargs["citations"] = chunk.get("citations", [])
|
||||||
|
for attr in ["images", "related_questions"]:
|
||||||
|
if attr in chunk:
|
||||||
|
additional_kwargs[attr] = chunk[attr]
|
||||||
|
|
||||||
chunk = self._convert_delta_to_message_chunk(
|
chunk = self._convert_delta_to_message_chunk(
|
||||||
choice["delta"], default_chunk_class
|
choice["delta"], default_chunk_class
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(chunk, AIMessageChunk) and usage_metadata:
|
if isinstance(chunk, AIMessageChunk) and usage_metadata:
|
||||||
chunk.usage_metadata = usage_metadata
|
chunk.usage_metadata = usage_metadata
|
||||||
|
|
||||||
if first_chunk:
|
if first_chunk:
|
||||||
chunk.additional_kwargs |= {"citations": citations}
|
chunk.additional_kwargs |= additional_kwargs
|
||||||
first_chunk = False
|
first_chunk = False
|
||||||
|
|
||||||
finish_reason = choice.get("finish_reason")
|
finish_reason = choice.get("finish_reason")
|
||||||
generation_info = (
|
generation_info = (
|
||||||
dict(finish_reason=finish_reason) if finish_reason is not None else None
|
dict(finish_reason=finish_reason) if finish_reason is not None else None
|
||||||
@ -386,9 +395,14 @@ class ChatPerplexity(BaseChatModel):
|
|||||||
else:
|
else:
|
||||||
usage_metadata = None
|
usage_metadata = None
|
||||||
|
|
||||||
|
additional_kwargs = {"citations": response.citations}
|
||||||
|
for attr in ["images", "related_questions"]:
|
||||||
|
if hasattr(response, attr):
|
||||||
|
additional_kwargs[attr] = getattr(response, attr)
|
||||||
|
|
||||||
message = AIMessage(
|
message = AIMessage(
|
||||||
content=response.choices[0].message.content,
|
content=response.choices[0].message.content,
|
||||||
additional_kwargs={"citations": response.citations},
|
additional_kwargs=additional_kwargs,
|
||||||
usage_metadata=usage_metadata,
|
usage_metadata=usage_metadata,
|
||||||
)
|
)
|
||||||
return ChatResult(generations=[ChatGeneration(message=message)])
|
return ChatResult(generations=[ChatGeneration(message=message)])
|
||||||
|
@ -116,3 +116,160 @@ def test_perplexity_stream_includes_citations(mocker: MockerFixture) -> None:
|
|||||||
assert full.additional_kwargs == {"citations": ["example.com", "example2.com"]}
|
assert full.additional_kwargs == {"citations": ["example.com", "example2.com"]}
|
||||||
|
|
||||||
patcher.assert_called_once()
|
patcher.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.requires("openai")
|
||||||
|
def test_perplexity_stream_includes_citations_and_images(mocker: MockerFixture) -> None:
|
||||||
|
"""Test that the stream method includes citations in the additional_kwargs."""
|
||||||
|
llm = ChatPerplexity(
|
||||||
|
model="test",
|
||||||
|
timeout=30,
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
mock_chunk_0 = {
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"delta": {
|
||||||
|
"content": "Hello ",
|
||||||
|
},
|
||||||
|
"finish_reason": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"citations": ["example.com", "example2.com"],
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"image_url": "mock_image_url",
|
||||||
|
"origin_url": "mock_origin_url",
|
||||||
|
"height": 100,
|
||||||
|
"width": 100,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
mock_chunk_1 = {
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"delta": {
|
||||||
|
"content": "Perplexity",
|
||||||
|
},
|
||||||
|
"finish_reason": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"citations": ["example.com", "example2.com"],
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"image_url": "mock_image_url",
|
||||||
|
"origin_url": "mock_origin_url",
|
||||||
|
"height": 100,
|
||||||
|
"width": 100,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
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[BaseMessageChunk] = None
|
||||||
|
for i, chunk in enumerate(stream):
|
||||||
|
full = chunk if full is None else full + chunk
|
||||||
|
assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"]
|
||||||
|
if i == 0:
|
||||||
|
assert chunk.additional_kwargs["citations"] == [
|
||||||
|
"example.com",
|
||||||
|
"example2.com",
|
||||||
|
]
|
||||||
|
assert chunk.additional_kwargs["images"] == [
|
||||||
|
{
|
||||||
|
"image_url": "mock_image_url",
|
||||||
|
"origin_url": "mock_origin_url",
|
||||||
|
"height": 100,
|
||||||
|
"width": 100,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
assert "citations" not in chunk.additional_kwargs
|
||||||
|
assert "images" not in chunk.additional_kwargs
|
||||||
|
assert isinstance(full, AIMessageChunk)
|
||||||
|
assert full.content == "Hello Perplexity"
|
||||||
|
assert full.additional_kwargs == {
|
||||||
|
"citations": ["example.com", "example2.com"],
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"image_url": "mock_image_url",
|
||||||
|
"origin_url": "mock_origin_url",
|
||||||
|
"height": 100,
|
||||||
|
"width": 100,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
patcher.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.requires("openai")
|
||||||
|
def test_perplexity_stream_includes_citations_and_related_questions(
|
||||||
|
mocker: MockerFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test that the stream method includes citations in the additional_kwargs."""
|
||||||
|
llm = ChatPerplexity(
|
||||||
|
model="test",
|
||||||
|
timeout=30,
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
mock_chunk_0 = {
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"delta": {
|
||||||
|
"content": "Hello ",
|
||||||
|
},
|
||||||
|
"finish_reason": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"citations": ["example.com", "example2.com"],
|
||||||
|
"related_questions": ["example_question_1", "example_question_2"],
|
||||||
|
}
|
||||||
|
mock_chunk_1 = {
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"delta": {
|
||||||
|
"content": "Perplexity",
|
||||||
|
},
|
||||||
|
"finish_reason": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"citations": ["example.com", "example2.com"],
|
||||||
|
"related_questions": ["example_question_1", "example_question_2"],
|
||||||
|
}
|
||||||
|
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[BaseMessageChunk] = None
|
||||||
|
for i, chunk in enumerate(stream):
|
||||||
|
full = chunk if full is None else full + chunk
|
||||||
|
assert chunk.content == mock_chunks[i]["choices"][0]["delta"]["content"]
|
||||||
|
if i == 0:
|
||||||
|
assert chunk.additional_kwargs["citations"] == [
|
||||||
|
"example.com",
|
||||||
|
"example2.com",
|
||||||
|
]
|
||||||
|
assert chunk.additional_kwargs["related_questions"] == [
|
||||||
|
"example_question_1",
|
||||||
|
"example_question_2",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
assert "citations" not in chunk.additional_kwargs
|
||||||
|
assert "related_questions" not in chunk.additional_kwargs
|
||||||
|
assert isinstance(full, AIMessageChunk)
|
||||||
|
assert full.content == "Hello Perplexity"
|
||||||
|
assert full.additional_kwargs == {
|
||||||
|
"citations": ["example.com", "example2.com"],
|
||||||
|
"related_questions": ["example_question_1", "example_question_2"],
|
||||||
|
}
|
||||||
|
|
||||||
|
patcher.assert_called_once()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
version = 1
|
version = 1
|
||||||
|
revision = 1
|
||||||
requires-python = ">=3.9, <4.0"
|
requires-python = ">=3.9, <4.0"
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"python_full_version >= '3.12.4' and platform_python_implementation == 'PyPy'",
|
"python_full_version >= '3.12.4' and platform_python_implementation == 'PyPy'",
|
||||||
@ -1531,6 +1532,7 @@ requires-dist = [
|
|||||||
{ name = "requests", specifier = ">=2,<3" },
|
{ name = "requests", specifier = ">=2,<3" },
|
||||||
{ name = "sqlalchemy", specifier = ">=1.4,<3" },
|
{ name = "sqlalchemy", specifier = ">=1.4,<3" },
|
||||||
]
|
]
|
||||||
|
provides-extras = ["community", "anthropic", "openai", "azure-ai", "cohere", "google-vertexai", "google-genai", "fireworks", "ollama", "together", "mistralai", "huggingface", "groq", "aws", "deepseek", "xai"]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
codespell = [{ name = "codespell", specifier = ">=2.2.0,<3.0.0" }]
|
codespell = [{ name = "codespell", specifier = ">=2.2.0,<3.0.0" }]
|
||||||
@ -1745,7 +1747,7 @@ typing = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-core"
|
name = "langchain-core"
|
||||||
version = "0.3.45"
|
version = "0.3.47"
|
||||||
source = { editable = "../core" }
|
source = { editable = "../core" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jsonpatch" },
|
{ name = "jsonpatch" },
|
||||||
@ -1803,7 +1805,7 @@ typing = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-tests"
|
name = "langchain-tests"
|
||||||
version = "0.3.14"
|
version = "0.3.15"
|
||||||
source = { editable = "../standard-tests" }
|
source = { editable = "../standard-tests" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
|
Loading…
Reference in New Issue
Block a user