cohere-haystack 9.1.0__tar.gz → 9.3.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-9.1.0 → cohere_haystack-9.3.0}/CHANGELOG.md +18 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/PKG-INFO +3 -2
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/pyproject.toml +1 -1
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/generators/cohere/chat/chat_generator.py +13 -6
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/generators/cohere/generator.py +7 -0
- cohere_haystack-9.3.0/tests/conftest.py +18 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_chat_generator.py +65 -16
- cohere_haystack-9.1.0/tests/conftest.py +0 -8
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/.gitignore +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/LICENSE.txt +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/README.md +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/examples/cohere_embedding.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/examples/cohere_generation.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/examples/cohere_ranker.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/pydoc/config_docusaurus.yml +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/cohere/__init__.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/cohere/document_embedder.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/cohere/document_image_embedder.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/cohere/embedding_types.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/cohere/text_embedder.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/cohere/utils.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/embedders/py.typed +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/generators/cohere/__init__.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/generators/cohere/chat/__init__.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/generators/py.typed +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/rankers/cohere/__init__.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/rankers/cohere/ranker.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/src/haystack_integrations/components/rankers/py.typed +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/__init__.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_chat_generator_async.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_chat_generator_chunks.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_document_embedder.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_document_image_embedder.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_files/apple.jpg +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_files/sample_pdf_1.pdf +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_generator.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_ranker.py +0 -0
- {cohere_haystack-9.1.0 → cohere_haystack-9.3.0}/tests/test_text_embedder.py +0 -0
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/cohere-v9.2.0] - 2026-06-05
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- Cohere - accept str as ChatGenerator input; deprecate generator (#3395)
|
|
8
|
+
|
|
9
|
+
### 🧹 Chores
|
|
10
|
+
|
|
11
|
+
- Cohere - set lower bound pin for Pydantic (#3284)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [integrations/cohere-v9.1.0] - 2026-04-13
|
|
15
|
+
|
|
16
|
+
### 🚀 Features
|
|
17
|
+
|
|
18
|
+
- *(cohere)* Add async support to CohereRanker (#3143)
|
|
19
|
+
|
|
20
|
+
|
|
3
21
|
## [integrations/cohere-v9.0.0] - 2026-04-07
|
|
4
22
|
|
|
5
23
|
### 🐛 Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cohere-haystack
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.3.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
|
|
@@ -19,7 +19,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
19
19
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Requires-Dist: cohere>=5.17.0
|
|
22
|
-
Requires-Dist: haystack-ai>=2.
|
|
22
|
+
Requires-Dist: haystack-ai>=2.30.0
|
|
23
|
+
Requires-Dist: pydantic>=2.12.0
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
|
|
25
26
|
# cohere-haystack
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
24
24
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
25
25
|
]
|
|
26
|
-
dependencies = ["haystack-ai>=2.
|
|
26
|
+
dependencies = ["haystack-ai>=2.30.0", "cohere>=5.17.0", "pydantic>=2.12.0"]
|
|
27
27
|
|
|
28
28
|
[project.urls]
|
|
29
29
|
Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/cohere#readme"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
import json
|
|
2
3
|
from collections.abc import AsyncIterator, Iterator
|
|
3
4
|
from typing import Any, ClassVar, Literal, get_args
|
|
4
5
|
|
|
5
6
|
from haystack import component, default_from_dict, default_to_dict, logging
|
|
6
|
-
from haystack.components.generators.utils import _convert_streaming_chunks_to_chat_message
|
|
7
|
+
from haystack.components.generators.utils import _convert_streaming_chunks_to_chat_message, _normalize_messages
|
|
7
8
|
from haystack.dataclasses import ChatMessage, ComponentInfo, ImageContent, TextContent, ToolCall
|
|
8
9
|
from haystack.dataclasses.streaming_chunk import (
|
|
9
|
-
AsyncStreamingCallbackT,
|
|
10
10
|
FinishReason,
|
|
11
11
|
StreamingCallbackT,
|
|
12
12
|
StreamingChunk,
|
|
@@ -351,7 +351,7 @@ def _parse_streaming_response(
|
|
|
351
351
|
async def _parse_async_streaming_response(
|
|
352
352
|
response: AsyncIterator[StreamedChatResponseV2],
|
|
353
353
|
model: str,
|
|
354
|
-
streaming_callback:
|
|
354
|
+
streaming_callback: StreamingCallbackT,
|
|
355
355
|
component_info: ComponentInfo,
|
|
356
356
|
) -> ChatMessage:
|
|
357
357
|
"""
|
|
@@ -375,7 +375,10 @@ async def _parse_async_streaming_response(
|
|
|
375
375
|
|
|
376
376
|
original_chunks.append(chunk)
|
|
377
377
|
chunks.append(streaming_chunk)
|
|
378
|
-
await
|
|
378
|
+
# sync callbacks are allowed in async contexts with Haystack >= 3.0, so only await async ones
|
|
379
|
+
callback_result = streaming_callback(streaming_chunk)
|
|
380
|
+
if inspect.isawaitable(callback_result):
|
|
381
|
+
await callback_result
|
|
379
382
|
|
|
380
383
|
return _convert_streaming_chunks_to_chat_message(chunks=chunks)
|
|
381
384
|
|
|
@@ -616,7 +619,7 @@ class CohereChatGenerator:
|
|
|
616
619
|
@component.output_types(replies=list[ChatMessage])
|
|
617
620
|
def run(
|
|
618
621
|
self,
|
|
619
|
-
messages: list[ChatMessage],
|
|
622
|
+
messages: list[ChatMessage] | str,
|
|
620
623
|
generation_kwargs: dict[str, Any] | None = None,
|
|
621
624
|
tools: ToolsType | None = None,
|
|
622
625
|
streaming_callback: StreamingCallbackT | None = None,
|
|
@@ -625,6 +628,7 @@ class CohereChatGenerator:
|
|
|
625
628
|
Invoke the chat endpoint based on the provided messages and generation parameters.
|
|
626
629
|
|
|
627
630
|
:param messages: list of `ChatMessage` instances representing the input messages.
|
|
631
|
+
If a string is provided, it is converted to a list containing a ChatMessage with user role.
|
|
628
632
|
:param generation_kwargs: additional keyword arguments for chat generation. These parameters will
|
|
629
633
|
potentially override the parameters passed in the __init__ method.
|
|
630
634
|
For more details on the parameters supported by the Cohere API, refer to the
|
|
@@ -637,6 +641,7 @@ class CohereChatGenerator:
|
|
|
637
641
|
:returns: A dictionary with the following keys:
|
|
638
642
|
- `replies`: a list of `ChatMessage` instances representing the generated responses.
|
|
639
643
|
"""
|
|
644
|
+
messages = _normalize_messages(messages)
|
|
640
645
|
|
|
641
646
|
# update generation kwargs by merging with the generation kwargs passed to the run method
|
|
642
647
|
generation_kwargs = {**self.generation_kwargs, **(generation_kwargs or {})}
|
|
@@ -682,7 +687,7 @@ class CohereChatGenerator:
|
|
|
682
687
|
@component.output_types(replies=list[ChatMessage])
|
|
683
688
|
async def run_async(
|
|
684
689
|
self,
|
|
685
|
-
messages: list[ChatMessage],
|
|
690
|
+
messages: list[ChatMessage] | str,
|
|
686
691
|
generation_kwargs: dict[str, Any] | None = None,
|
|
687
692
|
tools: ToolsType | None = None,
|
|
688
693
|
streaming_callback: StreamingCallbackT | None = None,
|
|
@@ -691,6 +696,7 @@ class CohereChatGenerator:
|
|
|
691
696
|
Asynchronously invoke the chat endpoint based on the provided messages and generation parameters.
|
|
692
697
|
|
|
693
698
|
:param messages: list of `ChatMessage` instances representing the input messages.
|
|
699
|
+
If a string is provided, it is converted to a list containing a ChatMessage with user role.
|
|
694
700
|
:param generation_kwargs: additional keyword arguments for chat generation. These parameters will
|
|
695
701
|
potentially override the parameters passed in the __init__ method.
|
|
696
702
|
For more details on the parameters supported by the Cohere API, refer to the
|
|
@@ -701,6 +707,7 @@ class CohereChatGenerator:
|
|
|
701
707
|
:returns: A dictionary with the following keys:
|
|
702
708
|
- `replies`: a list of `ChatMessage` instances representing the generated responses.
|
|
703
709
|
"""
|
|
710
|
+
messages = _normalize_messages(messages)
|
|
704
711
|
|
|
705
712
|
# update generation kwargs by merging with the generation kwargs passed to the run method
|
|
706
713
|
generation_kwargs = {**self.generation_kwargs, **(generation_kwargs or {})}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import warnings
|
|
4
5
|
from collections.abc import Callable
|
|
5
6
|
from typing import Any, ClassVar
|
|
6
7
|
|
|
@@ -70,6 +71,12 @@ class CohereGenerator(CohereChatGenerator):
|
|
|
70
71
|
You can check them in model's documentation.
|
|
71
72
|
"""
|
|
72
73
|
|
|
74
|
+
warnings.warn(
|
|
75
|
+
"The `CohereGenerator` component is deprecated and will be removed in a future version. "
|
|
76
|
+
"Use `CohereChatGenerator` instead, which now also supports string inputs.",
|
|
77
|
+
FutureWarning,
|
|
78
|
+
stacklevel=2,
|
|
79
|
+
)
|
|
73
80
|
# from_dict deserialization, where `generation_kwargs` is in **kwargs
|
|
74
81
|
if "generation_kwargs" in kwargs:
|
|
75
82
|
generation_kwargs = kwargs.pop("generation_kwargs")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture()
|
|
7
|
+
def test_files_path():
|
|
8
|
+
return Path(__file__).parent / "test_files"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture(autouse=True)
|
|
12
|
+
def allow_deserialization_of_test_modules(monkeypatch):
|
|
13
|
+
"""
|
|
14
|
+
haystack-ai >= 3.0 refuses to deserialize classes and callables from modules outside its
|
|
15
|
+
trusted-module allowlist. Tools and callbacks defined in the test modules live outside that
|
|
16
|
+
allowlist, so trust them explicitly; haystack-ai 2.x ignores this environment variable.
|
|
17
|
+
"""
|
|
18
|
+
monkeypatch.setenv("HAYSTACK_DESERIALIZATION_ALLOWLIST", "tests,test_*")
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from unittest.mock import MagicMock
|
|
2
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
5
|
from cohere.core import ApiError
|
|
6
6
|
from haystack import Pipeline
|
|
7
7
|
from haystack.components.generators.utils import print_streaming_chunk
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from haystack.components.tools import ToolInvoker
|
|
11
|
+
except ImportError: # ToolInvoker was removed in Haystack 3.0
|
|
12
|
+
ToolInvoker = None
|
|
9
13
|
from haystack.dataclasses import ChatMessage, ChatRole, ImageContent, ToolCall
|
|
10
14
|
from haystack.dataclasses.streaming_chunk import StreamingChunk
|
|
11
15
|
from haystack.tools import Tool, Toolset
|
|
@@ -337,6 +341,11 @@ class TestCohereChatGenerator:
|
|
|
337
341
|
|
|
338
342
|
pipeline_dict = pipeline.to_dict()
|
|
339
343
|
|
|
344
|
+
# the Tool serialization format is owned by haystack-ai and varies across its versions; the
|
|
345
|
+
# dumps/loads round-trip below covers the tools, so exclude them from the pinned-dict comparison
|
|
346
|
+
tools_entries = pipeline_dict["components"]["generator"]["init_parameters"].pop("tools")
|
|
347
|
+
assert len(tools_entries) == 1
|
|
348
|
+
|
|
340
349
|
expected_dict = {
|
|
341
350
|
"metadata": {},
|
|
342
351
|
"max_runs_per_component": 100,
|
|
@@ -352,20 +361,6 @@ class TestCohereChatGenerator:
|
|
|
352
361
|
"generation_kwargs": {"temperature": 0.7},
|
|
353
362
|
"timeout": None,
|
|
354
363
|
"max_retries": None,
|
|
355
|
-
"tools": [
|
|
356
|
-
{
|
|
357
|
-
"type": "haystack.tools.tool.Tool",
|
|
358
|
-
"data": {
|
|
359
|
-
"name": "weather",
|
|
360
|
-
"description": "useful to determine the weather in a given location",
|
|
361
|
-
"parameters": {"city": {"type": "string"}},
|
|
362
|
-
"function": "tests.test_chat_generator.weather",
|
|
363
|
-
"outputs_to_string": tool.outputs_to_string,
|
|
364
|
-
"inputs_from_state": tool.inputs_from_state,
|
|
365
|
-
"outputs_to_state": tool.outputs_to_state,
|
|
366
|
-
},
|
|
367
|
-
}
|
|
368
|
-
],
|
|
369
364
|
},
|
|
370
365
|
}
|
|
371
366
|
},
|
|
@@ -498,6 +493,59 @@ class TestCohereChatGenerator:
|
|
|
498
493
|
assert multimodal_msg["content"][0]["type"] == "text"
|
|
499
494
|
assert multimodal_msg["content"][1]["type"] == "image_url"
|
|
500
495
|
|
|
496
|
+
def test_run_with_string_input(self):
|
|
497
|
+
"""Test that a string input is converted to a user ChatMessage before sending to the API."""
|
|
498
|
+
generator = CohereChatGenerator(api_key=Secret.from_token("test-api-key"))
|
|
499
|
+
|
|
500
|
+
mock_response = MagicMock()
|
|
501
|
+
mock_response.message.content = [MagicMock()]
|
|
502
|
+
mock_response.message.content[0].text = "Paris"
|
|
503
|
+
mock_response.message.content[0].type = "text"
|
|
504
|
+
mock_response.message.tool_calls = None
|
|
505
|
+
mock_response.finish_reason = "COMPLETE"
|
|
506
|
+
mock_response.usage = None
|
|
507
|
+
|
|
508
|
+
generator.client.chat = MagicMock(return_value=mock_response)
|
|
509
|
+
|
|
510
|
+
result = generator.run("What's the capital of France?")
|
|
511
|
+
|
|
512
|
+
generator.client.chat.assert_called_once()
|
|
513
|
+
call_args = generator.client.chat.call_args
|
|
514
|
+
sent_messages = call_args[1]["messages"]
|
|
515
|
+
|
|
516
|
+
assert len(sent_messages) == 1
|
|
517
|
+
assert sent_messages[0]["role"] == "user"
|
|
518
|
+
assert isinstance(result["replies"], list)
|
|
519
|
+
assert len(result["replies"]) == 1
|
|
520
|
+
assert isinstance(result["replies"][0], ChatMessage)
|
|
521
|
+
|
|
522
|
+
@pytest.mark.asyncio
|
|
523
|
+
async def test_run_async_with_string_input(self):
|
|
524
|
+
"""Test that a string input is converted to a user ChatMessage before sending to the async API."""
|
|
525
|
+
generator = CohereChatGenerator(api_key=Secret.from_token("test-api-key"))
|
|
526
|
+
|
|
527
|
+
mock_response = MagicMock()
|
|
528
|
+
mock_response.message.content = [MagicMock()]
|
|
529
|
+
mock_response.message.content[0].text = "Paris"
|
|
530
|
+
mock_response.message.content[0].type = "text"
|
|
531
|
+
mock_response.message.tool_calls = None
|
|
532
|
+
mock_response.finish_reason = "COMPLETE"
|
|
533
|
+
mock_response.usage = None
|
|
534
|
+
|
|
535
|
+
generator.async_client.chat = AsyncMock(return_value=mock_response)
|
|
536
|
+
|
|
537
|
+
result = await generator.run_async("What's the capital of France?")
|
|
538
|
+
|
|
539
|
+
generator.async_client.chat.assert_called_once()
|
|
540
|
+
call_args = generator.async_client.chat.call_args
|
|
541
|
+
sent_messages = call_args[1]["messages"]
|
|
542
|
+
|
|
543
|
+
assert len(sent_messages) == 1
|
|
544
|
+
assert sent_messages[0]["role"] == "user"
|
|
545
|
+
assert isinstance(result["replies"], list)
|
|
546
|
+
assert len(result["replies"]) == 1
|
|
547
|
+
assert isinstance(result["replies"][0], ChatMessage)
|
|
548
|
+
|
|
501
549
|
|
|
502
550
|
@pytest.mark.skipif(
|
|
503
551
|
not os.environ.get("COHERE_API_KEY", None) and not os.environ.get("CO_API_KEY", None),
|
|
@@ -689,6 +737,7 @@ class TestCohereChatGeneratorInference:
|
|
|
689
737
|
assert len(final_message.text) > 0
|
|
690
738
|
assert "paris" in final_message.text.lower()
|
|
691
739
|
|
|
740
|
+
@pytest.mark.skipif(ToolInvoker is None, reason="ToolInvoker is not available in the installed haystack-ai version")
|
|
692
741
|
def test_pipeline_with_cohere_chat_generator(self):
|
|
693
742
|
"""
|
|
694
743
|
Test that the CohereChatGenerator component can be used in a pipeline
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|