mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-17 15:35:14 +00:00
partners/voyageai: enable setting output dimension (#28740)
Voyage has introduced voyage-3-large and voyage-code-3, which feature different output dimensions by leveraging a technique called "Matryoshka Embeddings" (see blog - https://blog.voyageai.com/2024/12/04/voyage-code-3/). These two models are available in various sizes: [256, 512, 1024, 2048] (https://docs.voyageai.com/docs/embeddings#model-choices). This PR adds the option to set the required output dimension.
This commit is contained in:
committed by
GitHub
parent
0afc284920
commit
f8883a1321
@@ -1,5 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Iterable, List, Optional
|
from typing import Any, Iterable, List, Literal, Optional, cast
|
||||||
|
|
||||||
import voyageai # type: ignore
|
import voyageai # type: ignore
|
||||||
from langchain_core.embeddings import Embeddings
|
from langchain_core.embeddings import Embeddings
|
||||||
@@ -37,8 +37,10 @@ class VoyageAIEmbeddings(BaseModel, Embeddings):
|
|||||||
_aclient: voyageai.client_async.AsyncClient = PrivateAttr()
|
_aclient: voyageai.client_async.AsyncClient = PrivateAttr()
|
||||||
model: str
|
model: str
|
||||||
batch_size: int
|
batch_size: int
|
||||||
|
|
||||||
|
output_dimension: Optional[Literal[256, 512, 1024, 2048]] = None
|
||||||
show_progress_bar: bool = False
|
show_progress_bar: bool = False
|
||||||
truncation: Optional[bool] = None
|
truncation: bool = True
|
||||||
voyage_api_key: SecretStr = Field(
|
voyage_api_key: SecretStr = Field(
|
||||||
alias="api_key",
|
alias="api_key",
|
||||||
default_factory=secret_from_env(
|
default_factory=secret_from_env(
|
||||||
@@ -105,22 +107,26 @@ class VoyageAIEmbeddings(BaseModel, Embeddings):
|
|||||||
|
|
||||||
_iter = self._get_batch_iterator(texts)
|
_iter = self._get_batch_iterator(texts)
|
||||||
for i in _iter:
|
for i in _iter:
|
||||||
embeddings.extend(
|
r = self._client.embed(
|
||||||
self._client.embed(
|
|
||||||
texts[i : i + self.batch_size],
|
texts[i : i + self.batch_size],
|
||||||
model=self.model,
|
model=self.model,
|
||||||
input_type="document",
|
input_type="document",
|
||||||
truncation=self.truncation,
|
truncation=self.truncation,
|
||||||
|
output_dimension=self.output_dimension,
|
||||||
).embeddings
|
).embeddings
|
||||||
)
|
embeddings.extend(cast(Iterable[List[float]], r))
|
||||||
|
|
||||||
return embeddings
|
return embeddings
|
||||||
|
|
||||||
def embed_query(self, text: str) -> List[float]:
|
def embed_query(self, text: str) -> List[float]:
|
||||||
"""Embed query text."""
|
"""Embed query text."""
|
||||||
return self._client.embed(
|
r = self._client.embed(
|
||||||
[text], model=self.model, input_type="query", truncation=self.truncation
|
[text],
|
||||||
|
model=self.model,
|
||||||
|
input_type="query",
|
||||||
|
truncation=self.truncation,
|
||||||
|
output_dimension=self.output_dimension,
|
||||||
).embeddings[0]
|
).embeddings[0]
|
||||||
|
return cast(List[float], r)
|
||||||
|
|
||||||
async def aembed_documents(self, texts: List[str]) -> List[List[float]]:
|
async def aembed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||||
embeddings: List[List[float]] = []
|
embeddings: List[List[float]] = []
|
||||||
@@ -132,8 +138,9 @@ class VoyageAIEmbeddings(BaseModel, Embeddings):
|
|||||||
model=self.model,
|
model=self.model,
|
||||||
input_type="document",
|
input_type="document",
|
||||||
truncation=self.truncation,
|
truncation=self.truncation,
|
||||||
|
output_dimension=self.output_dimension,
|
||||||
)
|
)
|
||||||
embeddings.extend(r.embeddings)
|
embeddings.extend(cast(Iterable[List[float]], r.embeddings))
|
||||||
|
|
||||||
return embeddings
|
return embeddings
|
||||||
|
|
||||||
@@ -143,5 +150,6 @@ class VoyageAIEmbeddings(BaseModel, Embeddings):
|
|||||||
model=self.model,
|
model=self.model,
|
||||||
input_type="query",
|
input_type="query",
|
||||||
truncation=self.truncation,
|
truncation=self.truncation,
|
||||||
|
output_dimension=self.output_dimension,
|
||||||
)
|
)
|
||||||
return r.embeddings[0]
|
return cast(List[float], r.embeddings[0])
|
||||||
|
@@ -16,8 +16,8 @@ from voyageai.object import RerankingObject # type: ignore
|
|||||||
class VoyageAIRerank(BaseDocumentCompressor):
|
class VoyageAIRerank(BaseDocumentCompressor):
|
||||||
"""Document compressor that uses `VoyageAI Rerank API`."""
|
"""Document compressor that uses `VoyageAI Rerank API`."""
|
||||||
|
|
||||||
client: voyageai.Client = None
|
client: voyageai.Client = None # type: ignore
|
||||||
aclient: voyageai.AsyncClient = None
|
aclient: voyageai.AsyncClient = None # type: ignore
|
||||||
"""VoyageAI clients to use for compressing documents."""
|
"""VoyageAI clients to use for compressing documents."""
|
||||||
voyage_api_key: Optional[SecretStr] = None
|
voyage_api_key: Optional[SecretStr] = None
|
||||||
"""VoyageAI API key. Must be specified directly or via environment variable
|
"""VoyageAI API key. Must be specified directly or via environment variable
|
||||||
|
1697
libs/partners/voyageai/poetry.lock
generated
1697
libs/partners/voyageai/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,9 @@ disallow_untyped_defs = "True"
|
|||||||
"Release Notes" = "https://github.com/langchain-ai/langchain/releases?q=tag%3A%22langchain-voyageai%3D%3D0%22&expanded=true"
|
"Release Notes" = "https://github.com/langchain-ai/langchain/releases?q=tag%3A%22langchain-voyageai%3D%3D0%22&expanded=true"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = ">=3.9,<4.0"
|
python = ">=3.9,<3.13"
|
||||||
langchain-core = "^0.3.15"
|
langchain-core = "^0.3.15"
|
||||||
voyageai = ">=0.2.1,<1"
|
voyageai = ">=0.3.2,<1"
|
||||||
pydantic = ">=2,<3"
|
pydantic = ">=2,<3"
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
|
@@ -51,3 +51,12 @@ async def test_langchain_voyageai_async_embedding_query() -> None:
|
|||||||
embedding = VoyageAIEmbeddings(model=MODEL) # type: ignore[call-arg]
|
embedding = VoyageAIEmbeddings(model=MODEL) # type: ignore[call-arg]
|
||||||
output = await embedding.aembed_query(document)
|
output = await embedding.aembed_query(document)
|
||||||
assert len(output) == 1024
|
assert len(output) == 1024
|
||||||
|
|
||||||
|
|
||||||
|
def test_langchain_voyageai_embedding_documents_with_output_dimension() -> None:
|
||||||
|
"""Test voyage embeddings."""
|
||||||
|
documents = ["foo bar"]
|
||||||
|
embedding = VoyageAIEmbeddings(model="voyage-3-large", output_dimension=256) # type: ignore[call-arg]
|
||||||
|
output = embedding.embed_documents(documents)
|
||||||
|
assert len(output) == 1
|
||||||
|
assert len(output[0]) == 256
|
||||||
|
@@ -47,3 +47,15 @@ def test_initialization_voyage_1_batch_size() -> None:
|
|||||||
assert emb.batch_size == 15
|
assert emb.batch_size == 15
|
||||||
assert emb.model == "voyage-01"
|
assert emb.model == "voyage-01"
|
||||||
assert emb._client is not None
|
assert emb._client is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_initialization_with_output_dimension() -> None:
|
||||||
|
emb = VoyageAIEmbeddings(
|
||||||
|
api_key="NOT_A_VALID_KEY", # type: ignore
|
||||||
|
model="voyage-3-large",
|
||||||
|
output_dimension=256,
|
||||||
|
batch_size=10,
|
||||||
|
)
|
||||||
|
assert isinstance(emb, Embeddings)
|
||||||
|
assert emb.model == "voyage-3-large"
|
||||||
|
assert emb.output_dimension == 256
|
||||||
|
Reference in New Issue
Block a user