Skip to content

Commit 2eb0ca8

Browse files
authored
chore!: nvidia - drop Python 3.9 and use X|Y typing; fix default reranking model; improve tests (#2736)
* make test more robust * chore!: nvidia - drop Python 3.9 and use X|Y typing * try changing default model * improve test * fix fma
1 parent 79ba646 commit 2eb0ca8

12 files changed

Lines changed: 72 additions & 79 deletions

File tree

integrations/nvidia/pyproject.toml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,22 @@ name = "nvidia-haystack"
77
dynamic = ["version"]
88
description = ''
99
readme = "README.md"
10-
requires-python = ">=3.9"
10+
requires-python = ">=3.10"
1111
license = "Apache-2.0"
1212
keywords = []
1313
authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }]
1414
classifiers = [
1515
"License :: OSI Approved :: Apache Software License",
1616
"Development Status :: 4 - Beta",
1717
"Programming Language :: Python",
18-
"Programming Language :: Python :: 3.9",
1918
"Programming Language :: Python :: 3.10",
2019
"Programming Language :: Python :: 3.11",
2120
"Programming Language :: Python :: 3.12",
2221
"Programming Language :: Python :: 3.13",
2322
"Programming Language :: Python :: Implementation :: CPython",
2423
"Programming Language :: Python :: Implementation :: PyPy",
2524
]
26-
dependencies = ["haystack-ai>=2.19.0", "requests>=2.25.0", "tqdm>=4.21.0"]
25+
dependencies = ["haystack-ai>=2.22.0", "requests>=2.25.0", "tqdm>=4.21.0"]
2726

2827
[project.urls]
2928
Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/nvidia#readme"
@@ -82,7 +81,6 @@ disallow_incomplete_defs = true
8281

8382

8483
[tool.ruff]
85-
target-version = "py39"
8684
line-length = 120
8785

8886
[tool.ruff.lint]
@@ -129,10 +127,6 @@ ignore = [
129127
"B008",
130128
"S101",
131129
]
132-
unfixable = [
133-
# Don't touch unused imports
134-
"F401",
135-
]
136130

137131
[tool.ruff.lint.isort]
138132
known-first-party = ["haystack_integrations"]

integrations/nvidia/src/haystack_integrations/components/embedders/nvidia/document_embedder.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import os
66
import warnings
77
from dataclasses import replace
8-
from typing import Any, Optional, Union
8+
from typing import Any
99

1010
from haystack import Document, component, default_from_dict, default_to_dict, logging
1111
from haystack.utils import Secret, deserialize_secrets_inplace
@@ -39,17 +39,17 @@ class NvidiaDocumentEmbedder:
3939

4040
def __init__(
4141
self,
42-
model: Optional[str] = None,
43-
api_key: Optional[Secret] = Secret.from_env_var("NVIDIA_API_KEY"),
42+
model: str | None = None,
43+
api_key: Secret | None = Secret.from_env_var("NVIDIA_API_KEY"),
4444
api_url: str = os.getenv("NVIDIA_API_URL", DEFAULT_API_URL),
4545
prefix: str = "",
4646
suffix: str = "",
4747
batch_size: int = 32,
4848
progress_bar: bool = True,
49-
meta_fields_to_embed: Optional[list[str]] = None,
49+
meta_fields_to_embed: list[str] | None = None,
5050
embedding_separator: str = "\n",
51-
truncate: Optional[Union[EmbeddingTruncateMode, str]] = None,
52-
timeout: Optional[float] = None,
51+
truncate: EmbeddingTruncateMode | str | None = None,
52+
timeout: float | None = None,
5353
) -> None:
5454
"""
5555
Create a NvidiaTextEmbedder component.
@@ -98,7 +98,7 @@ def __init__(
9898
truncate = EmbeddingTruncateMode.from_str(truncate)
9999
self.truncate = truncate
100100

101-
self.backend: Optional[Any] = None
101+
self.backend: Any | None = None
102102
self._initialized = False
103103

104104
if timeout is None:
@@ -235,7 +235,7 @@ def _embed_batch(self, texts_to_embed: list[str], batch_size: int) -> tuple[list
235235
return all_embeddings, {"usage": {"prompt_tokens": usage_prompt_tokens, "total_tokens": usage_total_tokens}}
236236

237237
@component.output_types(documents=list[Document], meta=dict[str, Any])
238-
def run(self, documents: list[Document]) -> dict[str, Union[list[Document], dict[str, Any]]]:
238+
def run(self, documents: list[Document]) -> dict[str, list[Document] | dict[str, Any]]:
239239
"""
240240
Embed a list of Documents.
241241
@@ -268,7 +268,7 @@ def run(self, documents: list[Document]) -> dict[str, Union[list[Document], dict
268268
embeddings, metadata = self._embed_batch(texts_to_embed, self.batch_size)
269269

270270
new_documents = []
271-
for doc, emb in zip(documents, embeddings):
271+
for doc, emb in zip(documents, embeddings, strict=True):
272272
new_documents.append(replace(doc, embedding=emb))
273273

274274
return {"documents": new_documents, "meta": metadata}

integrations/nvidia/src/haystack_integrations/components/embedders/nvidia/text_embedder.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import os
66
import warnings
7-
from typing import Any, Optional, Union
7+
from typing import Any
88

99
from haystack import component, default_from_dict, default_to_dict, logging
1010
from haystack.utils import Secret, deserialize_secrets_inplace
@@ -39,13 +39,13 @@ class NvidiaTextEmbedder:
3939

4040
def __init__(
4141
self,
42-
model: Optional[str] = None,
43-
api_key: Optional[Secret] = Secret.from_env_var("NVIDIA_API_KEY"),
42+
model: str | None = None,
43+
api_key: Secret | None = Secret.from_env_var("NVIDIA_API_KEY"),
4444
api_url: str = os.getenv("NVIDIA_API_URL", DEFAULT_API_URL),
4545
prefix: str = "",
4646
suffix: str = "",
47-
truncate: Optional[Union[EmbeddingTruncateMode, str]] = None,
48-
timeout: Optional[float] = None,
47+
truncate: EmbeddingTruncateMode | str | None = None,
48+
timeout: float | None = None,
4949
):
5050
"""
5151
Create a NvidiaTextEmbedder component.
@@ -81,7 +81,7 @@ def __init__(
8181
truncate = EmbeddingTruncateMode.from_str(truncate)
8282
self.truncate = truncate
8383

84-
self.backend: Optional[Any] = None
84+
self.backend: Any | None = None
8585
self._initialized = False
8686

8787
if timeout is None:
@@ -187,7 +187,7 @@ def from_dict(cls, data: dict[str, Any]) -> "NvidiaTextEmbedder":
187187
return default_from_dict(cls, data)
188188

189189
@component.output_types(embedding=list[float], meta=dict[str, Any])
190-
def run(self, text: str) -> dict[str, Union[list[float], dict[str, Any]]]:
190+
def run(self, text: str) -> dict[str, list[float] | dict[str, Any]]:
191191
"""
192192
Embed a string.
193193

integrations/nvidia/src/haystack_integrations/components/generators/nvidia/chat/chat_generator.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
import os
6-
from typing import Any, Optional
6+
from typing import Any
77

88
from haystack import component, default_to_dict, logging
99
from haystack.components.generators.chat import OpenAIChatGenerator
@@ -53,13 +53,13 @@ def __init__(
5353
*,
5454
api_key: Secret = Secret.from_env_var("NVIDIA_API_KEY"),
5555
model: str = "meta/llama-3.1-8b-instruct",
56-
streaming_callback: Optional[StreamingCallbackT] = None,
57-
api_base_url: Optional[str] = os.getenv("NVIDIA_API_URL", DEFAULT_API_URL),
58-
generation_kwargs: Optional[dict[str, Any]] = None,
59-
tools: Optional[ToolsType] = None,
60-
timeout: Optional[float] = None,
61-
max_retries: Optional[int] = None,
62-
http_client_kwargs: Optional[dict[str, Any]] = None,
56+
streaming_callback: StreamingCallbackT | None = None,
57+
api_base_url: str | None = os.getenv("NVIDIA_API_URL", DEFAULT_API_URL),
58+
generation_kwargs: dict[str, Any] | None = None,
59+
tools: ToolsType | None = None,
60+
timeout: float | None = None,
61+
max_retries: int | None = None,
62+
http_client_kwargs: dict[str, Any] | None = None,
6363
) -> None:
6464
"""
6565
Creates an instance of NvidiaChatGenerator.

integrations/nvidia/src/haystack_integrations/components/generators/nvidia/generator.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import os
66
import warnings
7-
from typing import Any, Optional, Union
7+
from typing import Any
88

99
from haystack import component, default_from_dict, default_to_dict
1010
from haystack.utils.auth import Secret, deserialize_secrets_inplace
@@ -44,11 +44,11 @@ class NvidiaGenerator:
4444

4545
def __init__(
4646
self,
47-
model: Optional[str] = None,
47+
model: str | None = None,
4848
api_url: str = os.getenv("NVIDIA_API_URL", DEFAULT_API_URL),
49-
api_key: Optional[Secret] = Secret.from_env_var("NVIDIA_API_KEY"),
50-
model_arguments: Optional[dict[str, Any]] = None,
51-
timeout: Optional[float] = None,
49+
api_key: Secret | None = Secret.from_env_var("NVIDIA_API_KEY"),
50+
model_arguments: dict[str, Any] | None = None,
51+
timeout: float | None = None,
5252
) -> None:
5353
"""
5454
Create a NvidiaGenerator component.
@@ -79,7 +79,7 @@ def __init__(
7979
self._api_key = api_key
8080
self._model_arguments = model_arguments or {}
8181

82-
self.backend: Optional[Any] = None
82+
self.backend: Any | None = None
8383

8484
self.is_hosted = is_hosted(api_url)
8585
if timeout is None:
@@ -171,7 +171,7 @@ def from_dict(cls, data: dict[str, Any]) -> "NvidiaGenerator":
171171
return default_from_dict(cls, data)
172172

173173
@component.output_types(replies=list[str], meta=list[dict[str, Any]])
174-
def run(self, prompt: str) -> dict[str, Union[list[str], list[dict[str, Any]]]]:
174+
def run(self, prompt: str) -> dict[str, list[str] | list[dict[str, Any]]]:
175175
"""
176176
Queries the model with the provided prompt.
177177

integrations/nvidia/src/haystack_integrations/components/rankers/nvidia/ranker.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import os
66
import warnings
7-
from typing import Any, Optional, Union
7+
from typing import Any
88

99
from haystack import Document, component, default_from_dict, default_to_dict, logging
1010
from haystack.utils import Secret, deserialize_secrets_inplace
@@ -47,16 +47,16 @@ class NvidiaRanker:
4747

4848
def __init__(
4949
self,
50-
model: Optional[str] = None,
51-
truncate: Optional[Union[RankerTruncateMode, str]] = None,
50+
model: str | None = None,
51+
truncate: RankerTruncateMode | str | None = None,
5252
api_url: str = os.getenv("NVIDIA_API_URL", DEFAULT_API_URL),
53-
api_key: Optional[Secret] = Secret.from_env_var("NVIDIA_API_KEY"),
53+
api_key: Secret | None = Secret.from_env_var("NVIDIA_API_KEY"),
5454
top_k: int = 5,
5555
query_prefix: str = "",
5656
document_prefix: str = "",
57-
meta_fields_to_embed: Optional[list[str]] = None,
57+
meta_fields_to_embed: list[str] | None = None,
5858
embedding_separator: str = "\n",
59-
timeout: Optional[float] = None,
59+
timeout: float | None = None,
6060
) -> None:
6161
"""
6262
Create a NvidiaRanker component.
@@ -107,7 +107,7 @@ def __init__(
107107
self.api_url = url_validation(api_url)
108108
self.top_k = top_k
109109
self._initialized = False
110-
self.backend: Optional[Any] = None
110+
self.backend: Any | None = None
111111
self.is_hosted = is_hosted(api_url)
112112

113113
self.query_prefix = query_prefix
@@ -192,7 +192,7 @@ def _prepare_documents_to_embed(self, documents: list[Document]) -> list[str]:
192192
return document_texts
193193

194194
@component.output_types(documents=list[Document])
195-
def run(self, query: str, documents: list[Document], top_k: Optional[int] = None) -> dict[str, list[Document]]:
195+
def run(self, query: str, documents: list[Document], top_k: int | None = None) -> dict[str, list[Document]]:
196196
"""
197197
Rank a list of documents based on a given query.
198198

integrations/nvidia/src/haystack_integrations/utils/nvidia/models.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
from dataclasses import dataclass
6-
from typing import Literal, Optional, Union
6+
from typing import Literal
77

88
from .client import Client
99

@@ -24,13 +24,13 @@ class Model:
2424
"""
2525

2626
id: str
27-
model_type: Optional[Literal["chat", "embedding", "ranking"]] = None
28-
client: Optional[Union[Client, str]] = None
29-
endpoint: Optional[str] = None
30-
aliases: Optional[list] = None
31-
base_model: Optional[str] = None
32-
supports_tools: Optional[bool] = False
33-
supports_structured_output: Optional[bool] = False
27+
model_type: Literal["chat", "embedding", "ranking"] | None = None
28+
client: Client | str | None = None
29+
endpoint: str | None = None
30+
aliases: list | None = None
31+
base_model: str | None = None
32+
supports_tools: bool | None = False
33+
supports_structured_output: bool | None = False
3434

3535
def __hash__(self) -> int:
3636
return hash(self.id)
@@ -480,7 +480,7 @@ def validate(self):
480480

481481
DEFAULT_MODELS = {
482482
"embedding": "nvidia/nv-embedqa-e5-v5",
483-
"ranking": "nvidia/nv-rerankqa-mistral-4b-v3",
483+
"ranking": "nv-rerank-qa-mistral-4b:1",
484484
"chat": "meta/llama3-8b-instruct",
485485
}
486486

integrations/nvidia/src/haystack_integrations/utils/nvidia/nim_backend.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import os
66
import warnings
7-
from typing import Any, Literal, Optional, Union
7+
from typing import Any, Literal
88

99
import requests
1010
from haystack import logging
@@ -23,12 +23,12 @@ class NimBackend:
2323
def __init__(
2424
self,
2525
api_url: str,
26-
model_type: Optional[Literal["chat", "embedding", "ranking"]] = None,
27-
model: Optional[str] = None,
28-
api_key: Optional[Secret] = Secret.from_env_var("NVIDIA_API_KEY"),
29-
model_kwargs: Optional[dict[str, Any]] = None,
30-
client: Optional[Union[str, Client]] = None,
31-
timeout: Optional[float] = None,
26+
model_type: Literal["chat", "embedding", "ranking"] | None = None,
27+
model: str | None = None,
28+
api_key: Secret | None = Secret.from_env_var("NVIDIA_API_KEY"),
29+
model_kwargs: dict[str, Any] | None = None,
30+
client: str | Client | None = None,
31+
timeout: float | None = None,
3232
):
3333
headers = {
3434
"Content-Type": "application/json",
@@ -44,7 +44,7 @@ def __init__(
4444
self.api_url = api_url
4545
if isinstance(client, str):
4646
client = Client.from_str(client)
47-
validated_model: Optional[Model] = None
47+
validated_model: Model | None = None
4848
if is_hosted(self.api_url):
4949
if not api_key:
5050
warnings.warn(

integrations/nvidia/src/haystack_integrations/utils/nvidia/utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
import warnings
6-
from typing import Optional
76
from urllib.parse import urlparse, urlunparse
87

98
from .client import Client
@@ -47,7 +46,7 @@ def is_hosted(api_url: str) -> bool:
4746
]
4847

4948

50-
def lookup_model(name: str) -> Optional[Model]:
49+
def lookup_model(name: str) -> Model | None:
5150
"""
5251
Lookup a model by name, using only the table of known models.
5352
The name is either:
@@ -65,7 +64,7 @@ def lookup_model(name: str) -> Optional[Model]:
6564
return model
6665

6766

68-
def determine_model(name: str) -> Optional[Model]:
67+
def determine_model(name: str) -> Model | None:
6968
"""
7069
Determine the model to use based on a name, using
7170
only the table of known models.
@@ -85,7 +84,7 @@ def determine_model(name: str) -> Optional[Model]:
8584

8685
def validate_hosted_model(
8786
model_name: str,
88-
client: Optional[Client] = None,
87+
client: Client | None = None,
8988
) -> Model:
9089
"""
9190
Checks if a given model is compatible with given client.

integrations/nvidia/tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

5-
from typing import Any, Optional
5+
from typing import Any
66

77
import pytest
88
from haystack.utils import Secret
@@ -12,7 +12,7 @@
1212

1313

1414
class MockBackend(NimBackend):
15-
def __init__(self, model: str, api_key: Optional[Secret] = None, model_kwargs: Optional[dict[str, Any]] = None):
15+
def __init__(self, model: str, api_key: Secret | None = None, model_kwargs: dict[str, Any] | None = None):
1616
api_key = api_key or Secret.from_env_var("NVIDIA_API_KEY")
1717
super().__init__(api_url="", model=model, api_key=api_key, model_kwargs=model_kwargs or {})
1818

0 commit comments

Comments
 (0)