cohere-haystack 9.2.0__tar.gz → 9.4.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.2.0 → cohere_haystack-9.4.0}/CHANGELOG.md +23 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/PKG-INFO +1 -1
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/generators/cohere/chat/chat_generator.py +31 -5
- cohere_haystack-9.4.0/tests/conftest.py +18 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_chat_generator.py +11 -15
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_chat_generator_chunks.py +62 -0
- cohere_haystack-9.2.0/tests/conftest.py +0 -8
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/.gitignore +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/LICENSE.txt +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/README.md +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/examples/cohere_embedding.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/examples/cohere_generation.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/examples/cohere_ranker.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/pydoc/config_docusaurus.yml +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/pyproject.toml +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/cohere/__init__.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/cohere/document_embedder.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/cohere/document_image_embedder.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/cohere/embedding_types.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/cohere/text_embedder.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/cohere/utils.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/embedders/py.typed +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/generators/cohere/__init__.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/generators/cohere/chat/__init__.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/generators/cohere/generator.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/generators/py.typed +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/rankers/cohere/__init__.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/rankers/cohere/ranker.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/src/haystack_integrations/components/rankers/py.typed +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/__init__.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_chat_generator_async.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_document_embedder.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_document_image_embedder.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_files/apple.jpg +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_files/sample_pdf_1.pdf +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_generator.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_ranker.py +0 -0
- {cohere_haystack-9.2.0 → cohere_haystack-9.4.0}/tests/test_text_embedder.py +0 -0
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/cohere-v9.3.0] - 2026-07-09
|
|
4
|
+
|
|
5
|
+
### 🧪 Testing
|
|
6
|
+
|
|
7
|
+
- Guard ToolInvoker imports so chat-generator tests run under Haystack 3.0 (#3535)
|
|
8
|
+
- Fix cohere, amazon_bedrock, mirage, and ollama tests under Haystack 3.0 (#3566)
|
|
9
|
+
|
|
10
|
+
### 🧹 Chores
|
|
11
|
+
|
|
12
|
+
- Support sync streaming callbacks in async contexts for Haystack 2.x/3.x compatibility (#3534)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## [integrations/cohere-v9.2.0] - 2026-06-05
|
|
16
|
+
|
|
17
|
+
### 🚀 Features
|
|
18
|
+
|
|
19
|
+
- Cohere - accept str as ChatGenerator input; deprecate generator (#3395)
|
|
20
|
+
|
|
21
|
+
### 🧹 Chores
|
|
22
|
+
|
|
23
|
+
- Cohere - set lower bound pin for Pydantic (#3284)
|
|
24
|
+
|
|
25
|
+
|
|
3
26
|
## [integrations/cohere-v9.1.0] - 2026-04-13
|
|
4
27
|
|
|
5
28
|
### 🚀 Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cohere-haystack
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.4.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
|
|
@@ -1,3 +1,4 @@
|
|
|
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
|
|
@@ -6,7 +7,6 @@ from haystack import component, default_from_dict, default_to_dict, logging
|
|
|
6
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,
|
|
@@ -275,6 +275,17 @@ def _convert_cohere_chunk_to_streaming_chunk(
|
|
|
275
275
|
arguments = chunk.delta.message.tool_calls.function.arguments
|
|
276
276
|
tool_calls = [ToolCallDelta(index=global_index, arguments=arguments)]
|
|
277
277
|
|
|
278
|
+
elif chunk.type == "citation-start":
|
|
279
|
+
# Each citation-start chunk carries one Citation object in delta.message.citations.
|
|
280
|
+
# Collect it here so _parse_streaming_response can aggregate them into the final message
|
|
281
|
+
# meta, matching the behavior of the non-streaming path which returns citations via
|
|
282
|
+
# chat_response.message.citations.
|
|
283
|
+
delta = getattr(chunk, "delta", None)
|
|
284
|
+
if delta and delta.message:
|
|
285
|
+
citation = getattr(delta.message, "citations", None)
|
|
286
|
+
if citation is not None:
|
|
287
|
+
meta["citations"] = [citation]
|
|
288
|
+
|
|
278
289
|
elif chunk.type == "message-end":
|
|
279
290
|
finish_reason_raw = getattr(chunk.delta, "finish_reason", None)
|
|
280
291
|
finish_reason = FINISH_REASON_MAPPING.get(finish_reason_raw) if finish_reason_raw else None
|
|
@@ -345,13 +356,20 @@ def _parse_streaming_response(
|
|
|
345
356
|
chunks.append(streaming_chunk)
|
|
346
357
|
streaming_callback(streaming_chunk)
|
|
347
358
|
|
|
348
|
-
|
|
359
|
+
message = _convert_streaming_chunks_to_chat_message(chunks=chunks)
|
|
360
|
+
|
|
361
|
+
# Aggregate citations collected from citation-start chunks into the message meta,
|
|
362
|
+
# mirroring the non-streaming path which sets meta["citations"] from chat_response.message.citations.
|
|
363
|
+
citations = [c for sc in chunks for c in (sc.meta.get("citations") or [])]
|
|
364
|
+
message.meta["citations"] = citations if citations else None
|
|
365
|
+
|
|
366
|
+
return message
|
|
349
367
|
|
|
350
368
|
|
|
351
369
|
async def _parse_async_streaming_response(
|
|
352
370
|
response: AsyncIterator[StreamedChatResponseV2],
|
|
353
371
|
model: str,
|
|
354
|
-
streaming_callback:
|
|
372
|
+
streaming_callback: StreamingCallbackT,
|
|
355
373
|
component_info: ComponentInfo,
|
|
356
374
|
) -> ChatMessage:
|
|
357
375
|
"""
|
|
@@ -375,9 +393,17 @@ async def _parse_async_streaming_response(
|
|
|
375
393
|
|
|
376
394
|
original_chunks.append(chunk)
|
|
377
395
|
chunks.append(streaming_chunk)
|
|
378
|
-
await
|
|
396
|
+
# sync callbacks are allowed in async contexts with Haystack >= 3.0, so only await async ones
|
|
397
|
+
callback_result = streaming_callback(streaming_chunk)
|
|
398
|
+
if inspect.isawaitable(callback_result):
|
|
399
|
+
await callback_result
|
|
379
400
|
|
|
380
|
-
|
|
401
|
+
message = _convert_streaming_chunks_to_chat_message(chunks=chunks)
|
|
402
|
+
|
|
403
|
+
citations = [c for sc in chunks for c in (sc.meta.get("citations") or [])]
|
|
404
|
+
message.meta["citations"] = citations if citations else None
|
|
405
|
+
|
|
406
|
+
return message
|
|
381
407
|
|
|
382
408
|
|
|
383
409
|
@component
|
|
@@ -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_*")
|
|
@@ -5,7 +5,11 @@ 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
|
},
|
|
@@ -742,6 +737,7 @@ class TestCohereChatGeneratorInference:
|
|
|
742
737
|
assert len(final_message.text) > 0
|
|
743
738
|
assert "paris" in final_message.text.lower()
|
|
744
739
|
|
|
740
|
+
@pytest.mark.skipif(ToolInvoker is None, reason="ToolInvoker is not available in the installed haystack-ai version")
|
|
745
741
|
def test_pipeline_with_cohere_chat_generator(self):
|
|
746
742
|
"""
|
|
747
743
|
Test that the CohereChatGenerator component can be used in a pipeline
|
|
@@ -358,6 +358,7 @@ class TestCohereChunkConversion:
|
|
|
358
358
|
"finish_reason": "tool_calls",
|
|
359
359
|
"index": 0,
|
|
360
360
|
"usage": {"prompt_tokens": 60.0, "completion_tokens": 41.0},
|
|
361
|
+
"citations": None,
|
|
361
362
|
},
|
|
362
363
|
)
|
|
363
364
|
|
|
@@ -406,3 +407,64 @@ class TestCohereChunkConversion:
|
|
|
406
407
|
result = _convert_cohere_chunk_to_streaming_chunk(chunk=mock_chunk, model="command-r-08-2024")
|
|
407
408
|
|
|
408
409
|
assert result.meta["usage"] == {"completion_tokens": 0.0, "prompt_tokens": 0.0}
|
|
410
|
+
|
|
411
|
+
def test_convert_citation_start_chunk(self):
|
|
412
|
+
citation = MagicMock()
|
|
413
|
+
citation.start = 0
|
|
414
|
+
citation.end = 10
|
|
415
|
+
citation.text = "Hello world"
|
|
416
|
+
|
|
417
|
+
delta_message = MagicMock()
|
|
418
|
+
delta_message.citations = citation
|
|
419
|
+
|
|
420
|
+
delta = MagicMock()
|
|
421
|
+
delta.message = delta_message
|
|
422
|
+
|
|
423
|
+
mock_chunk = MagicMock()
|
|
424
|
+
mock_chunk.type = "citation-start"
|
|
425
|
+
mock_chunk.delta = delta
|
|
426
|
+
|
|
427
|
+
result = _convert_cohere_chunk_to_streaming_chunk(chunk=mock_chunk, model="command-r-08-2024")
|
|
428
|
+
|
|
429
|
+
assert result.content == ""
|
|
430
|
+
assert result.start is False
|
|
431
|
+
assert result.tool_calls is None
|
|
432
|
+
assert result.finish_reason is None
|
|
433
|
+
assert result.meta["citations"] == [citation]
|
|
434
|
+
|
|
435
|
+
def test_parse_streaming_response_with_citations(self):
|
|
436
|
+
citation = MagicMock()
|
|
437
|
+
citation.start = 0
|
|
438
|
+
citation.end = 5
|
|
439
|
+
citation.text = "Hello"
|
|
440
|
+
|
|
441
|
+
delta_message = MagicMock()
|
|
442
|
+
delta_message.citations = citation
|
|
443
|
+
|
|
444
|
+
delta = MagicMock()
|
|
445
|
+
delta.message = delta_message
|
|
446
|
+
|
|
447
|
+
citation_chunk = MagicMock()
|
|
448
|
+
citation_chunk.type = "citation-start"
|
|
449
|
+
citation_chunk.delta = delta
|
|
450
|
+
|
|
451
|
+
msg_end_chunk = MsgEndStream(
|
|
452
|
+
type="message-end",
|
|
453
|
+
delta=MsgEndDelta(
|
|
454
|
+
finish_reason="COMPLETE",
|
|
455
|
+
usage=Usage(
|
|
456
|
+
billed_units=BilledUnits(input_tokens=5.0, output_tokens=5.0),
|
|
457
|
+
tokens=UsageTokens(input_tokens=50.0, output_tokens=20.0),
|
|
458
|
+
),
|
|
459
|
+
),
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
stream = iter([citation_chunk, msg_end_chunk])
|
|
463
|
+
chat_message = _parse_streaming_response(
|
|
464
|
+
stream,
|
|
465
|
+
model="command-r-08-2024",
|
|
466
|
+
streaming_callback=lambda _: None,
|
|
467
|
+
component_info=ComponentInfo(type="type"),
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
assert chat_message.meta["citations"] == [citation]
|
|
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
|
|
File without changes
|