cohere-haystack 10.0.1__tar.gz → 11.0.0__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.
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/CHANGELOG.md +14 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/PKG-INFO +4 -3
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/pyproject.toml +11 -4
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/cohere/document_embedder.py +6 -4
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/cohere/document_image_embedder.py +6 -3
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/cohere/text_embedder.py +6 -4
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/generators/cohere/chat/chat_generator.py +11 -18
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/rankers/cohere/ranker.py +7 -4
- cohere_haystack-11.0.0/src/haystack_integrations/utils/cohere/__init__.py +6 -0
- cohere_haystack-11.0.0/src/haystack_integrations/utils/cohere/api_base_url.py +29 -0
- cohere_haystack-11.0.0/src/haystack_integrations/utils/py.typed +0 -0
- cohere_haystack-11.0.0/tests/test_api_base_url.py +75 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_chat_generator.py +7 -21
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/.gitignore +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/LICENSE.txt +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/README.md +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/examples/cohere_embedding.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/examples/cohere_generation.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/examples/cohere_ranker.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/pydoc/config_docusaurus.yml +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/cohere/__init__.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/cohere/embedding_types.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/cohere/utils.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/embedders/py.typed +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/generators/cohere/__init__.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/generators/cohere/chat/__init__.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/generators/py.typed +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/rankers/cohere/__init__.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/src/haystack_integrations/components/rankers/py.typed +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/__init__.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/conftest.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_chat_generator_async.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_chat_generator_chunks.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_document_embedder.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_document_image_embedder.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_files/apple.jpg +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_files/sample_pdf_1.pdf +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_ranker.py +0 -0
- {cohere_haystack-10.0.1 → cohere_haystack-11.0.0}/tests/test_text_embedder.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/cohere-v10.1.0] - 2026-09-28
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- *(cohere)* Add utility for API base URL validation (#4015)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [integrations/cohere-v10.0.1] - 2026-09-10
|
|
11
|
+
|
|
12
|
+
### 🐛 Bug Fixes
|
|
13
|
+
|
|
14
|
+
- Batch CohereDocumentEmbedder.run_async like the sync path (#3926)
|
|
15
|
+
|
|
16
|
+
|
|
3
17
|
## [integrations/cohere-v10.0.0] - 2026-09-04
|
|
4
18
|
|
|
5
19
|
### 🚜 Refactor
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cohere-haystack
|
|
3
|
-
Version:
|
|
3
|
+
Version: 11.0.0
|
|
4
4
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/cohere#readme
|
|
5
5
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
6
6
|
Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/cohere
|
|
@@ -10,17 +10,18 @@ License-File: LICENSE.txt
|
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
19
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
21
|
Requires-Dist: cohere>=5.17.0
|
|
22
22
|
Requires-Dist: haystack-ai>=3.0.0
|
|
23
23
|
Requires-Dist: pydantic>=2.12.0
|
|
24
|
+
Requires-Dist: pydantic>=2.14.0; python_version >= '3.15'
|
|
24
25
|
Description-Content-Type: text/markdown
|
|
25
26
|
|
|
26
27
|
# cohere-haystack
|
|
@@ -7,7 +7,7 @@ name = "cohere-haystack"
|
|
|
7
7
|
dynamic = ["version"]
|
|
8
8
|
description = ''
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
keywords = []
|
|
13
13
|
authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }]
|
|
@@ -15,15 +15,21 @@ classifiers = [
|
|
|
15
15
|
"License :: OSI Approved :: Apache Software License",
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
17
17
|
"Programming Language :: Python",
|
|
18
|
-
"Programming Language :: Python :: 3.10",
|
|
19
18
|
"Programming Language :: Python :: 3.11",
|
|
20
19
|
"Programming Language :: Python :: 3.12",
|
|
21
20
|
"Programming Language :: Python :: 3.13",
|
|
22
21
|
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Programming Language :: Python :: 3.15",
|
|
23
23
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
24
24
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
25
25
|
]
|
|
26
|
-
dependencies = [
|
|
26
|
+
dependencies = [
|
|
27
|
+
"haystack-ai>=3.0.0",
|
|
28
|
+
"cohere>=5.17.0",
|
|
29
|
+
"pydantic>=2.12.0",
|
|
30
|
+
# pydantic 2.14.0 is the first release whose pydantic-core has Python 3.15 wheels.
|
|
31
|
+
"pydantic>=2.14.0; python_version >= '3.15'",
|
|
32
|
+
]
|
|
27
33
|
|
|
28
34
|
[project.urls]
|
|
29
35
|
Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/cohere#readme"
|
|
@@ -72,7 +78,8 @@ integration-cov-append-retry = 'pytest --cov=haystack_integrations --cov-append
|
|
|
72
78
|
|
|
73
79
|
types = """mypy -p haystack_integrations.components.embedders.cohere \
|
|
74
80
|
-p haystack_integrations.components.generators.cohere \
|
|
75
|
-
-p haystack_integrations.components.rankers.cohere
|
|
81
|
+
-p haystack_integrations.components.rankers.cohere \
|
|
82
|
+
-p haystack_integrations.utils.cohere {args}"""
|
|
76
83
|
|
|
77
84
|
[tool.mypy]
|
|
78
85
|
install_types = true
|
|
@@ -5,9 +5,10 @@ from dataclasses import replace
|
|
|
5
5
|
from typing import Any, ClassVar
|
|
6
6
|
|
|
7
7
|
from haystack import Document, component, default_from_dict, default_to_dict
|
|
8
|
-
from haystack.utils import Secret
|
|
8
|
+
from haystack.utils import Secret
|
|
9
9
|
|
|
10
10
|
from cohere import AsyncClientV2, ClientV2
|
|
11
|
+
from haystack_integrations.utils.cohere import validate_api_base_url
|
|
11
12
|
|
|
12
13
|
from .embedding_types import EmbeddingTypes
|
|
13
14
|
from .utils import get_async_response, get_response
|
|
@@ -68,7 +69,7 @@ class CohereDocumentEmbedder:
|
|
|
68
69
|
Read [Cohere documentation](https://docs.cohere.com/docs/models#embed) for a list of all supported models.
|
|
69
70
|
:param input_type: specifies the type of input you're giving to the model. Supported values are
|
|
70
71
|
"search_document", "search_query", "classification" and "clustering".
|
|
71
|
-
:param api_base_url: the Cohere API Base url.
|
|
72
|
+
:param api_base_url: the Cohere API Base url. The Cohere client appends the endpoint path to it.
|
|
72
73
|
:param truncate: truncate embeddings that are too long from start or end, ("NONE"|"START"|"END").
|
|
73
74
|
Passing "START" will discard the start of the input. "END" will discard the end of the input. In both
|
|
74
75
|
cases, input is discarded until the remaining input is exactly the maximum input token length for the model.
|
|
@@ -81,7 +82,10 @@ class CohereDocumentEmbedder:
|
|
|
81
82
|
:param embedding_separator: separator used to concatenate the meta fields to the Document text.
|
|
82
83
|
:param embedding_type: the type of embeddings to return. Defaults to float embeddings.
|
|
83
84
|
Note that int8, uint8, binary, and ubinary are only valid for v3 models.
|
|
85
|
+
|
|
86
|
+
:raises ValueError: If `api_base_url` is a full endpoint URL rather than a base URL.
|
|
84
87
|
"""
|
|
88
|
+
validate_api_base_url(api_base_url)
|
|
85
89
|
|
|
86
90
|
self.api_key = api_key
|
|
87
91
|
self.model = model
|
|
@@ -155,8 +159,6 @@ class CohereDocumentEmbedder:
|
|
|
155
159
|
# drop legacy use_async_client parameter
|
|
156
160
|
init_params.pop("use_async_client", None)
|
|
157
161
|
|
|
158
|
-
deserialize_secrets_inplace(init_params, ["api_key"])
|
|
159
|
-
|
|
160
162
|
# Convert embedding_type string to EmbeddingTypes enum value
|
|
161
163
|
init_params["embedding_type"] = EmbeddingTypes.from_str(init_params["embedding_type"])
|
|
162
164
|
|
|
@@ -13,10 +13,11 @@ from haystack.components.converters.image.image_utils import (
|
|
|
13
13
|
_PDFPageInfo,
|
|
14
14
|
)
|
|
15
15
|
from haystack.dataclasses import ByteStream
|
|
16
|
-
from haystack.utils.auth import Secret
|
|
16
|
+
from haystack.utils.auth import Secret
|
|
17
17
|
from tqdm import tqdm
|
|
18
18
|
|
|
19
19
|
from cohere import AsyncClientV2, ClientV2
|
|
20
|
+
from haystack_integrations.utils.cohere import validate_api_base_url
|
|
20
21
|
|
|
21
22
|
from .embedding_types import EmbeddingTypes
|
|
22
23
|
|
|
@@ -101,7 +102,7 @@ class CohereDocumentImageEmbedder:
|
|
|
101
102
|
The Cohere model to use for calculating embeddings.
|
|
102
103
|
Read [Cohere documentation](https://docs.cohere.com/docs/models#embed) for a list of all supported models.
|
|
103
104
|
:param api_base_url:
|
|
104
|
-
The Cohere API base URL.
|
|
105
|
+
The Cohere API base URL. The Cohere client appends the endpoint path to it.
|
|
105
106
|
:param timeout:
|
|
106
107
|
Request timeout in seconds.
|
|
107
108
|
:param embedding_dimension:
|
|
@@ -114,7 +115,10 @@ class CohereDocumentImageEmbedder:
|
|
|
114
115
|
:param progress_bar:
|
|
115
116
|
Whether to show a progress bar or not. Can be helpful to disable in production deployments
|
|
116
117
|
to keep the logs clean.
|
|
118
|
+
|
|
119
|
+
:raises ValueError: If `api_base_url` is a full endpoint URL rather than a base URL.
|
|
117
120
|
"""
|
|
121
|
+
validate_api_base_url(api_base_url)
|
|
118
122
|
|
|
119
123
|
self.file_path_meta_field = file_path_meta_field
|
|
120
124
|
self.root_path = root_path or ""
|
|
@@ -184,7 +188,6 @@ class CohereDocumentImageEmbedder:
|
|
|
184
188
|
Deserialized component.
|
|
185
189
|
"""
|
|
186
190
|
init_params = data["init_parameters"]
|
|
187
|
-
deserialize_secrets_inplace(init_params, keys=["api_key"])
|
|
188
191
|
init_params["embedding_type"] = EmbeddingTypes.from_str(init_params["embedding_type"])
|
|
189
192
|
|
|
190
193
|
return default_from_dict(cls, data)
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
from typing import Any, ClassVar
|
|
5
5
|
|
|
6
6
|
from haystack import component, default_from_dict, default_to_dict
|
|
7
|
-
from haystack.utils import Secret
|
|
7
|
+
from haystack.utils import Secret
|
|
8
8
|
|
|
9
9
|
from cohere import AsyncClientV2, ClientV2
|
|
10
|
+
from haystack_integrations.utils.cohere import validate_api_base_url
|
|
10
11
|
|
|
11
12
|
from .embedding_types import EmbeddingTypes
|
|
12
13
|
from .utils import get_async_response, get_response
|
|
@@ -60,7 +61,7 @@ class CohereTextEmbedder:
|
|
|
60
61
|
Read [Cohere documentation](https://docs.cohere.com/docs/models#embed) for a list of all supported models.
|
|
61
62
|
:param input_type: specifies the type of input you're giving to the model. Supported values are
|
|
62
63
|
"search_document", "search_query", "classification" and "clustering".
|
|
63
|
-
:param api_base_url: the Cohere API Base url.
|
|
64
|
+
:param api_base_url: the Cohere API Base url. The Cohere client appends the endpoint path to it.
|
|
64
65
|
:param truncate: truncate embeddings that are too long from start or end, ("NONE"|"START"|"END").
|
|
65
66
|
Passing "START" will discard the start of the input. "END" will discard the end of the input. In both
|
|
66
67
|
cases, input is discarded until the remaining input is exactly the maximum input token length for the model.
|
|
@@ -68,7 +69,10 @@ class CohereTextEmbedder:
|
|
|
68
69
|
:param timeout: request timeout in seconds.
|
|
69
70
|
:param embedding_type: the type of embeddings to return. Defaults to float embeddings.
|
|
70
71
|
Note that int8, uint8, binary, and ubinary are only valid for v3 models.
|
|
72
|
+
|
|
73
|
+
:raises ValueError: If `api_base_url` is a full endpoint URL rather than a base URL.
|
|
71
74
|
"""
|
|
75
|
+
validate_api_base_url(api_base_url)
|
|
72
76
|
|
|
73
77
|
self.api_key = api_key
|
|
74
78
|
self.model = model
|
|
@@ -142,8 +146,6 @@ class CohereTextEmbedder:
|
|
|
142
146
|
# drop legacy use_async_client parameter
|
|
143
147
|
init_params.pop("use_async_client", None)
|
|
144
148
|
|
|
145
|
-
deserialize_secrets_inplace(init_params, ["api_key"])
|
|
146
|
-
|
|
147
149
|
# Convert embedding_type string to EmbeddingTypes enum value
|
|
148
150
|
init_params["embedding_type"] = EmbeddingTypes.from_str(init_params["embedding_type"])
|
|
149
151
|
|
|
@@ -29,6 +29,7 @@ from httpx import AsyncHTTPTransport, HTTPTransport
|
|
|
29
29
|
from httpx import Client as HTTPXClient
|
|
30
30
|
|
|
31
31
|
from cohere import AsyncClientV2, ChatResponse, ClientV2, StreamedChatResponseV2
|
|
32
|
+
from haystack_integrations.utils.cohere import validate_api_base_url
|
|
32
33
|
|
|
33
34
|
logger = logging.getLogger(__name__)
|
|
34
35
|
|
|
@@ -459,13 +460,11 @@ class CohereChatGenerator:
|
|
|
459
460
|
|
|
460
461
|
### Advanced example
|
|
461
462
|
|
|
462
|
-
CohereChatGenerator
|
|
463
|
-
architecture, enabling tools to be invoked seamlessly across various generators.
|
|
463
|
+
CohereChatGenerator supports Haystack's tooling architecture, so it can drive tool calls in an `Agent`.
|
|
464
464
|
|
|
465
465
|
```python
|
|
466
|
-
from haystack import
|
|
466
|
+
from haystack.components.agents import Agent
|
|
467
467
|
from haystack.dataclasses import ChatMessage
|
|
468
|
-
from haystack.components.tools import ToolInvoker
|
|
469
468
|
from haystack.tools import Tool
|
|
470
469
|
from haystack_integrations.components.generators.cohere import CohereChatGenerator
|
|
471
470
|
|
|
@@ -489,20 +488,12 @@ class CohereChatGenerator:
|
|
|
489
488
|
function=weather,
|
|
490
489
|
)
|
|
491
490
|
|
|
492
|
-
#
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
pipeline.add_component("tool_invoker", ToolInvoker(tools=[weather_tool]))
|
|
496
|
-
pipeline.connect("generator", "tool_invoker")
|
|
491
|
+
# The Agent calls the tool and feeds the result back to the model
|
|
492
|
+
agent = Agent(chat_generator=CohereChatGenerator(), tools=[weather_tool])
|
|
493
|
+
result = agent.run(messages=[ChatMessage.from_user("What's the weather like in Paris?")])
|
|
497
494
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
data={"generator": {"messages": [ChatMessage.from_user("What's the weather like in Paris?")]}}
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
# The tool result will be available in the pipeline output
|
|
504
|
-
print(results["tool_invoker"]["tool_messages"][0].tool_call_result.result)
|
|
505
|
-
# Output: "The weather in Paris is sunny and 32°C"
|
|
495
|
+
print(result["last_message"].text)
|
|
496
|
+
# Output: "The weather in Paris is sunny and 32°C."
|
|
506
497
|
```
|
|
507
498
|
"""
|
|
508
499
|
|
|
@@ -544,7 +535,7 @@ class CohereChatGenerator:
|
|
|
544
535
|
:param streaming_callback: A callback function that is called when a new token is received from the stream.
|
|
545
536
|
The callback function accepts [StreamingChunk](https://docs.haystack.deepset.ai/docs/data-classes#streamingchunk)
|
|
546
537
|
as an argument.
|
|
547
|
-
:param api_base_url: The base URL of the Cohere API.
|
|
538
|
+
:param api_base_url: The base URL of the Cohere API. The Cohere client appends the endpoint path to it.
|
|
548
539
|
:param generation_kwargs: Other parameters to use for the model during generation. For a list of parameters,
|
|
549
540
|
see [Cohere Chat endpoint](https://docs.cohere.com/reference/chat).
|
|
550
541
|
Some of the parameters are:
|
|
@@ -564,11 +555,13 @@ class CohereChatGenerator:
|
|
|
564
555
|
Maximum number of retries to attempt for failed requests. If not set, it defaults to the default set by
|
|
565
556
|
the Cohere client.
|
|
566
557
|
|
|
558
|
+
:raises ValueError: If `api_base_url` is a full endpoint URL rather than a base URL.
|
|
567
559
|
"""
|
|
568
560
|
_check_duplicate_tool_names(flatten_tools_or_toolsets(tools))
|
|
569
561
|
|
|
570
562
|
if not api_base_url:
|
|
571
563
|
api_base_url = "https://api.cohere.com"
|
|
564
|
+
validate_api_base_url(api_base_url)
|
|
572
565
|
|
|
573
566
|
self.api_key = api_key
|
|
574
567
|
self.model = model
|
|
@@ -2,9 +2,10 @@ from dataclasses import replace
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
from haystack import Document, component, default_from_dict, default_to_dict, logging
|
|
5
|
-
from haystack.utils import Secret
|
|
5
|
+
from haystack.utils import Secret
|
|
6
6
|
|
|
7
7
|
from cohere import AsyncClientV2, ClientV2
|
|
8
|
+
from haystack_integrations.utils.cohere import validate_api_base_url
|
|
8
9
|
|
|
9
10
|
logger = logging.getLogger(__name__)
|
|
10
11
|
|
|
@@ -48,19 +49,22 @@ class CohereRanker:
|
|
|
48
49
|
:param model: Cohere model name. Check the list of supported models in the [Cohere documentation](https://docs.cohere.com/docs/models).
|
|
49
50
|
:param top_k: The maximum number of documents to return.
|
|
50
51
|
:param api_key: Cohere API key.
|
|
51
|
-
:param api_base_url: the base URL of the Cohere API.
|
|
52
|
+
:param api_base_url: the base URL of the Cohere API. The Cohere client appends the endpoint path to it.
|
|
52
53
|
:param meta_fields_to_embed: List of meta fields that should be concatenated
|
|
53
54
|
with the document content for reranking.
|
|
54
55
|
:param meta_data_separator: Separator used to concatenate the meta fields
|
|
55
56
|
to the Document content.
|
|
56
57
|
:param max_tokens_per_doc: The maximum number of tokens to embed for each document defaults to 4096.
|
|
57
58
|
|
|
58
|
-
:raises ValueError: If `top_k` is not > 0
|
|
59
|
+
:raises ValueError: If `top_k` is not > 0, or if `api_base_url` is a full endpoint URL rather than a
|
|
60
|
+
base URL.
|
|
59
61
|
"""
|
|
60
62
|
if top_k <= 0:
|
|
61
63
|
msg = f"top_k must be > 0, but got {top_k}"
|
|
62
64
|
raise ValueError(msg)
|
|
63
65
|
|
|
66
|
+
validate_api_base_url(api_base_url)
|
|
67
|
+
|
|
64
68
|
self.model_name = model
|
|
65
69
|
self.api_key = api_key
|
|
66
70
|
self.api_base_url = api_base_url
|
|
@@ -119,7 +123,6 @@ class CohereRanker:
|
|
|
119
123
|
# was serialized with the old version
|
|
120
124
|
data["init_parameters"].pop("max_chunks_per_doc", None)
|
|
121
125
|
|
|
122
|
-
deserialize_secrets_inplace(data["init_parameters"], keys=["api_key"])
|
|
123
126
|
return default_from_dict(cls, data)
|
|
124
127
|
|
|
125
128
|
def _prepare_cohere_input_docs(self, documents: list[Document], top_k: int | None = None) -> tuple[list[str], int]:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
# Cohere clients build a request URL by appending the endpoint path to the configured base URL, so a base URL that
|
|
7
|
+
# already ends with one of these paths produces a doubled path and a generic API error at request time.
|
|
8
|
+
_COHERE_ENDPOINT_PATH = re.compile(r"/v\d+/(?:rerank|embed|chat)$")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def validate_api_base_url(api_base_url: str) -> None:
|
|
12
|
+
"""
|
|
13
|
+
Check that `api_base_url` is a base URL rather than a full endpoint URL.
|
|
14
|
+
|
|
15
|
+
:param api_base_url: The base URL to validate.
|
|
16
|
+
:raises ValueError: If `api_base_url` ends with a Cohere endpoint path, which the Cohere client appends itself.
|
|
17
|
+
"""
|
|
18
|
+
trimmed = api_base_url.rstrip("/")
|
|
19
|
+
match = _COHERE_ENDPOINT_PATH.search(trimmed)
|
|
20
|
+
if match is None:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
base = trimmed[: match.start()]
|
|
24
|
+
hint = f" and pass '{base}' instead" if base else ""
|
|
25
|
+
msg = (
|
|
26
|
+
f"'api_base_url' must be a base URL, not a full endpoint URL, but got {api_base_url!r}. "
|
|
27
|
+
f"The Cohere client appends the endpoint path itself, so remove the trailing '{match.group()}'{hint}."
|
|
28
|
+
)
|
|
29
|
+
raise ValueError(msg)
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from haystack_integrations.components.embedders.cohere import (
|
|
7
|
+
CohereDocumentEmbedder,
|
|
8
|
+
CohereDocumentImageEmbedder,
|
|
9
|
+
CohereTextEmbedder,
|
|
10
|
+
)
|
|
11
|
+
from haystack_integrations.components.generators.cohere import CohereChatGenerator
|
|
12
|
+
from haystack_integrations.components.rankers.cohere import CohereRanker
|
|
13
|
+
from haystack_integrations.utils.cohere import validate_api_base_url
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestValidateApiBaseUrl:
|
|
17
|
+
@pytest.mark.parametrize(
|
|
18
|
+
"api_base_url",
|
|
19
|
+
[
|
|
20
|
+
"https://api.cohere.com",
|
|
21
|
+
"https://api.cohere.com/",
|
|
22
|
+
"https://resource.services.ai.azure.com/models",
|
|
23
|
+
# only a trailing endpoint path is rejected, not one in the middle of the base path
|
|
24
|
+
"https://gateway.example.com/v2/rerank/proxy",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
def test_accepts_base_urls(self, api_base_url):
|
|
28
|
+
validate_api_base_url(api_base_url)
|
|
29
|
+
|
|
30
|
+
@pytest.mark.parametrize(
|
|
31
|
+
"api_base_url",
|
|
32
|
+
[
|
|
33
|
+
"https://api.cohere.com/v1/rerank",
|
|
34
|
+
"https://api.cohere.com/v2/embed",
|
|
35
|
+
"https://api.cohere.com/v2/chat",
|
|
36
|
+
# a future API version is rejected without needing a new entry in the pattern
|
|
37
|
+
"https://api.cohere.com/v3/rerank",
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
def test_rejects_endpoint_urls(self, api_base_url):
|
|
41
|
+
with pytest.raises(ValueError, match="must be a base URL"):
|
|
42
|
+
validate_api_base_url(api_base_url)
|
|
43
|
+
|
|
44
|
+
@pytest.mark.parametrize("suffix", ["", "/", "//"])
|
|
45
|
+
def test_error_names_the_endpoint_path_and_the_corrected_url(self, suffix):
|
|
46
|
+
url = f"https://resource.services.ai.azure.com/models/v2/rerank{suffix}"
|
|
47
|
+
with pytest.raises(ValueError) as exc_info:
|
|
48
|
+
validate_api_base_url(url)
|
|
49
|
+
|
|
50
|
+
message = str(exc_info.value)
|
|
51
|
+
assert "remove the trailing '/v2/rerank'" in message
|
|
52
|
+
assert "pass 'https://resource.services.ai.azure.com/models' instead" in message
|
|
53
|
+
|
|
54
|
+
def test_omits_the_hint_when_nothing_would_remain(self):
|
|
55
|
+
with pytest.raises(ValueError) as exc_info:
|
|
56
|
+
validate_api_base_url("/v2/rerank")
|
|
57
|
+
|
|
58
|
+
assert "instead" not in str(exc_info.value)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.mark.parametrize(
|
|
62
|
+
"component_cls",
|
|
63
|
+
[
|
|
64
|
+
CohereRanker,
|
|
65
|
+
CohereTextEmbedder,
|
|
66
|
+
CohereDocumentEmbedder,
|
|
67
|
+
CohereDocumentImageEmbedder,
|
|
68
|
+
CohereChatGenerator,
|
|
69
|
+
],
|
|
70
|
+
)
|
|
71
|
+
def test_components_reject_full_endpoint_urls(component_cls, monkeypatch):
|
|
72
|
+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
|
|
73
|
+
|
|
74
|
+
with pytest.raises(ValueError, match="must be a base URL"):
|
|
75
|
+
component_cls(api_base_url="https://resource.services.ai.azure.com/models/v2/rerank")
|
|
@@ -4,12 +4,8 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
4
4
|
import pytest
|
|
5
5
|
from cohere.core import ApiError
|
|
6
6
|
from haystack import Pipeline
|
|
7
|
+
from haystack.components.agents import Agent
|
|
7
8
|
from haystack.components.generators.utils import print_streaming_chunk
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
from haystack.components.tools import ToolInvoker
|
|
11
|
-
except ImportError: # ToolInvoker was removed in Haystack 3.0
|
|
12
|
-
ToolInvoker = None
|
|
13
9
|
from haystack.dataclasses import ChatMessage, ChatRole, ImageContent, ToolCall
|
|
14
10
|
from haystack.dataclasses.streaming_chunk import StreamingChunk
|
|
15
11
|
from haystack.tools import Tool, Toolset
|
|
@@ -810,10 +806,9 @@ class TestCohereChatGeneratorInference:
|
|
|
810
806
|
assert len(final_message.text) > 0
|
|
811
807
|
assert "paris" in final_message.text.lower()
|
|
812
808
|
|
|
813
|
-
|
|
814
|
-
def test_pipeline_with_cohere_chat_generator(self):
|
|
809
|
+
def test_agent_with_cohere_chat_generator(self):
|
|
815
810
|
"""
|
|
816
|
-
Test that the CohereChatGenerator component can
|
|
811
|
+
Test that the CohereChatGenerator component can drive tool calls in an Agent
|
|
817
812
|
"""
|
|
818
813
|
weather_tool = Tool(
|
|
819
814
|
name="weather",
|
|
@@ -831,20 +826,11 @@ class TestCohereChatGeneratorInference:
|
|
|
831
826
|
function=weather,
|
|
832
827
|
)
|
|
833
828
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
pipeline.add_component("tool_invoker", ToolInvoker(tools=[weather_tool]))
|
|
837
|
-
|
|
838
|
-
pipeline.connect("generator", "tool_invoker")
|
|
829
|
+
agent = Agent(chat_generator=CohereChatGenerator(model="command-r7b-12-2024"), tools=[weather_tool])
|
|
830
|
+
result = agent.run(messages=[ChatMessage.from_user("What's the weather like in Paris?")])
|
|
839
831
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
)
|
|
843
|
-
|
|
844
|
-
assert (
|
|
845
|
-
"The weather in Paris is sunny and 32°C"
|
|
846
|
-
== results["tool_invoker"]["tool_messages"][0].tool_call_result.result
|
|
847
|
-
)
|
|
832
|
+
tool_results = [msg.tool_call_result.result for msg in result["messages"] if msg.tool_call_result]
|
|
833
|
+
assert "The weather in Paris is sunny and 32°C" in tool_results
|
|
848
834
|
|
|
849
835
|
def test_live_run_multimodal(self):
|
|
850
836
|
generator = CohereChatGenerator(
|
|
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
|
|
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
|