Skip to content

Commit 3d2f1b8

Browse files
authored
chore!: cohere - drop Python 3.9 and use X|Y typing (#2696)
1 parent 18cef47 commit 3d2f1b8

11 files changed

Lines changed: 61 additions & 63 deletions

File tree

integrations/cohere/pyproject.toml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,22 @@ name = "cohere-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", "cohere>=5.17.0"]
25+
dependencies = ["haystack-ai>=2.22.0", "cohere>=5.17.0"]
2726

2827
[project.urls]
2928
Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/cohere#readme"
@@ -79,7 +78,6 @@ check_untyped_defs = true
7978
disallow_incomplete_defs = true
8079

8180
[tool.ruff]
82-
target-version = "py39"
8381
line-length = 120
8482

8583
[tool.ruff.lint]
@@ -126,10 +124,6 @@ ignore = [
126124
"B008",
127125
"S101",
128126
]
129-
unfixable = [
130-
# Don't touch unused imports
131-
"F401",
132-
]
133127

134128
[tool.ruff.lint.isort]
135129
known-first-party = ["haystack_integrations"]

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
22
#
33
# SPDX-License-Identifier: Apache-2.0
4-
from typing import Any, Optional, Union
4+
from typing import Any
55

66
from haystack import Document, component, default_from_dict, default_to_dict
77
from haystack.utils import Secret, deserialize_secrets_inplace
@@ -45,9 +45,9 @@ def __init__(
4545
timeout: float = 120.0,
4646
batch_size: int = 32,
4747
progress_bar: bool = True,
48-
meta_fields_to_embed: Optional[list[str]] = None,
48+
meta_fields_to_embed: list[str] | None = None,
4949
embedding_separator: str = "\n",
50-
embedding_type: Optional[EmbeddingTypes] = None,
50+
embedding_type: EmbeddingTypes | None = None,
5151
):
5252
"""
5353
:param api_key: the Cohere API key.
@@ -167,7 +167,7 @@ def _prepare_texts_to_embed(self, documents: list[Document]) -> list[str]:
167167
return texts_to_embed
168168

169169
@component.output_types(documents=list[Document], meta=dict[str, Any])
170-
def run(self, documents: list[Document]) -> dict[str, Union[list[Document], dict[str, Any]]]:
170+
def run(self, documents: list[Document]) -> dict[str, list[Document] | dict[str, Any]]:
171171
"""Embed a list of `Documents`.
172172
173173
:param documents: documents to embed.
@@ -195,13 +195,13 @@ def run(self, documents: list[Document]) -> dict[str, Union[list[Document], dict
195195
self.embedding_type,
196196
)
197197

198-
for doc, embeddings in zip(documents, all_embeddings):
198+
for doc, embeddings in zip(documents, all_embeddings, strict=True):
199199
doc.embedding = embeddings
200200

201201
return {"documents": documents, "meta": metadata}
202202

203203
@component.output_types(documents=list[Document], meta=dict[str, Any])
204-
async def run_async(self, documents: list[Document]) -> dict[str, Union[list[Document], dict[str, Any]]]:
204+
async def run_async(self, documents: list[Document]) -> dict[str, list[Document] | dict[str, Any]]:
205205
"""
206206
Embed a list of `Documents` asynchronously.
207207
@@ -228,7 +228,7 @@ async def run_async(self, documents: list[Document]) -> dict[str, Union[list[Doc
228228
embedding_type=self.embedding_type,
229229
)
230230

231-
for doc, embeddings in zip(documents, all_embeddings):
231+
for doc, embeddings in zip(documents, all_embeddings, strict=True):
232232
doc.embedding = embeddings
233233

234234
return {"documents": documents, "meta": metadata}

integrations/cohere/src/haystack_integrations/components/embedders/cohere/document_image_embedder.py

Lines changed: 13 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 replace
6-
from typing import Any, Optional
6+
from typing import Any
77

88
from haystack import Document, component, default_from_dict, default_to_dict, logging
99
from haystack.components.converters.image.image_utils import (
@@ -63,13 +63,13 @@ def __init__(
6363
self,
6464
*,
6565
file_path_meta_field: str = "file_path",
66-
root_path: Optional[str] = None,
67-
image_size: Optional[tuple[int, int]] = None,
66+
root_path: str | None = None,
67+
image_size: tuple[int, int] | None = None,
6868
api_key: Secret = Secret.from_env_var(["COHERE_API_KEY", "CO_API_KEY"]),
6969
model: str = "embed-v4.0",
7070
api_base_url: str = "https://api.cohere.com",
7171
timeout: float = 120.0,
72-
embedding_dimension: Optional[int] = None,
72+
embedding_dimension: int | None = None,
7373
embedding_type: EmbeddingTypes = EmbeddingTypes.FLOAT,
7474
progress_bar: bool = True,
7575
) -> None:
@@ -205,7 +205,7 @@ def _extract_images_to_embed(self, documents: list[Document]) -> list[str]:
205205
)
206206
raise ValueError(msg)
207207

208-
images_to_embed: list[Optional[str]] = [None] * len(documents)
208+
images_to_embed: list[str | None] = [None] * len(documents)
209209
pdf_page_infos: list[_PDFPageInfo] = []
210210

211211
for doc_idx, image_source_info in enumerate(images_source_info):
@@ -259,7 +259,9 @@ def run(self, documents: list[Document]) -> dict[str, list[Document]]:
259259
embeddings = []
260260

261261
# The Cohere API only supports passing one image at a time
262-
for doc, image in tqdm(zip(documents, images_to_embed), desc="Embedding images", disable=not self.progress_bar):
262+
for doc, image in tqdm(
263+
zip(documents, images_to_embed, strict=True), desc="Embedding images", disable=not self.progress_bar
264+
):
263265
try:
264266
response = self._client.embed(
265267
model=self.model,
@@ -276,7 +278,7 @@ def run(self, documents: list[Document]) -> dict[str, list[Document]]:
276278
embeddings.append(embedding)
277279

278280
docs_with_embeddings = []
279-
for doc, emb in zip(documents, embeddings):
281+
for doc, emb in zip(documents, embeddings, strict=True):
280282
# we store this information for later inspection
281283
new_meta = {
282284
**doc.meta,
@@ -305,7 +307,9 @@ async def run_async(self, documents: list[Document]) -> dict[str, list[Document]
305307
embeddings = []
306308

307309
# The Cohere API only supports passing one image at a time
308-
for doc, image in tqdm(zip(documents, images_to_embed), desc="Embedding images", disable=not self.progress_bar):
310+
for doc, image in tqdm(
311+
zip(documents, images_to_embed, strict=True), desc="Embedding images", disable=not self.progress_bar
312+
):
309313
try:
310314
response = await self._async_client.embed(
311315
model=self.model,
@@ -322,7 +326,7 @@ async def run_async(self, documents: list[Document]) -> dict[str, list[Document]
322326
embeddings.append(embedding)
323327

324328
docs_with_embeddings = []
325-
for doc, emb in zip(documents, embeddings):
329+
for doc, emb in zip(documents, embeddings, strict=True):
326330
# we store this information for later inspection
327331
new_meta = {
328332
**doc.meta,

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
22
#
33
# SPDX-License-Identifier: Apache-2.0
4-
from typing import Any, Optional, Union
4+
from typing import Any
55

66
from haystack import component, default_from_dict, default_to_dict
77
from haystack.utils import Secret, deserialize_secrets_inplace
@@ -40,7 +40,7 @@ def __init__(
4040
api_base_url: str = "https://api.cohere.com",
4141
truncate: str = "END",
4242
timeout: float = 120.0,
43-
embedding_type: Optional[EmbeddingTypes] = None,
43+
embedding_type: EmbeddingTypes | None = None,
4444
):
4545
"""
4646
:param api_key: the Cohere API key.
@@ -134,7 +134,7 @@ def from_dict(cls, data: dict[str, Any]) -> "CohereTextEmbedder":
134134
return default_from_dict(cls, data)
135135

136136
@component.output_types(embedding=list[float], meta=dict[str, Any])
137-
def run(self, text: str) -> dict[str, Union[list[float], dict[str, Any]]]:
137+
def run(self, text: str) -> dict[str, list[float] | dict[str, Any]]:
138138
"""
139139
Embed text.
140140
@@ -161,7 +161,7 @@ def run(self, text: str) -> dict[str, Union[list[float], dict[str, Any]]]:
161161
return {"embedding": embedding[0], "meta": metadata}
162162

163163
@component.output_types(embedding=list[float], meta=dict[str, Any])
164-
async def run_async(self, text: str) -> dict[str, Union[list[float], dict[str, Any]]]:
164+
async def run_async(self, text: str) -> dict[str, list[float] | dict[str, Any]]:
165165
"""
166166
Asynchronously embed text.
167167

integrations/cohere/src/haystack_integrations/components/embedders/cohere/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
22
#
33
# SPDX-License-Identifier: Apache-2.0
4-
from typing import Any, Optional
4+
from typing import Any
55

66
from tqdm import tqdm
77

@@ -16,7 +16,7 @@ async def get_async_response(
1616
model_name: str,
1717
input_type: str,
1818
truncate: str,
19-
embedding_type: Optional[EmbeddingTypes] = None,
19+
embedding_type: EmbeddingTypes | None = None,
2020
) -> tuple[list[list[float]], dict[str, Any]]:
2121
"""Embeds a list of texts asynchronously using the Cohere API.
2222
@@ -64,7 +64,7 @@ def get_response(
6464
truncate: str,
6565
batch_size: int = 32,
6666
progress_bar: bool = False,
67-
embedding_type: Optional[EmbeddingTypes] = None,
67+
embedding_type: EmbeddingTypes | None = None,
6868
) -> tuple[list[list[float]], dict[str, Any]]:
6969
"""Embeds a list of texts using the Cohere API.
7070

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
from collections.abc import AsyncIterator, Iterator
3-
from typing import Any, Literal, Optional, Union, get_args
3+
from typing import Any, Literal, get_args
44

55
from haystack import component, default_from_dict, default_to_dict, logging
66
from haystack.components.generators.utils import _convert_streaming_chunks_to_chat_message
@@ -77,7 +77,7 @@ def _format_tool(tool: Tool) -> dict[str, Any]:
7777

7878
def _format_message(
7979
message: ChatMessage,
80-
) -> Union[UserChatMessageV2, AssistantChatMessageV2, SystemChatMessageV2, ToolChatMessageV2]:
80+
) -> UserChatMessageV2 | AssistantChatMessageV2 | SystemChatMessageV2 | ToolChatMessageV2:
8181
"""
8282
Formats a Haystack ChatMessage into Cohere's chat format.
8383
@@ -147,7 +147,7 @@ def _format_message(
147147
raise ValueError(msg)
148148

149149
# Build multimodal content following Cohere's API specification
150-
content_parts: list[Union[CohereTextContent, ImageUrlContent]] = []
150+
content_parts: list[CohereTextContent | ImageUrlContent] = []
151151
for part in message._content:
152152
if isinstance(part, TextContent) and part.text:
153153
text_content = CohereTextContent(text=part.text)
@@ -234,7 +234,7 @@ def _parse_response(chat_response: ChatResponse, model: str) -> ChatMessage:
234234
def _convert_cohere_chunk_to_streaming_chunk(
235235
chunk: StreamedChatResponseV2,
236236
model: str,
237-
component_info: Optional[ComponentInfo] = None,
237+
component_info: ComponentInfo | None = None,
238238
global_index: int = 0,
239239
) -> StreamingChunk:
240240
"""
@@ -518,10 +518,10 @@ def __init__(
518518
self,
519519
api_key: Secret = Secret.from_env_var(["COHERE_API_KEY", "CO_API_KEY"]),
520520
model: str = "command-a-03-2025",
521-
streaming_callback: Optional[StreamingCallbackT] = None,
522-
api_base_url: Optional[str] = None,
523-
generation_kwargs: Optional[dict[str, Any]] = None,
524-
tools: Optional[ToolsType] = None,
521+
streaming_callback: StreamingCallbackT | None = None,
522+
api_base_url: str | None = None,
523+
generation_kwargs: dict[str, Any] | None = None,
524+
tools: ToolsType | None = None,
525525
**kwargs: Any,
526526
):
527527
"""
@@ -618,9 +618,9 @@ def from_dict(cls, data: dict[str, Any]) -> "CohereChatGenerator":
618618
def run(
619619
self,
620620
messages: list[ChatMessage],
621-
generation_kwargs: Optional[dict[str, Any]] = None,
622-
tools: Optional[ToolsType] = None,
623-
streaming_callback: Optional[StreamingCallbackT] = None,
621+
generation_kwargs: dict[str, Any] | None = None,
622+
tools: ToolsType | None = None,
623+
streaming_callback: StreamingCallbackT | None = None,
624624
) -> dict[str, list[ChatMessage]]:
625625
"""
626626
Invoke the chat endpoint based on the provided messages and generation parameters.
@@ -685,9 +685,9 @@ def run(
685685
async def run_async(
686686
self,
687687
messages: list[ChatMessage],
688-
generation_kwargs: Optional[dict[str, Any]] = None,
689-
tools: Optional[ToolsType] = None,
690-
streaming_callback: Optional[StreamingCallbackT] = None,
688+
generation_kwargs: dict[str, Any] | None = None,
689+
tools: ToolsType | None = None,
690+
streaming_callback: StreamingCallbackT | None = None,
691691
) -> dict[str, list[ChatMessage]]:
692692
"""
693693
Asynchronously invoke the chat endpoint based on the provided messages and generation parameters.

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
22
#
33
# SPDX-License-Identifier: Apache-2.0
4-
from typing import Any, Callable, Optional, Union
4+
from collections.abc import Callable
5+
from typing import Any
56

67
from haystack import component, logging
78
from haystack.dataclasses import ChatMessage
@@ -33,8 +34,8 @@ def __init__(
3334
self,
3435
api_key: Secret = Secret.from_env_var(["COHERE_API_KEY", "CO_API_KEY"]),
3536
model: str = "command-a-03-2025",
36-
streaming_callback: Optional[Callable] = None,
37-
api_base_url: Optional[str] = None,
37+
streaming_callback: Callable | None = None,
38+
api_base_url: str | None = None,
3839
**kwargs: Any,
3940
):
4041
"""
@@ -57,7 +58,7 @@ def __init__(
5758
def run( # type: ignore[override] # due to incompatible signature with ChatGenerator
5859
self,
5960
prompt: str,
60-
) -> dict[str, Union[list[str], list[dict[str, Any]]]]:
61+
) -> dict[str, list[str] | list[dict[str, Any]]]:
6162
"""
6263
Queries the LLM with the prompts to produce replies.
6364
@@ -80,7 +81,7 @@ def run( # type: ignore[override] # due to incompatible signature with ChatGene
8081
async def run_async( # type: ignore[override] # due to incompatible signature with ChatGenerator
8182
self,
8283
prompt: str,
83-
) -> dict[str, Union[list[str], list[dict[str, Any]]]]:
84+
) -> dict[str, list[str] | list[dict[str, Any]]]:
8485
"""
8586
Queries the LLM asynchronously with the prompts to produce replies.
8687
:param prompt: the prompt to be sent to the generative model.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Optional
1+
from typing import Any
22

33
from haystack import Document, component, default_from_dict, default_to_dict, logging
44
from haystack.utils import Secret, deserialize_secrets_inplace
@@ -37,7 +37,7 @@ def __init__(
3737
top_k: int = 10,
3838
api_key: Secret = Secret.from_env_var(["COHERE_API_KEY", "CO_API_KEY"]),
3939
api_base_url: str = "https://api.cohere.com",
40-
meta_fields_to_embed: Optional[list[str]] = None,
40+
meta_fields_to_embed: list[str] | None = None,
4141
meta_data_separator: str = "\n",
4242
max_tokens_per_doc: int = 4096,
4343
):
@@ -120,7 +120,7 @@ def _prepare_cohere_input_docs(self, documents: list[Document]) -> list[str]:
120120
return concatenated_input_list
121121

122122
@component.output_types(documents=list[Document])
123-
def run(self, query: str, documents: list[Document], top_k: Optional[int] = None) -> dict[str, list[Document]]:
123+
def run(self, query: str, documents: list[Document], top_k: int | None = None) -> dict[str, list[Document]]:
124124
"""
125125
Use the Cohere Reranker to re-rank the list of documents based on the query.
126126
@@ -160,7 +160,7 @@ def run(self, query: str, documents: list[Document], top_k: Optional[int] = None
160160
indices = [output.index for output in response.results]
161161
scores = [output.relevance_score for output in response.results]
162162
sorted_docs = []
163-
for idx, score in zip(indices, scores):
163+
for idx, score in zip(indices, scores, strict=True):
164164
doc = documents[idx]
165165
doc.score = score
166166
sorted_docs.append(documents[idx])

0 commit comments

Comments
 (0)