perplexity-haystack 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- perplexity_haystack-0.1.1/CHANGELOG.md +9 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/PKG-INFO +1 -1
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/embedders/perplexity/document_embedder.py +107 -1
- perplexity_haystack-0.1.1/src/haystack_integrations/components/embedders/perplexity/embedding_encoding.py +34 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/embedders/perplexity/text_embedder.py +23 -0
- perplexity_haystack-0.1.1/tests/test_embedding_encoding.py +24 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_document_embedder.py +208 -8
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_text_embedder.py +69 -7
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/.gitignore +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/LICENSE.txt +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/README.md +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/pydoc/config_docusaurus.yml +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/pyproject.toml +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/embedders/perplexity/__init__.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/embedders/py.typed +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/generators/perplexity/__init__.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/generators/perplexity/chat/__init__.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/generators/perplexity/chat/chat_generator.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/generators/py.typed +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/websearch/perplexity/__init__.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/websearch/perplexity/perplexity_websearch.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/src/haystack_integrations/components/websearch/py.typed +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/__init__.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_chat_generator.py +0 -0
- {perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_websearch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: perplexity-haystack
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Haystack integration for Perplexity Web Search
|
|
5
5
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/perplexity#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
@@ -3,11 +3,24 @@
|
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
5
|
import importlib.metadata
|
|
6
|
+
from collections.abc import Iterable
|
|
6
7
|
from typing import Any, ClassVar
|
|
7
8
|
|
|
8
|
-
from haystack import component, default_from_dict, default_to_dict
|
|
9
|
+
from haystack import component, default_from_dict, default_to_dict, logging
|
|
9
10
|
from haystack.components.embedders import OpenAIDocumentEmbedder
|
|
10
11
|
from haystack.utils.auth import Secret
|
|
12
|
+
from more_itertools import batched
|
|
13
|
+
from openai import APIError
|
|
14
|
+
from openai.types.create_embedding_response import CreateEmbeddingResponse
|
|
15
|
+
from tqdm import tqdm
|
|
16
|
+
from tqdm.asyncio import tqdm as async_tqdm
|
|
17
|
+
|
|
18
|
+
from haystack_integrations.components.embedders.perplexity.embedding_encoding import (
|
|
19
|
+
_decode_embedding,
|
|
20
|
+
_validate_encoding_format,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
11
24
|
|
|
12
25
|
_INTEGRATION_SLUG = "haystack"
|
|
13
26
|
_PACKAGE_NAME = "perplexity-haystack"
|
|
@@ -73,6 +86,7 @@ class PerplexityDocumentEmbedder(OpenAIDocumentEmbedder):
|
|
|
73
86
|
progress_bar: bool = True,
|
|
74
87
|
meta_fields_to_embed: list[str] | None = None,
|
|
75
88
|
embedding_separator: str = "\n",
|
|
89
|
+
encoding_format: str = "base64_int8",
|
|
76
90
|
timeout: float | None = None,
|
|
77
91
|
max_retries: int | None = None,
|
|
78
92
|
http_client_kwargs: dict[str, Any] | None = None,
|
|
@@ -99,6 +113,8 @@ class PerplexityDocumentEmbedder(OpenAIDocumentEmbedder):
|
|
|
99
113
|
List of meta fields that should be embedded along with the Document text.
|
|
100
114
|
:param embedding_separator:
|
|
101
115
|
Separator used to concatenate the meta fields to the Document text.
|
|
116
|
+
:param encoding_format:
|
|
117
|
+
The Perplexity embedding encoding format. Supported values are `base64_int8` and `base64_binary`.
|
|
102
118
|
:param timeout:
|
|
103
119
|
Timeout for Perplexity client calls. If not set, it defaults to either the `OPENAI_TIMEOUT` environment
|
|
104
120
|
variable, or 30 seconds.
|
|
@@ -109,6 +125,7 @@ class PerplexityDocumentEmbedder(OpenAIDocumentEmbedder):
|
|
|
109
125
|
A dictionary of keyword arguments to configure a custom `httpx.Client`or `httpx.AsyncClient`.
|
|
110
126
|
For more information, see the [HTTPX documentation](https://www.python-httpx.org/api/#client).
|
|
111
127
|
"""
|
|
128
|
+
self.encoding_format = _validate_encoding_format(encoding_format)
|
|
112
129
|
super(PerplexityDocumentEmbedder, self).__init__( # noqa: UP008
|
|
113
130
|
api_key=api_key,
|
|
114
131
|
model=model,
|
|
@@ -129,6 +146,94 @@ class PerplexityDocumentEmbedder(OpenAIDocumentEmbedder):
|
|
|
129
146
|
self.timeout = timeout
|
|
130
147
|
self.max_retries = max_retries
|
|
131
148
|
|
|
149
|
+
def _decode_response_embeddings(self, response: CreateEmbeddingResponse) -> list[list[float]]:
|
|
150
|
+
return [_decode_embedding(str(el.embedding), self.encoding_format) for el in response.data]
|
|
151
|
+
|
|
152
|
+
def _embed_batch(
|
|
153
|
+
self, texts_to_embed: dict[str, str], batch_size: int
|
|
154
|
+
) -> tuple[dict[str, list[float]], dict[str, Any]]:
|
|
155
|
+
"""
|
|
156
|
+
Embed a list of texts in batches.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
doc_ids_to_embeddings: dict[str, list[float]] = {}
|
|
160
|
+
meta: dict[str, Any] = {}
|
|
161
|
+
for batch in tqdm(
|
|
162
|
+
batched(texts_to_embed.items(), batch_size), disable=not self.progress_bar, desc="Calculating embeddings"
|
|
163
|
+
):
|
|
164
|
+
args: dict[str, Any] = {
|
|
165
|
+
"model": self.model,
|
|
166
|
+
"input": [b[1] for b in batch],
|
|
167
|
+
"encoding_format": self.encoding_format,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
response = self.client.embeddings.create(**args)
|
|
172
|
+
except APIError as exc:
|
|
173
|
+
ids = ", ".join(b[0] for b in batch)
|
|
174
|
+
msg = "Failed embedding of documents {ids} caused by {exc}"
|
|
175
|
+
logger.exception(msg, ids=ids, exc=exc)
|
|
176
|
+
if self.raise_on_failure:
|
|
177
|
+
raise exc
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
embeddings = self._decode_response_embeddings(response)
|
|
181
|
+
doc_ids_to_embeddings.update(dict(zip((b[0] for b in batch), embeddings, strict=True)))
|
|
182
|
+
|
|
183
|
+
if "model" not in meta:
|
|
184
|
+
meta["model"] = response.model
|
|
185
|
+
if "usage" not in meta:
|
|
186
|
+
meta["usage"] = dict(response.usage)
|
|
187
|
+
else:
|
|
188
|
+
meta["usage"]["prompt_tokens"] += response.usage.prompt_tokens
|
|
189
|
+
meta["usage"]["total_tokens"] += response.usage.total_tokens
|
|
190
|
+
|
|
191
|
+
return doc_ids_to_embeddings, meta
|
|
192
|
+
|
|
193
|
+
async def _embed_batch_async(
|
|
194
|
+
self, texts_to_embed: dict[str, str], batch_size: int
|
|
195
|
+
) -> tuple[dict[str, list[float]], dict[str, Any]]:
|
|
196
|
+
"""
|
|
197
|
+
Embed a list of texts in batches asynchronously.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
doc_ids_to_embeddings: dict[str, list[float]] = {}
|
|
201
|
+
meta: dict[str, Any] = {}
|
|
202
|
+
|
|
203
|
+
batches: Iterable[tuple[tuple[str, str], ...]] = list(batched(texts_to_embed.items(), batch_size))
|
|
204
|
+
if self.progress_bar:
|
|
205
|
+
batches = async_tqdm(batches, desc="Calculating embeddings")
|
|
206
|
+
|
|
207
|
+
for batch in batches:
|
|
208
|
+
args: dict[str, Any] = {
|
|
209
|
+
"model": self.model,
|
|
210
|
+
"input": [b[1] for b in batch],
|
|
211
|
+
"encoding_format": self.encoding_format,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
response = await self.async_client.embeddings.create(**args)
|
|
216
|
+
except APIError as exc:
|
|
217
|
+
ids = ", ".join(b[0] for b in batch)
|
|
218
|
+
msg = "Failed embedding of documents {ids} caused by {exc}"
|
|
219
|
+
logger.exception(msg, ids=ids, exc=exc)
|
|
220
|
+
if self.raise_on_failure:
|
|
221
|
+
raise exc
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
embeddings = self._decode_response_embeddings(response)
|
|
225
|
+
doc_ids_to_embeddings.update(dict(zip((b[0] for b in batch), embeddings, strict=True)))
|
|
226
|
+
|
|
227
|
+
if "model" not in meta:
|
|
228
|
+
meta["model"] = response.model
|
|
229
|
+
if "usage" not in meta:
|
|
230
|
+
meta["usage"] = dict(response.usage)
|
|
231
|
+
else:
|
|
232
|
+
meta["usage"]["prompt_tokens"] += response.usage.prompt_tokens
|
|
233
|
+
meta["usage"]["total_tokens"] += response.usage.total_tokens
|
|
234
|
+
|
|
235
|
+
return doc_ids_to_embeddings, meta
|
|
236
|
+
|
|
132
237
|
def to_dict(self) -> dict[str, Any]:
|
|
133
238
|
"""
|
|
134
239
|
Serializes the component to a dictionary.
|
|
@@ -147,6 +252,7 @@ class PerplexityDocumentEmbedder(OpenAIDocumentEmbedder):
|
|
|
147
252
|
progress_bar=self.progress_bar,
|
|
148
253
|
meta_fields_to_embed=self.meta_fields_to_embed,
|
|
149
254
|
embedding_separator=self.embedding_separator,
|
|
255
|
+
encoding_format=self.encoding_format,
|
|
150
256
|
timeout=self.timeout,
|
|
151
257
|
max_retries=self.max_retries,
|
|
152
258
|
http_client_kwargs=self.http_client_kwargs,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
SUPPORTED_ENCODING_FORMATS = {"base64_int8", "base64_binary"}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _validate_encoding_format(encoding_format: str) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Validate Perplexity's embedding encoding format.
|
|
15
|
+
"""
|
|
16
|
+
if encoding_format not in SUPPORTED_ENCODING_FORMATS:
|
|
17
|
+
supported_formats = "', '".join(sorted(SUPPORTED_ENCODING_FORMATS))
|
|
18
|
+
msg = f"Unsupported encoding_format='{encoding_format}'. Use '{supported_formats}'."
|
|
19
|
+
raise ValueError(msg)
|
|
20
|
+
return encoding_format
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _decode_embedding(embedding: str, encoding_format: str) -> list[float]:
|
|
24
|
+
"""
|
|
25
|
+
Decode a Perplexity base64 embedding into Haystack's list[float] representation.
|
|
26
|
+
"""
|
|
27
|
+
raw_embedding = base64.b64decode(embedding)
|
|
28
|
+
if encoding_format == "base64_int8":
|
|
29
|
+
return np.frombuffer(raw_embedding, dtype=np.int8).astype(np.float32).tolist()
|
|
30
|
+
if encoding_format == "base64_binary":
|
|
31
|
+
return np.unpackbits(np.frombuffer(raw_embedding, dtype=np.uint8)).astype(np.float32).tolist()
|
|
32
|
+
|
|
33
|
+
msg = f"Unsupported encoding_format='{encoding_format}'."
|
|
34
|
+
raise ValueError(msg)
|
|
@@ -8,6 +8,12 @@ from typing import Any, ClassVar
|
|
|
8
8
|
from haystack import component, default_from_dict, default_to_dict
|
|
9
9
|
from haystack.components.embedders import OpenAITextEmbedder
|
|
10
10
|
from haystack.utils.auth import Secret
|
|
11
|
+
from openai.types.create_embedding_response import CreateEmbeddingResponse
|
|
12
|
+
|
|
13
|
+
from haystack_integrations.components.embedders.perplexity.embedding_encoding import (
|
|
14
|
+
_decode_embedding,
|
|
15
|
+
_validate_encoding_format,
|
|
16
|
+
)
|
|
11
17
|
|
|
12
18
|
_INTEGRATION_SLUG = "haystack"
|
|
13
19
|
_PACKAGE_NAME = "perplexity-haystack"
|
|
@@ -64,6 +70,7 @@ class PerplexityTextEmbedder(OpenAITextEmbedder):
|
|
|
64
70
|
api_base_url: str | None = "https://api.perplexity.ai/v1",
|
|
65
71
|
prefix: str = "",
|
|
66
72
|
suffix: str = "",
|
|
73
|
+
encoding_format: str = "base64_int8",
|
|
67
74
|
timeout: float | None = None,
|
|
68
75
|
max_retries: int | None = None,
|
|
69
76
|
http_client_kwargs: dict[str, Any] | None = None,
|
|
@@ -81,6 +88,8 @@ class PerplexityTextEmbedder(OpenAITextEmbedder):
|
|
|
81
88
|
A string to add to the beginning of each text.
|
|
82
89
|
:param suffix:
|
|
83
90
|
A string to add to the end of each text.
|
|
91
|
+
:param encoding_format:
|
|
92
|
+
The Perplexity embedding encoding format. Supported values are `base64_int8` and `base64_binary`.
|
|
84
93
|
:param timeout:
|
|
85
94
|
Timeout for Perplexity client calls. If not set, it defaults to either the `OPENAI_TIMEOUT` environment
|
|
86
95
|
variable, or 30 seconds.
|
|
@@ -92,6 +101,7 @@ class PerplexityTextEmbedder(OpenAITextEmbedder):
|
|
|
92
101
|
For more information, see the [HTTPX documentation](https://www.python-httpx.org/api/#client).
|
|
93
102
|
"""
|
|
94
103
|
|
|
104
|
+
self.encoding_format = _validate_encoding_format(encoding_format)
|
|
95
105
|
super(PerplexityTextEmbedder, self).__init__( # noqa: UP008
|
|
96
106
|
api_key=api_key,
|
|
97
107
|
model=model,
|
|
@@ -108,6 +118,18 @@ class PerplexityTextEmbedder(OpenAITextEmbedder):
|
|
|
108
118
|
self.timeout = timeout
|
|
109
119
|
self.max_retries = max_retries
|
|
110
120
|
|
|
121
|
+
def _prepare_input(self, text: str) -> dict[str, Any]:
|
|
122
|
+
kwargs = OpenAITextEmbedder._prepare_input(self, text=text)
|
|
123
|
+
kwargs["input"] = [kwargs["input"]]
|
|
124
|
+
kwargs["encoding_format"] = self.encoding_format
|
|
125
|
+
return kwargs
|
|
126
|
+
|
|
127
|
+
def _prepare_output(self, result: CreateEmbeddingResponse) -> dict[str, Any]:
|
|
128
|
+
return {
|
|
129
|
+
"embedding": _decode_embedding(str(result.data[0].embedding), self.encoding_format),
|
|
130
|
+
"meta": {"model": result.model, "usage": dict(result.usage)},
|
|
131
|
+
}
|
|
132
|
+
|
|
111
133
|
def to_dict(self) -> dict[str, Any]:
|
|
112
134
|
"""
|
|
113
135
|
Serializes the component to a dictionary.
|
|
@@ -122,6 +144,7 @@ class PerplexityTextEmbedder(OpenAITextEmbedder):
|
|
|
122
144
|
api_base_url=self.api_base_url,
|
|
123
145
|
prefix=self.prefix,
|
|
124
146
|
suffix=self.suffix,
|
|
147
|
+
encoding_format=self.encoding_format,
|
|
125
148
|
timeout=self.timeout,
|
|
126
149
|
max_retries=self.max_retries,
|
|
127
150
|
http_client_kwargs=self.http_client_kwargs,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from haystack_integrations.components.embedders.perplexity.embedding_encoding import (
|
|
8
|
+
_decode_embedding,
|
|
9
|
+
_validate_encoding_format,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_validate_encoding_format_rejects_unsupported_format():
|
|
14
|
+
with pytest.raises(ValueError) as exc_info:
|
|
15
|
+
_validate_encoding_format("base64_float16")
|
|
16
|
+
|
|
17
|
+
assert str(exc_info.value) == "Unsupported encoding_format='base64_float16'. Use 'base64_binary', 'base64_int8'."
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_decode_embedding_rejects_unsupported_format():
|
|
21
|
+
with pytest.raises(ValueError) as exc_info:
|
|
22
|
+
_decode_embedding("", "base64_float16")
|
|
23
|
+
|
|
24
|
+
assert str(exc_info.value) == "Unsupported encoding_format='base64_float16'."
|
{perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_document_embedder.py
RENAMED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import base64
|
|
5
6
|
import json
|
|
6
7
|
import os
|
|
8
|
+
from types import SimpleNamespace
|
|
7
9
|
|
|
8
10
|
import httpx
|
|
11
|
+
import numpy as np
|
|
9
12
|
import pytest
|
|
10
13
|
from haystack import Document
|
|
11
14
|
from haystack.utils import Secret
|
|
15
|
+
from openai import APIError
|
|
12
16
|
|
|
13
17
|
from haystack_integrations.components.embedders.perplexity import (
|
|
14
18
|
document_embedder as document_embedder_module,
|
|
@@ -17,17 +21,24 @@ from haystack_integrations.components.embedders.perplexity.document_embedder imp
|
|
|
17
21
|
PerplexityDocumentEmbedder,
|
|
18
22
|
)
|
|
19
23
|
|
|
24
|
+
INT8_EMBEDDING = [-2.0, -1.0, 0.0, 1.0, 2.0]
|
|
25
|
+
INT8_EMBEDDING_PAYLOAD = base64.b64encode(np.array(INT8_EMBEDDING, dtype=np.int8).tobytes()).decode()
|
|
26
|
+
BINARY_EMBEDDING = [1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0]
|
|
27
|
+
BINARY_EMBEDDING_PAYLOAD = base64.b64encode(np.packbits(np.array(BINARY_EMBEDDING, dtype=np.uint8)).tobytes()).decode()
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
|
|
30
|
+
def _make_transport(captured: list[httpx.Request], embeddings: list[str] | None = None) -> httpx.MockTransport:
|
|
22
31
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
23
32
|
captured.append(request)
|
|
33
|
+
inputs = json.loads(request.content)["input"]
|
|
34
|
+
response_embeddings = embeddings or [INT8_EMBEDDING_PAYLOAD] * len(inputs)
|
|
24
35
|
return httpx.Response(
|
|
25
36
|
200,
|
|
26
37
|
json={
|
|
27
38
|
"object": "list",
|
|
28
39
|
"data": [
|
|
29
|
-
{"object": "embedding", "embedding":
|
|
30
|
-
|
|
40
|
+
{"object": "embedding", "embedding": embedding, "index": index}
|
|
41
|
+
for index, embedding in enumerate(response_embeddings)
|
|
31
42
|
],
|
|
32
43
|
"model": "pplx-embed-v1-0.6b",
|
|
33
44
|
"usage": {"prompt_tokens": 6, "total_tokens": 6},
|
|
@@ -38,6 +49,14 @@ def _make_transport(captured: list[httpx.Request]) -> httpx.MockTransport:
|
|
|
38
49
|
return httpx.MockTransport(handler)
|
|
39
50
|
|
|
40
51
|
|
|
52
|
+
def _make_api_error() -> APIError:
|
|
53
|
+
return APIError(
|
|
54
|
+
"Failed embedding",
|
|
55
|
+
request=httpx.Request("POST", "https://api.perplexity.ai/v1/embeddings"),
|
|
56
|
+
body=None,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
41
60
|
class TestPerplexityDocumentEmbedder:
|
|
42
61
|
def test_attribution_header_falls_back_when_package_is_not_installed(self, monkeypatch):
|
|
43
62
|
def raise_package_not_found(_package_name: str) -> str:
|
|
@@ -77,6 +96,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
77
96
|
assert embedder.progress_bar is True
|
|
78
97
|
assert embedder.meta_fields_to_embed == []
|
|
79
98
|
assert embedder.embedding_separator == "\n"
|
|
99
|
+
assert embedder.encoding_format == "base64_int8"
|
|
80
100
|
|
|
81
101
|
def test_init_with_parameters(self):
|
|
82
102
|
embedder = PerplexityDocumentEmbedder(
|
|
@@ -89,6 +109,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
89
109
|
progress_bar=False,
|
|
90
110
|
meta_fields_to_embed=["test_field"],
|
|
91
111
|
embedding_separator="-",
|
|
112
|
+
encoding_format="base64_binary",
|
|
92
113
|
)
|
|
93
114
|
|
|
94
115
|
assert embedder.api_key == Secret.from_token("test-api-key")
|
|
@@ -100,6 +121,14 @@ class TestPerplexityDocumentEmbedder:
|
|
|
100
121
|
assert embedder.progress_bar is False
|
|
101
122
|
assert embedder.meta_fields_to_embed == ["test_field"]
|
|
102
123
|
assert embedder.embedding_separator == "-"
|
|
124
|
+
assert embedder.encoding_format == "base64_binary"
|
|
125
|
+
|
|
126
|
+
def test_init_rejects_float_encoding_format(self):
|
|
127
|
+
with pytest.raises(ValueError):
|
|
128
|
+
PerplexityDocumentEmbedder(
|
|
129
|
+
api_key=Secret.from_token("test-api-key"),
|
|
130
|
+
encoding_format="float",
|
|
131
|
+
)
|
|
103
132
|
|
|
104
133
|
def test_to_dict(self, monkeypatch):
|
|
105
134
|
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-api-key")
|
|
@@ -122,6 +151,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
122
151
|
"progress_bar": True,
|
|
123
152
|
"meta_fields_to_embed": [],
|
|
124
153
|
"embedding_separator": "\n",
|
|
154
|
+
"encoding_format": "base64_int8",
|
|
125
155
|
"timeout": None,
|
|
126
156
|
"max_retries": None,
|
|
127
157
|
"http_client_kwargs": None,
|
|
@@ -140,6 +170,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
140
170
|
progress_bar=False,
|
|
141
171
|
meta_fields_to_embed=["test_field"],
|
|
142
172
|
embedding_separator="-",
|
|
173
|
+
encoding_format="base64_binary",
|
|
143
174
|
timeout=10.0,
|
|
144
175
|
max_retries=2,
|
|
145
176
|
http_client_kwargs={"proxy": "http://localhost:8080"},
|
|
@@ -161,6 +192,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
161
192
|
"progress_bar": False,
|
|
162
193
|
"meta_fields_to_embed": ["test_field"],
|
|
163
194
|
"embedding_separator": "-",
|
|
195
|
+
"encoding_format": "base64_binary",
|
|
164
196
|
"timeout": 10.0,
|
|
165
197
|
"max_retries": 2,
|
|
166
198
|
"http_client_kwargs": {"proxy": "http://localhost:8080"},
|
|
@@ -183,6 +215,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
183
215
|
"progress_bar": True,
|
|
184
216
|
"meta_fields_to_embed": [],
|
|
185
217
|
"embedding_separator": "\n",
|
|
218
|
+
"encoding_format": "base64_binary",
|
|
186
219
|
"timeout": None,
|
|
187
220
|
"max_retries": None,
|
|
188
221
|
"http_client_kwargs": None,
|
|
@@ -200,6 +233,7 @@ class TestPerplexityDocumentEmbedder:
|
|
|
200
233
|
assert component.progress_bar is True
|
|
201
234
|
assert component.meta_fields_to_embed == []
|
|
202
235
|
assert component.embedding_separator == "\n"
|
|
236
|
+
assert component.encoding_format == "base64_binary"
|
|
203
237
|
assert component.timeout is None
|
|
204
238
|
assert component.max_retries is None
|
|
205
239
|
assert component.http_client_kwargs is None
|
|
@@ -222,8 +256,8 @@ class TestPerplexityDocumentEmbedder:
|
|
|
222
256
|
result = embedder.run(docs)
|
|
223
257
|
|
|
224
258
|
docs_with_embeddings = result["documents"]
|
|
225
|
-
assert docs_with_embeddings[0].embedding ==
|
|
226
|
-
assert docs_with_embeddings[1].embedding ==
|
|
259
|
+
assert docs_with_embeddings[0].embedding == INT8_EMBEDDING
|
|
260
|
+
assert docs_with_embeddings[1].embedding == INT8_EMBEDDING
|
|
227
261
|
assert len(captured) == 1
|
|
228
262
|
request = captured[0]
|
|
229
263
|
assert request.headers["Authorization"] == "Bearer test-api-key"
|
|
@@ -234,7 +268,172 @@ class TestPerplexityDocumentEmbedder:
|
|
|
234
268
|
"I love cheese",
|
|
235
269
|
"A transformer is a deep learning architecture",
|
|
236
270
|
]
|
|
237
|
-
assert body["encoding_format"] == "
|
|
271
|
+
assert body["encoding_format"] == "base64_int8"
|
|
272
|
+
|
|
273
|
+
@pytest.mark.parametrize(
|
|
274
|
+
("encoding_format", "payload", "expected_embedding"),
|
|
275
|
+
[
|
|
276
|
+
("base64_int8", INT8_EMBEDDING_PAYLOAD, INT8_EMBEDDING),
|
|
277
|
+
("base64_binary", BINARY_EMBEDDING_PAYLOAD, BINARY_EMBEDDING),
|
|
278
|
+
],
|
|
279
|
+
)
|
|
280
|
+
def test_run_decodes_base64_embeddings(self, encoding_format, payload, expected_embedding):
|
|
281
|
+
captured: list[httpx.Request] = []
|
|
282
|
+
embedder = PerplexityDocumentEmbedder(
|
|
283
|
+
api_key=Secret.from_token("test-api-key"),
|
|
284
|
+
encoding_format=encoding_format,
|
|
285
|
+
progress_bar=False,
|
|
286
|
+
http_client_kwargs={"transport": _make_transport(captured, embeddings=[payload])},
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
result = embedder.run([Document(content="I love cheese")])
|
|
290
|
+
|
|
291
|
+
embedding = result["documents"][0].embedding
|
|
292
|
+
assert embedding == expected_embedding
|
|
293
|
+
assert len(embedding) == len(expected_embedding)
|
|
294
|
+
assert all(isinstance(value, float) for value in embedding)
|
|
295
|
+
body = json.loads(captured[0].content)
|
|
296
|
+
assert body["encoding_format"] == encoding_format
|
|
297
|
+
|
|
298
|
+
def test_run_accumulates_usage_across_batches(self):
|
|
299
|
+
captured: list[httpx.Request] = []
|
|
300
|
+
embedder = PerplexityDocumentEmbedder(
|
|
301
|
+
api_key=Secret.from_token("test-api-key"),
|
|
302
|
+
batch_size=1,
|
|
303
|
+
progress_bar=False,
|
|
304
|
+
http_client_kwargs={"transport": _make_transport(captured)},
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
result = embedder.run(
|
|
308
|
+
[
|
|
309
|
+
Document(content="I love cheese"),
|
|
310
|
+
Document(content="A transformer is a deep learning architecture"),
|
|
311
|
+
]
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
assert [document.embedding for document in result["documents"]] == [INT8_EMBEDDING, INT8_EMBEDDING]
|
|
315
|
+
assert result["meta"]["usage"] == {"prompt_tokens": 12, "total_tokens": 12}
|
|
316
|
+
assert len(captured) == 2
|
|
317
|
+
|
|
318
|
+
def test_embed_batch_skips_api_error_when_raise_on_failure_is_false(self):
|
|
319
|
+
api_error = _make_api_error()
|
|
320
|
+
|
|
321
|
+
def create(**_kwargs):
|
|
322
|
+
raise api_error
|
|
323
|
+
|
|
324
|
+
embedder = PerplexityDocumentEmbedder(
|
|
325
|
+
api_key=Secret.from_token("test-api-key"),
|
|
326
|
+
progress_bar=False,
|
|
327
|
+
)
|
|
328
|
+
embedder.raise_on_failure = False
|
|
329
|
+
embedder.client = SimpleNamespace(embeddings=SimpleNamespace(create=create))
|
|
330
|
+
|
|
331
|
+
embeddings, meta = embedder._embed_batch({"doc-1": "I love cheese"}, batch_size=1)
|
|
332
|
+
|
|
333
|
+
assert embeddings == {}
|
|
334
|
+
assert meta == {}
|
|
335
|
+
|
|
336
|
+
def test_embed_batch_raises_api_error_when_raise_on_failure_is_true(self):
|
|
337
|
+
api_error = _make_api_error()
|
|
338
|
+
|
|
339
|
+
def create(**_kwargs):
|
|
340
|
+
raise api_error
|
|
341
|
+
|
|
342
|
+
embedder = PerplexityDocumentEmbedder(
|
|
343
|
+
api_key=Secret.from_token("test-api-key"),
|
|
344
|
+
progress_bar=False,
|
|
345
|
+
)
|
|
346
|
+
embedder.raise_on_failure = True
|
|
347
|
+
embedder.client = SimpleNamespace(embeddings=SimpleNamespace(create=create))
|
|
348
|
+
|
|
349
|
+
with pytest.raises(APIError) as exc_info:
|
|
350
|
+
embedder._embed_batch({"doc-1": "I love cheese"}, batch_size=1)
|
|
351
|
+
|
|
352
|
+
assert exc_info.value is api_error
|
|
353
|
+
|
|
354
|
+
@pytest.mark.asyncio
|
|
355
|
+
async def test_run_async_decodes_base64_embeddings(self):
|
|
356
|
+
captured: list[httpx.Request] = []
|
|
357
|
+
embedder = PerplexityDocumentEmbedder(
|
|
358
|
+
api_key=Secret.from_token("test-api-key"),
|
|
359
|
+
progress_bar=False,
|
|
360
|
+
http_client_kwargs={"transport": _make_transport(captured, embeddings=[BINARY_EMBEDDING_PAYLOAD])},
|
|
361
|
+
encoding_format="base64_binary",
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
result = await embedder.run_async([Document(content="I love cheese")])
|
|
365
|
+
|
|
366
|
+
assert result["documents"][0].embedding == BINARY_EMBEDDING
|
|
367
|
+
body = json.loads(captured[0].content)
|
|
368
|
+
assert body["encoding_format"] == "base64_binary"
|
|
369
|
+
|
|
370
|
+
@pytest.mark.asyncio
|
|
371
|
+
async def test_run_async_uses_progress_bar_and_accumulates_usage_across_batches(self, monkeypatch):
|
|
372
|
+
captured: list[httpx.Request] = []
|
|
373
|
+
batches_seen = []
|
|
374
|
+
|
|
375
|
+
def fake_async_tqdm(batches, desc):
|
|
376
|
+
assert desc == "Calculating embeddings"
|
|
377
|
+
batches_seen.extend(batches)
|
|
378
|
+
return batches
|
|
379
|
+
|
|
380
|
+
monkeypatch.setattr(document_embedder_module, "async_tqdm", fake_async_tqdm)
|
|
381
|
+
embedder = PerplexityDocumentEmbedder(
|
|
382
|
+
api_key=Secret.from_token("test-api-key"),
|
|
383
|
+
batch_size=1,
|
|
384
|
+
progress_bar=True,
|
|
385
|
+
http_client_kwargs={"transport": _make_transport(captured)},
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
result = await embedder.run_async(
|
|
389
|
+
[
|
|
390
|
+
Document(content="I love cheese"),
|
|
391
|
+
Document(content="A transformer is a deep learning architecture"),
|
|
392
|
+
]
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
assert [document.embedding for document in result["documents"]] == [INT8_EMBEDDING, INT8_EMBEDDING]
|
|
396
|
+
assert result["meta"]["usage"] == {"prompt_tokens": 12, "total_tokens": 12}
|
|
397
|
+
assert len(batches_seen) == 2
|
|
398
|
+
assert len(captured) == 2
|
|
399
|
+
|
|
400
|
+
@pytest.mark.asyncio
|
|
401
|
+
async def test_embed_batch_async_skips_api_error_when_raise_on_failure_is_false(self):
|
|
402
|
+
api_error = _make_api_error()
|
|
403
|
+
|
|
404
|
+
async def create(**_kwargs):
|
|
405
|
+
raise api_error
|
|
406
|
+
|
|
407
|
+
embedder = PerplexityDocumentEmbedder(
|
|
408
|
+
api_key=Secret.from_token("test-api-key"),
|
|
409
|
+
progress_bar=False,
|
|
410
|
+
)
|
|
411
|
+
embedder.raise_on_failure = False
|
|
412
|
+
embedder.async_client = SimpleNamespace(embeddings=SimpleNamespace(create=create))
|
|
413
|
+
|
|
414
|
+
embeddings, meta = await embedder._embed_batch_async({"doc-1": "I love cheese"}, batch_size=1)
|
|
415
|
+
|
|
416
|
+
assert embeddings == {}
|
|
417
|
+
assert meta == {}
|
|
418
|
+
|
|
419
|
+
@pytest.mark.asyncio
|
|
420
|
+
async def test_embed_batch_async_raises_api_error_when_raise_on_failure_is_true(self):
|
|
421
|
+
api_error = _make_api_error()
|
|
422
|
+
|
|
423
|
+
async def create(**_kwargs):
|
|
424
|
+
raise api_error
|
|
425
|
+
|
|
426
|
+
embedder = PerplexityDocumentEmbedder(
|
|
427
|
+
api_key=Secret.from_token("test-api-key"),
|
|
428
|
+
progress_bar=False,
|
|
429
|
+
)
|
|
430
|
+
embedder.raise_on_failure = True
|
|
431
|
+
embedder.async_client = SimpleNamespace(embeddings=SimpleNamespace(create=create))
|
|
432
|
+
|
|
433
|
+
with pytest.raises(APIError) as exc_info:
|
|
434
|
+
await embedder._embed_batch_async({"doc-1": "I love cheese"}, batch_size=1)
|
|
435
|
+
|
|
436
|
+
assert exc_info.value is api_error
|
|
238
437
|
|
|
239
438
|
def test_run_wrong_input_format(self):
|
|
240
439
|
embedder = PerplexityDocumentEmbedder(api_key=Secret.from_token("test-api-key"))
|
|
@@ -269,7 +468,7 @@ class TestPerplexityDocumentEmbedderInference:
|
|
|
269
468
|
assert len(embedded_docs) == len(docs)
|
|
270
469
|
for doc in embedded_docs:
|
|
271
470
|
assert isinstance(doc.embedding, list)
|
|
272
|
-
assert len(doc.embedding)
|
|
471
|
+
assert len(doc.embedding) == 1024
|
|
273
472
|
assert all(isinstance(x, float) for x in doc.embedding)
|
|
274
473
|
|
|
275
474
|
@pytest.mark.asyncio
|
|
@@ -285,4 +484,5 @@ class TestPerplexityDocumentEmbedderInference:
|
|
|
285
484
|
assert len(embedded_docs) == len(docs)
|
|
286
485
|
for doc in embedded_docs:
|
|
287
486
|
assert isinstance(doc.embedding, list)
|
|
288
|
-
assert len(doc.embedding)
|
|
487
|
+
assert len(doc.embedding) == 1024
|
|
488
|
+
assert all(isinstance(x, float) for x in doc.embedding)
|
{perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_text_embedder.py
RENAMED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import base64
|
|
5
6
|
import json
|
|
6
7
|
import os
|
|
7
8
|
|
|
8
9
|
import httpx
|
|
10
|
+
import numpy as np
|
|
9
11
|
import pytest
|
|
10
12
|
from haystack.utils import Secret
|
|
11
13
|
|
|
@@ -16,15 +18,20 @@ from haystack_integrations.components.embedders.perplexity.text_embedder import
|
|
|
16
18
|
PerplexityTextEmbedder,
|
|
17
19
|
)
|
|
18
20
|
|
|
21
|
+
INT8_EMBEDDING = [-2.0, -1.0, 0.0, 1.0, 2.0]
|
|
22
|
+
INT8_EMBEDDING_PAYLOAD = base64.b64encode(np.array(INT8_EMBEDDING, dtype=np.int8).tobytes()).decode()
|
|
23
|
+
BINARY_EMBEDDING = [1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0]
|
|
24
|
+
BINARY_EMBEDDING_PAYLOAD = base64.b64encode(np.packbits(np.array(BINARY_EMBEDDING, dtype=np.uint8)).tobytes()).decode()
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
|
|
27
|
+
def _make_transport(captured: list[httpx.Request], embedding: str = INT8_EMBEDDING_PAYLOAD) -> httpx.MockTransport:
|
|
21
28
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
22
29
|
captured.append(request)
|
|
23
30
|
return httpx.Response(
|
|
24
31
|
200,
|
|
25
32
|
json={
|
|
26
33
|
"object": "list",
|
|
27
|
-
"data": [{"object": "embedding", "embedding":
|
|
34
|
+
"data": [{"object": "embedding", "embedding": embedding, "index": 0}],
|
|
28
35
|
"model": "pplx-embed-v1-0.6b",
|
|
29
36
|
"usage": {"prompt_tokens": 3, "total_tokens": 3},
|
|
30
37
|
},
|
|
@@ -67,6 +74,7 @@ class TestPerplexityTextEmbedder:
|
|
|
67
74
|
assert embedder.model == "pplx-embed-v1-0.6b"
|
|
68
75
|
assert embedder.prefix == ""
|
|
69
76
|
assert embedder.suffix == ""
|
|
77
|
+
assert embedder.encoding_format == "base64_int8"
|
|
70
78
|
|
|
71
79
|
def test_init_with_parameters(self):
|
|
72
80
|
embedder = PerplexityTextEmbedder(
|
|
@@ -74,6 +82,7 @@ class TestPerplexityTextEmbedder:
|
|
|
74
82
|
model="pplx-embed-v1-4b",
|
|
75
83
|
prefix="START",
|
|
76
84
|
suffix="END",
|
|
85
|
+
encoding_format="base64_binary",
|
|
77
86
|
)
|
|
78
87
|
|
|
79
88
|
assert embedder.api_key == Secret.from_token("test-api-key")
|
|
@@ -81,6 +90,14 @@ class TestPerplexityTextEmbedder:
|
|
|
81
90
|
assert embedder.model == "pplx-embed-v1-4b"
|
|
82
91
|
assert embedder.prefix == "START"
|
|
83
92
|
assert embedder.suffix == "END"
|
|
93
|
+
assert embedder.encoding_format == "base64_binary"
|
|
94
|
+
|
|
95
|
+
def test_init_rejects_float_encoding_format(self):
|
|
96
|
+
with pytest.raises(ValueError):
|
|
97
|
+
PerplexityTextEmbedder(
|
|
98
|
+
api_key=Secret.from_token("test-api-key"),
|
|
99
|
+
encoding_format="float",
|
|
100
|
+
)
|
|
84
101
|
|
|
85
102
|
def test_to_dict(self, monkeypatch):
|
|
86
103
|
monkeypatch.setenv("PERPLEXITY_API_KEY", "test-api-key")
|
|
@@ -97,6 +114,7 @@ class TestPerplexityTextEmbedder:
|
|
|
97
114
|
"api_base_url": "https://api.perplexity.ai/v1",
|
|
98
115
|
"prefix": "",
|
|
99
116
|
"suffix": "",
|
|
117
|
+
"encoding_format": "base64_int8",
|
|
100
118
|
"timeout": None,
|
|
101
119
|
"max_retries": None,
|
|
102
120
|
"http_client_kwargs": None,
|
|
@@ -111,6 +129,7 @@ class TestPerplexityTextEmbedder:
|
|
|
111
129
|
api_base_url="https://custom-api-base-url.com",
|
|
112
130
|
prefix="START",
|
|
113
131
|
suffix="END",
|
|
132
|
+
encoding_format="base64_binary",
|
|
114
133
|
timeout=10.0,
|
|
115
134
|
max_retries=2,
|
|
116
135
|
http_client_kwargs={"proxy": "http://localhost:8080"},
|
|
@@ -126,6 +145,7 @@ class TestPerplexityTextEmbedder:
|
|
|
126
145
|
"api_base_url": "https://custom-api-base-url.com",
|
|
127
146
|
"prefix": "START",
|
|
128
147
|
"suffix": "END",
|
|
148
|
+
"encoding_format": "base64_binary",
|
|
129
149
|
"timeout": 10.0,
|
|
130
150
|
"max_retries": 2,
|
|
131
151
|
"http_client_kwargs": {"proxy": "http://localhost:8080"},
|
|
@@ -142,6 +162,7 @@ class TestPerplexityTextEmbedder:
|
|
|
142
162
|
"api_base_url": "https://api.perplexity.ai/v1",
|
|
143
163
|
"prefix": "",
|
|
144
164
|
"suffix": "",
|
|
165
|
+
"encoding_format": "base64_binary",
|
|
145
166
|
"timeout": None,
|
|
146
167
|
"max_retries": None,
|
|
147
168
|
"http_client_kwargs": None,
|
|
@@ -155,6 +176,7 @@ class TestPerplexityTextEmbedder:
|
|
|
155
176
|
assert component.model == "pplx-embed-v1-0.6b"
|
|
156
177
|
assert component.prefix == ""
|
|
157
178
|
assert component.suffix == ""
|
|
179
|
+
assert component.encoding_format == "base64_binary"
|
|
158
180
|
assert component.timeout is None
|
|
159
181
|
assert component.max_retries is None
|
|
160
182
|
assert component.http_client_kwargs is None
|
|
@@ -168,15 +190,54 @@ class TestPerplexityTextEmbedder:
|
|
|
168
190
|
|
|
169
191
|
result = embedder.run(text="The food was delicious")
|
|
170
192
|
|
|
171
|
-
assert result["embedding"] ==
|
|
193
|
+
assert result["embedding"] == INT8_EMBEDDING
|
|
172
194
|
assert len(captured) == 1
|
|
173
195
|
request = captured[0]
|
|
174
196
|
assert request.headers["Authorization"] == "Bearer test-api-key"
|
|
175
197
|
assert request.headers["X-Pplx-Integration"].startswith("haystack/")
|
|
176
198
|
body = json.loads(request.content)
|
|
177
199
|
assert body["model"] == "pplx-embed-v1-0.6b"
|
|
178
|
-
assert body["input"] == "The food was delicious"
|
|
179
|
-
assert body["encoding_format"] == "
|
|
200
|
+
assert body["input"] == ["The food was delicious"]
|
|
201
|
+
assert body["encoding_format"] == "base64_int8"
|
|
202
|
+
|
|
203
|
+
@pytest.mark.parametrize(
|
|
204
|
+
("encoding_format", "payload", "expected_embedding"),
|
|
205
|
+
[
|
|
206
|
+
("base64_int8", INT8_EMBEDDING_PAYLOAD, INT8_EMBEDDING),
|
|
207
|
+
("base64_binary", BINARY_EMBEDDING_PAYLOAD, BINARY_EMBEDDING),
|
|
208
|
+
],
|
|
209
|
+
)
|
|
210
|
+
def test_run_decodes_base64_embeddings(self, encoding_format, payload, expected_embedding):
|
|
211
|
+
captured: list[httpx.Request] = []
|
|
212
|
+
embedder = PerplexityTextEmbedder(
|
|
213
|
+
api_key=Secret.from_token("test-api-key"),
|
|
214
|
+
encoding_format=encoding_format,
|
|
215
|
+
http_client_kwargs={"transport": _make_transport(captured, embedding=payload)},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
result = embedder.run(text="The food was delicious")
|
|
219
|
+
|
|
220
|
+
embedding = result["embedding"]
|
|
221
|
+
assert embedding == expected_embedding
|
|
222
|
+
assert len(embedding) == len(expected_embedding)
|
|
223
|
+
assert all(isinstance(value, float) for value in embedding)
|
|
224
|
+
body = json.loads(captured[0].content)
|
|
225
|
+
assert body["encoding_format"] == encoding_format
|
|
226
|
+
|
|
227
|
+
@pytest.mark.asyncio
|
|
228
|
+
async def test_run_async_decodes_base64_embeddings(self):
|
|
229
|
+
captured: list[httpx.Request] = []
|
|
230
|
+
embedder = PerplexityTextEmbedder(
|
|
231
|
+
api_key=Secret.from_token("test-api-key"),
|
|
232
|
+
encoding_format="base64_binary",
|
|
233
|
+
http_client_kwargs={"transport": _make_transport(captured, embedding=BINARY_EMBEDDING_PAYLOAD)},
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
result = await embedder.run_async(text="The food was delicious")
|
|
237
|
+
|
|
238
|
+
assert result["embedding"] == BINARY_EMBEDDING
|
|
239
|
+
body = json.loads(captured[0].content)
|
|
240
|
+
assert body["encoding_format"] == "base64_binary"
|
|
180
241
|
|
|
181
242
|
def test_run_wrong_input_format(self):
|
|
182
243
|
embedder = PerplexityTextEmbedder(api_key=Secret.from_token("test-api-key"))
|
|
@@ -201,7 +262,7 @@ class TestPerplexityTextEmbedderInference:
|
|
|
201
262
|
result = embedder.run(text=text)
|
|
202
263
|
|
|
203
264
|
assert isinstance(result["embedding"], list)
|
|
204
|
-
assert len(result["embedding"])
|
|
265
|
+
assert len(result["embedding"]) == 1024
|
|
205
266
|
assert all(isinstance(x, float) for x in result["embedding"])
|
|
206
267
|
|
|
207
268
|
@pytest.mark.asyncio
|
|
@@ -211,4 +272,5 @@ class TestPerplexityTextEmbedderInference:
|
|
|
211
272
|
result = await embedder.run_async(text=text)
|
|
212
273
|
|
|
213
274
|
assert isinstance(result["embedding"], list)
|
|
214
|
-
assert len(result["embedding"])
|
|
275
|
+
assert len(result["embedding"]) == 1024
|
|
276
|
+
assert all(isinstance(x, float) for x in result["embedding"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{perplexity_haystack-0.1.0 → perplexity_haystack-0.1.1}/tests/test_perplexity_chat_generator.py
RENAMED
|
File without changes
|
|
File without changes
|