amazon-bedrock-haystack 5.1.0__py3-none-any.whl → 5.3.0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-bedrock-haystack
3
- Version: 5.1.0
3
+ Version: 5.3.0
4
4
  Summary: An integration of AWS S3 and Bedrock as a Downloader and Generator components.
5
5
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/amazon_bedrock#readme
6
6
  Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
21
21
  Requires-Python: >=3.9
22
22
  Requires-Dist: aioboto3>=14.0.0
23
23
  Requires-Dist: boto3>=1.28.57
24
- Requires-Dist: haystack-ai>=2.17.1
24
+ Requires-Dist: haystack-ai>=2.19.0
25
25
  Description-Content-Type: text/markdown
26
26
 
27
27
  # amazon-bedrock-haystack
@@ -7,23 +7,23 @@ haystack_integrations/common/s3/errors.py,sha256=BrTDLdhQvAuQutyg35cFyP5h8PNkDEi
7
7
  haystack_integrations/common/s3/utils.py,sha256=OJupFj54aQmg6S8VuVq6Lc2qpFZyyJajRVIpwe3_6iA,4744
8
8
  haystack_integrations/components/downloaders/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  haystack_integrations/components/downloaders/s3/__init__.py,sha256=2BOd3_N0kGqRJGH-ENrTJqOqzqHryRYaSuNqpLYKMFo,179
10
- haystack_integrations/components/downloaders/s3/s3_downloader.py,sha256=qarIeGxwDCA1BOZ1qdLfE8NcQtMS9bW54a8voEBHTbE,12637
10
+ haystack_integrations/components/downloaders/s3/s3_downloader.py,sha256=jj9VMQ5QciTZvJ8Qki_zha0tcK0-APm2rMhMPObtr1M,12625
11
11
  haystack_integrations/components/embedders/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  haystack_integrations/components/embedders/amazon_bedrock/__init__.py,sha256=7GlhHJ4jFHCxq5QN5losGuGtrGNjvEx2dSQvEYD2yG0,408
13
- haystack_integrations/components/embedders/amazon_bedrock/document_embedder.py,sha256=DD34-HAGwGwTU7KWGqKXXlFdwIs21JavBRDHrBqC-m4,13060
14
- haystack_integrations/components/embedders/amazon_bedrock/document_image_embedder.py,sha256=CHNH0Dt7JQqYNbZi1lKsGvarnEhJn3UNGdghF0IhqWw,16163
15
- haystack_integrations/components/embedders/amazon_bedrock/text_embedder.py,sha256=3eSqt3XpH2thblTeOPf-ej1V2UbdG2z50d3jInq1bYc,9144
13
+ haystack_integrations/components/embedders/amazon_bedrock/document_embedder.py,sha256=vRBdqqIv09dbL9qWWy3BlrwK2P036q2d30zsIzWs-A0,13387
14
+ haystack_integrations/components/embedders/amazon_bedrock/document_image_embedder.py,sha256=qpvsJsrJ7wi9mV_euQ4pX3wqcqJjJUPW6dfoYRvi0eM,16212
15
+ haystack_integrations/components/embedders/amazon_bedrock/text_embedder.py,sha256=o7ERGCH0F6h3QzULy3yXxGxFxZdeyW8wAz4fPL3pvzQ,9513
16
16
  haystack_integrations/components/generators/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  haystack_integrations/components/generators/amazon_bedrock/__init__.py,sha256=lv4NouIVm78YavUssWQrHHP_81u-7j21qW8v1kZMJPQ,284
18
- haystack_integrations/components/generators/amazon_bedrock/adapters.py,sha256=yBC-3YwV6qAwSXMtdZiLSYh2lUpPQIDy7Efl7w-Cu-k,19640
19
- haystack_integrations/components/generators/amazon_bedrock/generator.py,sha256=Brzw0XvtPJhz2kR2I3liAqWHRmDR6p5HzJerEAPhoJU,14743
18
+ haystack_integrations/components/generators/amazon_bedrock/adapters.py,sha256=SDCi_EA-1OUmTKrnbcql9oQQuLnoAGzF_XwrKnQ1_jk,19628
19
+ haystack_integrations/components/generators/amazon_bedrock/generator.py,sha256=1vuA0x6k8UOvPjeKjYvcYORpsTFXs1rKMUXlima51IY,14725
20
20
  haystack_integrations/components/generators/amazon_bedrock/chat/__init__.py,sha256=6GZ8Y3Lw0rLOsOAqi6Tu5mZC977UzQvgDxKpOWr8IQw,110
21
- haystack_integrations/components/generators/amazon_bedrock/chat/chat_generator.py,sha256=qArwfXcforWnPzLXrAW-1hkPFpMy3NSdDyJ5GOta25w,26068
22
- haystack_integrations/components/generators/amazon_bedrock/chat/utils.py,sha256=1M_k8CG2WH23Yz-sB7a1kiIqVh2QB8Pqi0zbWXyMUL8,27255
21
+ haystack_integrations/components/generators/amazon_bedrock/chat/chat_generator.py,sha256=batM98HDpUyZEwG-pGYhgTQ6Z9uoGE2TUb3EaZjmox4,26227
22
+ haystack_integrations/components/generators/amazon_bedrock/chat/utils.py,sha256=sWgh-58YeDIatDFNLX2YG1KPJghsyXK5p5uVM4pe8U4,27236
23
23
  haystack_integrations/components/rankers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  haystack_integrations/components/rankers/amazon_bedrock/__init__.py,sha256=mJQKShAP5AfZvfKQisSh7kfKu6RIXzsYdk4eqMtcaEk,75
25
- haystack_integrations/components/rankers/amazon_bedrock/ranker.py,sha256=QWtUKfJxMrlfLCWTb8cCP-lKEthnEBwnTd1NSbiFMkg,11812
26
- amazon_bedrock_haystack-5.1.0.dist-info/METADATA,sha256=W5IZ1NhzQ11GjMXl5WrDjRJ0MHnV8jpAHAMyZwNe4Yo,2228
27
- amazon_bedrock_haystack-5.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
- amazon_bedrock_haystack-5.1.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
29
- amazon_bedrock_haystack-5.1.0.dist-info/RECORD,,
25
+ haystack_integrations/components/rankers/amazon_bedrock/ranker.py,sha256=oFNTF2BnKNA1VERiF42ttTFDzxOiaBIA1H5ozLNFFFU,11800
26
+ amazon_bedrock_haystack-5.3.0.dist-info/METADATA,sha256=2z-MOhgu2Qp1Jnc56fs6ANIcmZo7ocLfwgtek22dvJY,2228
27
+ amazon_bedrock_haystack-5.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
28
+ amazon_bedrock_haystack-5.3.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
29
+ amazon_bedrock_haystack-5.3.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -5,7 +5,7 @@
5
5
  import os
6
6
  from concurrent.futures import ThreadPoolExecutor
7
7
  from pathlib import Path
8
- from typing import Any, Callable, Dict, List, Optional
8
+ from typing import Any, Callable, Optional
9
9
 
10
10
  from botocore.config import Config
11
11
  from haystack import component, default_from_dict, default_to_dict, logging
@@ -36,9 +36,9 @@ class S3Downloader:
36
36
  aws_session_token: Optional[Secret] = Secret.from_env_var("AWS_SESSION_TOKEN", strict=False), # noqa: B008
37
37
  aws_region_name: Optional[Secret] = Secret.from_env_var("AWS_DEFAULT_REGION", strict=False), # noqa: B008
38
38
  aws_profile_name: Optional[Secret] = Secret.from_env_var("AWS_PROFILE", strict=False), # noqa: B008
39
- boto3_config: Optional[Dict[str, Any]] = None,
39
+ boto3_config: Optional[dict[str, Any]] = None,
40
40
  file_root_path: Optional[str] = None,
41
- file_extensions: Optional[List[str]] = None,
41
+ file_extensions: Optional[list[str]] = None,
42
42
  file_name_meta_key: str = "file_name",
43
43
  max_workers: int = 32,
44
44
  max_cache_size: int = 100,
@@ -126,11 +126,11 @@ class S3Downloader:
126
126
  self.file_root_path.mkdir(parents=True, exist_ok=True)
127
127
  self._storage = S3Storage.from_env(session=self._session, config=self._config)
128
128
 
129
- @component.output_types(documents=List[Document])
129
+ @component.output_types(documents=list[Document])
130
130
  def run(
131
131
  self,
132
- documents: List[Document],
133
- ) -> Dict[str, List[Document]]:
132
+ documents: list[Document],
133
+ ) -> dict[str, list[Document]]:
134
134
  """Download files from AWS S3 Buckets to local filesystem.
135
135
 
136
136
  Return enriched `Document`s with the path of the downloaded file.
@@ -160,7 +160,7 @@ class S3Downloader:
160
160
  downloaded_documents = [d for d in iterable if d is not None]
161
161
  return {"documents": downloaded_documents}
162
162
 
163
- def _filter_documents_by_extensions(self, documents: List[Document]) -> List[Document]:
163
+ def _filter_documents_by_extensions(self, documents: list[Document]) -> list[Document]:
164
164
  """Filter documents by file extensions."""
165
165
  if not self.file_extensions:
166
166
  return documents
@@ -202,7 +202,7 @@ class S3Downloader:
202
202
  document.meta["file_path"] = str(file_path)
203
203
  return document
204
204
 
205
- def _cleanup_cache(self, documents: List[Document]) -> None:
205
+ def _cleanup_cache(self, documents: list[Document]) -> None:
206
206
  """
207
207
  Remove least-recently-accessed cache files when cache exceeds `max_cache_size`.
208
208
 
@@ -224,7 +224,7 @@ class S3Downloader:
224
224
  except Exception as error:
225
225
  logger.warning("Failed to remove cache file at {path} with error: {e}", path=p, e=error)
226
226
 
227
- def to_dict(self) -> Dict[str, Any]:
227
+ def to_dict(self) -> dict[str, Any]:
228
228
  """Serialize the component to a dictionary."""
229
229
 
230
230
  s3_key_generation_function_name = (
@@ -247,7 +247,7 @@ class S3Downloader:
247
247
  )
248
248
 
249
249
  @classmethod
250
- def from_dict(cls, data: Dict[str, Any]) -> "S3Downloader":
250
+ def from_dict(cls, data: dict[str, Any]) -> "S3Downloader":
251
251
  """
252
252
  Deserializes the component from a dictionary.
253
253
  :param data:
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import Any, Dict, List, Literal, Optional
2
+ from typing import Any, Literal, Optional
3
3
 
4
4
  from botocore.config import Config
5
5
  from botocore.exceptions import ClientError
@@ -18,10 +18,11 @@ logger = logging.getLogger(__name__)
18
18
 
19
19
  SUPPORTED_EMBEDDING_MODELS = [
20
20
  "amazon.titan-embed-text-v1",
21
- "cohere.embed-english-v3",
22
- "cohere.embed-multilingual-v3",
23
21
  "amazon.titan-embed-text-v2:0",
24
22
  "amazon.titan-embed-image-v1",
23
+ "cohere.embed-english-v3",
24
+ "cohere.embed-multilingual-v3",
25
+ "cohere.embed-v4:0",
25
26
  ]
26
27
 
27
28
 
@@ -59,10 +60,11 @@ class AmazonBedrockDocumentEmbedder:
59
60
  self,
60
61
  model: Literal[
61
62
  "amazon.titan-embed-text-v1",
62
- "cohere.embed-english-v3",
63
- "cohere.embed-multilingual-v3",
64
63
  "amazon.titan-embed-text-v2:0",
65
64
  "amazon.titan-embed-image-v1",
65
+ "cohere.embed-english-v3",
66
+ "cohere.embed-multilingual-v3",
67
+ "cohere.embed-v4:0",
66
68
  ],
67
69
  aws_access_key_id: Optional[Secret] = Secret.from_env_var("AWS_ACCESS_KEY_ID", strict=False), # noqa: B008
68
70
  aws_secret_access_key: Optional[Secret] = Secret.from_env_var( # noqa: B008
@@ -73,9 +75,9 @@ class AmazonBedrockDocumentEmbedder:
73
75
  aws_profile_name: Optional[Secret] = Secret.from_env_var("AWS_PROFILE", strict=False), # noqa: B008
74
76
  batch_size: int = 32,
75
77
  progress_bar: bool = True,
76
- meta_fields_to_embed: Optional[List[str]] = None,
78
+ meta_fields_to_embed: Optional[list[str]] = None,
77
79
  embedding_separator: str = "\n",
78
- boto3_config: Optional[Dict[str, Any]] = None,
80
+ boto3_config: Optional[dict[str, Any]] = None,
79
81
  **kwargs: Any,
80
82
  ) -> None:
81
83
  """
@@ -149,7 +151,7 @@ class AmazonBedrockDocumentEmbedder:
149
151
  )
150
152
  raise AmazonBedrockConfigurationError(msg) from exception
151
153
 
152
- def _prepare_texts_to_embed(self, documents: List[Document]) -> List[str]:
154
+ def _prepare_texts_to_embed(self, documents: list[Document]) -> list[str]:
153
155
  """
154
156
  Prepare the texts to embed by concatenating the Document text with the metadata fields to embed.
155
157
  """
@@ -162,7 +164,7 @@ class AmazonBedrockDocumentEmbedder:
162
164
  texts_to_embed.append(text_to_embed)
163
165
  return texts_to_embed
164
166
 
165
- def _embed_cohere(self, documents: List[Document]) -> List[Document]:
167
+ def _embed_cohere(self, documents: list[Document]) -> list[Document]:
166
168
  """
167
169
  Internal method to embed Documents using Cohere models.
168
170
  Batch inference is supported.
@@ -191,15 +193,19 @@ class AmazonBedrockDocumentEmbedder:
191
193
  msg = f"Could not perform inference for Amazon Bedrock model {self.model} due to:\n{exception}"
192
194
  raise AmazonBedrockInferenceError(msg) from exception
193
195
 
194
- response_body = json.loads(response.get("body").read())
195
- all_embeddings.extend(response_body["embeddings"])
196
+ cohere_embeddings = json.loads(response.get("body").read())["embeddings"]
197
+ # depending on the model, Cohere returns a dict with the embedding types as keys or a list of lists
198
+ embeddings_list = (
199
+ next(iter(cohere_embeddings.values())) if isinstance(cohere_embeddings, dict) else cohere_embeddings
200
+ )
201
+ all_embeddings.extend(embeddings_list)
196
202
 
197
203
  for doc, emb in zip(documents, all_embeddings):
198
204
  doc.embedding = emb
199
205
 
200
206
  return documents
201
207
 
202
- def _embed_titan(self, documents: List[Document]) -> List[Document]:
208
+ def _embed_titan(self, documents: list[Document]) -> list[Document]:
203
209
  """
204
210
  Internal method to embed Documents using Amazon Titan models.
205
211
  NOTE: Batch inference is not supported, so embeddings are created one by one.
@@ -227,8 +233,8 @@ class AmazonBedrockDocumentEmbedder:
227
233
 
228
234
  return documents
229
235
 
230
- @component.output_types(documents=List[Document])
231
- def run(self, documents: List[Document]) -> Dict[str, List[Document]]:
236
+ @component.output_types(documents=list[Document])
237
+ def run(self, documents: list[Document]) -> dict[str, list[Document]]:
232
238
  """Embed the provided `Document`s using the specified model.
233
239
 
234
240
  :param documents: The `Document`s to embed.
@@ -253,7 +259,7 @@ class AmazonBedrockDocumentEmbedder:
253
259
 
254
260
  return {"documents": documents_with_embeddings}
255
261
 
256
- def to_dict(self) -> Dict[str, Any]:
262
+ def to_dict(self) -> dict[str, Any]:
257
263
  """
258
264
  Serializes the component to a dictionary.
259
265
 
@@ -277,7 +283,7 @@ class AmazonBedrockDocumentEmbedder:
277
283
  )
278
284
 
279
285
  @classmethod
280
- def from_dict(cls, data: Dict[str, Any]) -> "AmazonBedrockDocumentEmbedder":
286
+ def from_dict(cls, data: dict[str, Any]) -> "AmazonBedrockDocumentEmbedder":
281
287
  """
282
288
  Deserializes the component from a dictionary.
283
289
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  import json
6
6
  from dataclasses import replace
7
- from typing import Any, Dict, List, Literal, Optional, Tuple
7
+ from typing import Any, Literal, Optional
8
8
 
9
9
  from botocore.config import Config
10
10
  from botocore.exceptions import ClientError
@@ -27,7 +27,12 @@ from haystack_integrations.common.amazon_bedrock.utils import get_aws_session
27
27
 
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
- SUPPORTED_EMBEDDING_MODELS = ["amazon.titan-embed-image-v1", "cohere.embed-english-v3", "cohere.embed-multilingual-v3"]
30
+ SUPPORTED_EMBEDDING_MODELS = [
31
+ "amazon.titan-embed-image-v1",
32
+ "cohere.embed-english-v3",
33
+ "cohere.embed-multilingual-v3",
34
+ "cohere.embed-v4:0",
35
+ ]
31
36
 
32
37
 
33
38
  @component
@@ -69,7 +74,12 @@ class AmazonBedrockDocumentImageEmbedder:
69
74
  def __init__(
70
75
  self,
71
76
  *,
72
- model: Literal["amazon.titan-embed-image-v1", "cohere.embed-english-v3", "cohere.embed-multilingual-v3"],
77
+ model: Literal[
78
+ "amazon.titan-embed-image-v1",
79
+ "cohere.embed-english-v3",
80
+ "cohere.embed-multilingual-v3",
81
+ "cohere.embed-v4:0",
82
+ ],
73
83
  aws_access_key_id: Optional[Secret] = Secret.from_env_var("AWS_ACCESS_KEY_ID", strict=False), # noqa: B008
74
84
  aws_secret_access_key: Optional[Secret] = Secret.from_env_var( # noqa: B008
75
85
  "AWS_SECRET_ACCESS_KEY", strict=False
@@ -79,9 +89,9 @@ class AmazonBedrockDocumentImageEmbedder:
79
89
  aws_profile_name: Optional[Secret] = Secret.from_env_var("AWS_PROFILE", strict=False), # noqa: B008
80
90
  file_path_meta_field: str = "file_path",
81
91
  root_path: Optional[str] = None,
82
- image_size: Optional[Tuple[int, int]] = None,
92
+ image_size: Optional[tuple[int, int]] = None,
83
93
  progress_bar: bool = True,
84
- boto3_config: Optional[Dict[str, Any]] = None,
94
+ boto3_config: Optional[dict[str, Any]] = None,
85
95
  **kwargs: Any,
86
96
  ) -> None:
87
97
  """
@@ -93,6 +103,7 @@ class AmazonBedrockDocumentImageEmbedder:
93
103
  - "amazon.titan-embed-image-v1"
94
104
  - "cohere.embed-english-v3"
95
105
  - "cohere.embed-multilingual-v3"
106
+ - "cohere.embed-v4:0"
96
107
  :param aws_access_key_id: AWS access key ID.
97
108
  :param aws_secret_access_key: AWS secret access key.
98
109
  :param aws_session_token: AWS session token.
@@ -135,14 +146,14 @@ class AmazonBedrockDocumentImageEmbedder:
135
146
  self.kwargs = kwargs
136
147
  self.embedding_types = None
137
148
 
138
- if emmbedding_types := self.kwargs.get("embedding_types"):
139
- if len(emmbedding_types) > 1:
149
+ if embedding_types := self.kwargs.get("embedding_types"):
150
+ if len(embedding_types) > 1:
140
151
  msg = (
141
152
  "You have provided multiple embedding_types for Cohere model. "
142
153
  "AmazonBedrockDocumentImageEmbedder only supports one embedding_type at a time."
143
154
  )
144
155
  raise ValueError(msg)
145
- self.embedding_types = emmbedding_types
156
+ self.embedding_types = embedding_types
146
157
 
147
158
  def resolve_secret(secret: Optional[Secret]) -> Optional[str]:
148
159
  return secret.resolve_value() if secret else None
@@ -296,7 +307,7 @@ class AmazonBedrockDocumentImageEmbedder:
296
307
 
297
308
  return {"documents": docs_with_embeddings}
298
309
 
299
- def _embed_titan(self, images: List[str]) -> List[List[float]]:
310
+ def _embed_titan(self, images: list[str]) -> list[list[float]]:
300
311
  """
301
312
  Internal method to embed base64 images using Amazon Titan models.
302
313
 
@@ -326,7 +337,7 @@ class AmazonBedrockDocumentImageEmbedder:
326
337
 
327
338
  return all_embeddings
328
339
 
329
- def _embed_cohere(self, image_uris: List[str]) -> List[List[float]]:
340
+ def _embed_cohere(self, image_uris: list[str]) -> list[list[float]]:
330
341
  """
331
342
  Internal method to embed base64 images using Cohere models.
332
343
 
@@ -351,15 +362,13 @@ class AmazonBedrockDocumentImageEmbedder:
351
362
  raise AmazonBedrockInferenceError(msg) from exception
352
363
 
353
364
  response_body = json.loads(response.get("body").read())
354
- embeddings = response_body["embeddings"]
365
+ cohere_embeddings = response_body["embeddings"]
355
366
 
356
- # if embedding_types is specified, cohere returns a dict with the embedding types as keys
357
- if isinstance(embeddings, dict):
358
- for embedding in embeddings.values():
359
- all_embeddings.append(embedding[0])
360
- else:
361
- # if embedding_types is not specified, cohere returns
362
- # a nested list of float embeddings
363
- all_embeddings.append(embeddings[0])
367
+ # depending on the model and embedding_types, Cohere returns a dict with the embedding types as keys
368
+ # or a list of lists
369
+ embeddings_list = (
370
+ next(iter(cohere_embeddings.values())) if isinstance(cohere_embeddings, dict) else cohere_embeddings
371
+ )
372
+ all_embeddings.extend(embeddings_list)
364
373
 
365
374
  return all_embeddings
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import Any, Dict, List, Literal, Optional
2
+ from typing import Any, Literal, Optional
3
3
 
4
4
  from botocore.config import Config
5
5
  from botocore.exceptions import ClientError
@@ -16,10 +16,11 @@ logger = logging.getLogger(__name__)
16
16
 
17
17
  SUPPORTED_EMBEDDING_MODELS = [
18
18
  "amazon.titan-embed-text-v1",
19
- "cohere.embed-english-v3",
20
- "cohere.embed-multilingual-v3",
21
19
  "amazon.titan-embed-text-v2:0",
22
20
  "amazon.titan-embed-image-v1",
21
+ "cohere.embed-english-v3",
22
+ "cohere.embed-multilingual-v3",
23
+ "cohere.embed-v4:0",
23
24
  ]
24
25
 
25
26
 
@@ -52,10 +53,11 @@ class AmazonBedrockTextEmbedder:
52
53
  self,
53
54
  model: Literal[
54
55
  "amazon.titan-embed-text-v1",
55
- "cohere.embed-english-v3",
56
- "cohere.embed-multilingual-v3",
57
56
  "amazon.titan-embed-text-v2:0",
58
57
  "amazon.titan-embed-image-v1",
58
+ "cohere.embed-english-v3",
59
+ "cohere.embed-multilingual-v3",
60
+ "cohere.embed-v4:0",
59
61
  ],
60
62
  aws_access_key_id: Optional[Secret] = Secret.from_env_var("AWS_ACCESS_KEY_ID", strict=False), # noqa: B008
61
63
  aws_secret_access_key: Optional[Secret] = Secret.from_env_var( # noqa: B008
@@ -64,7 +66,7 @@ class AmazonBedrockTextEmbedder:
64
66
  aws_session_token: Optional[Secret] = Secret.from_env_var("AWS_SESSION_TOKEN", strict=False), # noqa: B008
65
67
  aws_region_name: Optional[Secret] = Secret.from_env_var("AWS_DEFAULT_REGION", strict=False), # noqa: B008
66
68
  aws_profile_name: Optional[Secret] = Secret.from_env_var("AWS_PROFILE", strict=False), # noqa: B008
67
- boto3_config: Optional[Dict[str, Any]] = None,
69
+ boto3_config: Optional[dict[str, Any]] = None,
68
70
  **kwargs: Any,
69
71
  ) -> None:
70
72
  """
@@ -127,8 +129,8 @@ class AmazonBedrockTextEmbedder:
127
129
  )
128
130
  raise AmazonBedrockConfigurationError(msg) from exception
129
131
 
130
- @component.output_types(embedding=List[float])
131
- def run(self, text: str) -> Dict[str, List[float]]:
132
+ @component.output_types(embedding=list[float])
133
+ def run(self, text: str) -> dict[str, list[float]]:
132
134
  """Embeds the input text using the Amazon Bedrock model.
133
135
 
134
136
  :param text: The input text to embed.
@@ -168,7 +170,12 @@ class AmazonBedrockTextEmbedder:
168
170
  response_body = json.loads(response.get("body").read())
169
171
 
170
172
  if "cohere" in self.model:
171
- embedding = response_body["embeddings"][0]
173
+ cohere_embeddings = response_body["embeddings"]
174
+ # depending on the model, Cohere returns a dict with the embedding types as keys or a list of lists
175
+ embeddings_list = (
176
+ next(iter(cohere_embeddings.values())) if isinstance(cohere_embeddings, dict) else cohere_embeddings
177
+ )
178
+ embedding = embeddings_list[0]
172
179
  elif "titan" in self.model:
173
180
  embedding = response_body["embedding"]
174
181
  else:
@@ -177,7 +184,7 @@ class AmazonBedrockTextEmbedder:
177
184
 
178
185
  return {"embedding": embedding}
179
186
 
180
- def to_dict(self) -> Dict[str, Any]:
187
+ def to_dict(self) -> dict[str, Any]:
181
188
  """
182
189
  Serializes the component to a dictionary.
183
190
 
@@ -197,7 +204,7 @@ class AmazonBedrockTextEmbedder:
197
204
  )
198
205
 
199
206
  @classmethod
200
- def from_dict(cls, data: Dict[str, Any]) -> "AmazonBedrockTextEmbedder":
207
+ def from_dict(cls, data: dict[str, Any]) -> "AmazonBedrockTextEmbedder":
201
208
  """
202
209
  Deserializes the component from a dictionary.
203
210
 
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from abc import ABC, abstractmethod
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Optional
4
4
 
5
5
  from botocore.eventstream import EventStream
6
6
  from haystack.dataclasses import StreamingChunk, SyncStreamingCallbackT
@@ -19,12 +19,12 @@ class BedrockModelAdapter(ABC):
19
19
  It will be overridden by the corresponding parameter in the `model_kwargs` if it is present.
20
20
  """
21
21
 
22
- def __init__(self, model_kwargs: Dict[str, Any], max_length: Optional[int]) -> None:
22
+ def __init__(self, model_kwargs: dict[str, Any], max_length: Optional[int]) -> None:
23
23
  self.model_kwargs = model_kwargs
24
24
  self.max_length = max_length
25
25
 
26
26
  @abstractmethod
27
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
27
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
28
28
  """
29
29
  Prepares the body for the Amazon Bedrock request.
30
30
  Each subclass should implement this method to prepare the request body for the specific model.
@@ -34,7 +34,7 @@ class BedrockModelAdapter(ABC):
34
34
  :returns: A dictionary containing the body for the request.
35
35
  """
36
36
 
37
- def get_responses(self, response_body: Dict[str, Any]) -> List[str]:
37
+ def get_responses(self, response_body: dict[str, Any]) -> list[str]:
38
38
  """
39
39
  Extracts the responses from the Amazon Bedrock response.
40
40
 
@@ -45,7 +45,7 @@ class BedrockModelAdapter(ABC):
45
45
  responses = [completion.lstrip() for completion in completions]
46
46
  return responses
47
47
 
48
- def get_stream_responses(self, stream: EventStream, streaming_callback: SyncStreamingCallbackT) -> List[str]:
48
+ def get_stream_responses(self, stream: EventStream, streaming_callback: SyncStreamingCallbackT) -> list[str]:
49
49
  """
50
50
  Extracts the responses from the Amazon Bedrock streaming response.
51
51
 
@@ -53,7 +53,7 @@ class BedrockModelAdapter(ABC):
53
53
  :param streaming_callback: The handler for the streaming response.
54
54
  :returns: A list of string responses.
55
55
  """
56
- streaming_chunks: List[StreamingChunk] = []
56
+ streaming_chunks: list[StreamingChunk] = []
57
57
  for event in stream:
58
58
  chunk = event.get("chunk")
59
59
  if chunk:
@@ -65,7 +65,7 @@ class BedrockModelAdapter(ABC):
65
65
  responses = ["".join(streaming_chunk.content for streaming_chunk in streaming_chunks).lstrip()]
66
66
  return responses
67
67
 
68
- def _get_params(self, inference_kwargs: Dict[str, Any], default_params: Dict[str, Any]) -> Dict[str, Any]:
68
+ def _get_params(self, inference_kwargs: dict[str, Any], default_params: dict[str, Any]) -> dict[str, Any]:
69
69
  """
70
70
  Merges the default params with the inference kwargs and model kwargs.
71
71
 
@@ -83,7 +83,7 @@ class BedrockModelAdapter(ABC):
83
83
  }
84
84
 
85
85
  @abstractmethod
86
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
86
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
87
87
  """
88
88
  Extracts the responses from the Amazon Bedrock response.
89
89
 
@@ -92,7 +92,7 @@ class BedrockModelAdapter(ABC):
92
92
  """
93
93
 
94
94
  @abstractmethod
95
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
95
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
96
96
  """
97
97
  Extracts the content and meta from a streaming chunk.
98
98
 
@@ -115,7 +115,7 @@ class AnthropicClaudeAdapter(BedrockModelAdapter):
115
115
  :param max_length: Maximum length of generated text
116
116
  """
117
117
 
118
- def __init__(self, model_kwargs: Dict[str, Any], max_length: Optional[int]) -> None:
118
+ def __init__(self, model_kwargs: dict[str, Any], max_length: Optional[int]) -> None:
119
119
  self.use_messages_api = model_kwargs.get("use_messages_api", True)
120
120
  self.include_thinking = model_kwargs.get("include_thinking", True)
121
121
  self.thinking_tag = model_kwargs.get("thinking_tag", "thinking")
@@ -123,7 +123,7 @@ class AnthropicClaudeAdapter(BedrockModelAdapter):
123
123
  self.thinking_tag_end = f"</{self.thinking_tag}>\n\n" if self.thinking_tag else "\n\n"
124
124
  super().__init__(model_kwargs, max_length)
125
125
 
126
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
126
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
127
127
  """
128
128
  Prepares the body for the Claude model
129
129
 
@@ -134,7 +134,7 @@ class AnthropicClaudeAdapter(BedrockModelAdapter):
134
134
  - specified inference parameters.
135
135
  """
136
136
  if self.use_messages_api:
137
- default_params: Dict[str, Any] = {
137
+ default_params: dict[str, Any] = {
138
138
  "anthropic_version": "bedrock-2023-05-31",
139
139
  "max_tokens": self.max_length,
140
140
  "system": None,
@@ -160,7 +160,7 @@ class AnthropicClaudeAdapter(BedrockModelAdapter):
160
160
  body = {"prompt": f"\n\nHuman: {prompt}\n\nAssistant:", **params}
161
161
  return body
162
162
 
163
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
163
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
164
164
  """
165
165
  Extracts the responses from the Amazon Bedrock response.
166
166
 
@@ -181,7 +181,7 @@ class AnthropicClaudeAdapter(BedrockModelAdapter):
181
181
 
182
182
  return [response_body["completion"]]
183
183
 
184
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
184
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
185
185
  """
186
186
  Extracts the content and meta from a streaming chunk.
187
187
 
@@ -211,7 +211,7 @@ class MistralAdapter(BedrockModelAdapter):
211
211
  Adapter for the Mistral models.
212
212
  """
213
213
 
214
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
214
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
215
215
  """
216
216
  Prepares the body for the Mistral model
217
217
 
@@ -221,7 +221,7 @@ class MistralAdapter(BedrockModelAdapter):
221
221
  - `prompt`: The prompt to be sent to the model.
222
222
  - specified inference parameters.
223
223
  """
224
- default_params: Dict[str, Any] = {
224
+ default_params: dict[str, Any] = {
225
225
  "max_tokens": self.max_length,
226
226
  "stop": [],
227
227
  "temperature": None,
@@ -233,7 +233,7 @@ class MistralAdapter(BedrockModelAdapter):
233
233
  formatted_prompt = f"<s>[INST] {prompt} [/INST]" if "INST" not in prompt else prompt
234
234
  return {"prompt": formatted_prompt, **params}
235
235
 
236
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
236
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
237
237
  """
238
238
  Extracts the responses from the Amazon Bedrock response.
239
239
 
@@ -242,7 +242,7 @@ class MistralAdapter(BedrockModelAdapter):
242
242
  """
243
243
  return [output.get("text", "") for output in response_body.get("outputs", [])]
244
244
 
245
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
245
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
246
246
  """
247
247
  Extracts the content and meta from a streaming chunk.
248
248
 
@@ -261,7 +261,7 @@ class CohereCommandAdapter(BedrockModelAdapter):
261
261
  Adapter for the Cohere Command model.
262
262
  """
263
263
 
264
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
264
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
265
265
  """
266
266
  Prepares the body for the Command model
267
267
 
@@ -288,7 +288,7 @@ class CohereCommandAdapter(BedrockModelAdapter):
288
288
  body = {"prompt": prompt, **params}
289
289
  return body
290
290
 
291
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
291
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
292
292
  """
293
293
  Extracts the responses from the Cohere Command model response.
294
294
 
@@ -298,7 +298,7 @@ class CohereCommandAdapter(BedrockModelAdapter):
298
298
  responses = [generation["text"] for generation in response_body["generations"]]
299
299
  return responses
300
300
 
301
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
301
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
302
302
  """
303
303
  Extracts the content and meta from a streaming chunk.
304
304
 
@@ -313,7 +313,7 @@ class CohereCommandRAdapter(BedrockModelAdapter):
313
313
  Adapter for the Cohere Command R models.
314
314
  """
315
315
 
316
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
316
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
317
317
  """
318
318
  Prepares the body for the Command model
319
319
 
@@ -347,7 +347,7 @@ class CohereCommandRAdapter(BedrockModelAdapter):
347
347
  body = {"message": prompt, **params}
348
348
  return body
349
349
 
350
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
350
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
351
351
  """
352
352
  Extracts the responses from the Cohere Command model response.
353
353
 
@@ -357,7 +357,7 @@ class CohereCommandRAdapter(BedrockModelAdapter):
357
357
  responses = [response_body["text"]]
358
358
  return responses
359
359
 
360
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
360
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
361
361
  """
362
362
  Extracts the content and meta from a streaming chunk.
363
363
 
@@ -373,7 +373,7 @@ class AI21LabsJurassic2Adapter(BedrockModelAdapter):
373
373
  Model adapter for AI21 Labs' Jurassic 2 models.
374
374
  """
375
375
 
376
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
376
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
377
377
  """Prepares the body for the Jurassic 2 model.
378
378
 
379
379
  :param prompt: The prompt to be sent to the model.
@@ -397,11 +397,11 @@ class AI21LabsJurassic2Adapter(BedrockModelAdapter):
397
397
  body = {"prompt": prompt, **params}
398
398
  return body
399
399
 
400
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
400
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
401
401
  responses = [completion["data"]["text"] for completion in response_body["completions"]]
402
402
  return responses
403
403
 
404
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
404
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
405
405
  msg = "Streaming is not supported for AI21 Jurassic 2 models."
406
406
  raise NotImplementedError(msg)
407
407
 
@@ -411,7 +411,7 @@ class AmazonTitanAdapter(BedrockModelAdapter):
411
411
  Adapter for Amazon's Titan models.
412
412
  """
413
413
 
414
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
414
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
415
415
  """
416
416
  Prepares the body for the Titan model
417
417
 
@@ -432,7 +432,7 @@ class AmazonTitanAdapter(BedrockModelAdapter):
432
432
  body = {"inputText": prompt, "textGenerationConfig": params}
433
433
  return body
434
434
 
435
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
435
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
436
436
  """
437
437
  Extracts the responses from the Titan model response.
438
438
 
@@ -442,7 +442,7 @@ class AmazonTitanAdapter(BedrockModelAdapter):
442
442
  responses = [result["outputText"] for result in response_body["results"]]
443
443
  return responses
444
444
 
445
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
445
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
446
446
  """
447
447
  Extracts the content and meta from a streaming chunk.
448
448
 
@@ -457,7 +457,7 @@ class MetaLlamaAdapter(BedrockModelAdapter):
457
457
  Adapter for Meta's Llama2 models.
458
458
  """
459
459
 
460
- def prepare_body(self, prompt: str, **inference_kwargs: Any) -> Dict[str, Any]:
460
+ def prepare_body(self, prompt: str, **inference_kwargs: Any) -> dict[str, Any]:
461
461
  """
462
462
  Prepares the body for the Llama2 model
463
463
 
@@ -477,7 +477,7 @@ class MetaLlamaAdapter(BedrockModelAdapter):
477
477
  body = {"prompt": prompt, **params}
478
478
  return body
479
479
 
480
- def _extract_completions_from_response(self, response_body: Dict[str, Any]) -> List[str]:
480
+ def _extract_completions_from_response(self, response_body: dict[str, Any]) -> list[str]:
481
481
  """
482
482
  Extracts the responses from the Llama2 model response.
483
483
 
@@ -486,7 +486,7 @@ class MetaLlamaAdapter(BedrockModelAdapter):
486
486
  """
487
487
  return [response_body["generation"]]
488
488
 
489
- def _build_streaming_chunk(self, chunk: Dict[str, Any]) -> StreamingChunk:
489
+ def _build_streaming_chunk(self, chunk: dict[str, Any]) -> StreamingChunk:
490
490
  """
491
491
  Extracts the content and meta from a streaming chunk.
492
492
 
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List, Optional, Tuple, Union
1
+ from typing import Any, Optional
2
2
 
3
3
  import aioboto3
4
4
  from botocore.config import Config
@@ -7,10 +7,10 @@ from botocore.exceptions import ClientError
7
7
  from haystack import component, default_from_dict, default_to_dict, logging
8
8
  from haystack.dataclasses import ChatMessage, ComponentInfo, StreamingCallbackT, select_streaming_callback
9
9
  from haystack.tools import (
10
- Tool,
11
- Toolset,
10
+ ToolsType,
12
11
  _check_duplicate_tool_names,
13
12
  deserialize_tools_or_toolset_inplace,
13
+ flatten_tools_or_toolsets,
14
14
  serialize_tools_or_toolset,
15
15
  )
16
16
  from haystack.utils.auth import Secret, deserialize_secrets_inplace
@@ -154,12 +154,12 @@ class AmazonBedrockChatGenerator:
154
154
  aws_session_token: Optional[Secret] = Secret.from_env_var(["AWS_SESSION_TOKEN"], strict=False), # noqa: B008
155
155
  aws_region_name: Optional[Secret] = Secret.from_env_var(["AWS_DEFAULT_REGION"], strict=False), # noqa: B008
156
156
  aws_profile_name: Optional[Secret] = Secret.from_env_var(["AWS_PROFILE"], strict=False), # noqa: B008
157
- generation_kwargs: Optional[Dict[str, Any]] = None,
157
+ generation_kwargs: Optional[dict[str, Any]] = None,
158
158
  streaming_callback: Optional[StreamingCallbackT] = None,
159
- boto3_config: Optional[Dict[str, Any]] = None,
160
- tools: Optional[Union[List[Tool], Toolset]] = None,
159
+ boto3_config: Optional[dict[str, Any]] = None,
160
+ tools: Optional[ToolsType] = None,
161
161
  *,
162
- guardrail_config: Optional[Dict[str, str]] = None,
162
+ guardrail_config: Optional[dict[str, str]] = None,
163
163
  ) -> None:
164
164
  """
165
165
  Initializes the `AmazonBedrockChatGenerator` with the provided parameters. The parameters are passed to the
@@ -187,7 +187,8 @@ class AmazonBedrockChatGenerator:
187
187
  [StreamingChunk](https://docs.haystack.deepset.ai/docs/data-classes#streamingchunk) object and switches
188
188
  the streaming mode on.
189
189
  :param boto3_config: The configuration for the boto3 client.
190
- :param tools: A list of Tool objects or a Toolset that the model can use. Each tool should have a unique name.
190
+ :param tools: A list of Tool and/or Toolset objects, or a single Toolset for which the model can prepare calls.
191
+ Each tool should have a unique name.
191
192
  :param guardrail_config: Optional configuration for a guardrail that has been created in Amazon Bedrock.
192
193
  This must be provided as a dictionary matching either
193
194
  [GuardrailConfiguration](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_GuardrailConfiguration.html).
@@ -218,7 +219,7 @@ class AmazonBedrockChatGenerator:
218
219
  self.streaming_callback = streaming_callback
219
220
  self.boto3_config = boto3_config
220
221
 
221
- _check_duplicate_tool_names(list(tools or [])) # handles Toolset as well
222
+ _check_duplicate_tool_names(flatten_tools_or_toolsets(tools))
222
223
  self.tools = tools
223
224
 
224
225
  _validate_guardrail_config(guardrail_config=guardrail_config, streaming=streaming_callback is not None)
@@ -288,7 +289,7 @@ class AmazonBedrockChatGenerator:
288
289
  )
289
290
  raise AmazonBedrockConfigurationError(msg) from exception
290
291
 
291
- def to_dict(self) -> Dict[str, Any]:
292
+ def to_dict(self) -> dict[str, Any]:
292
293
  """
293
294
  Serializes the component to a dictionary.
294
295
 
@@ -312,7 +313,7 @@ class AmazonBedrockChatGenerator:
312
313
  )
313
314
 
314
315
  @classmethod
315
- def from_dict(cls, data: Dict[str, Any]) -> "AmazonBedrockChatGenerator":
316
+ def from_dict(cls, data: dict[str, Any]) -> "AmazonBedrockChatGenerator":
316
317
  """
317
318
  Deserializes the component from a dictionary.
318
319
 
@@ -339,12 +340,12 @@ class AmazonBedrockChatGenerator:
339
340
 
340
341
  def _prepare_request_params(
341
342
  self,
342
- messages: List[ChatMessage],
343
+ messages: list[ChatMessage],
343
344
  streaming_callback: Optional[StreamingCallbackT] = None,
344
- generation_kwargs: Optional[Dict[str, Any]] = None,
345
- tools: Optional[Union[List[Tool], Toolset]] = None,
345
+ generation_kwargs: Optional[dict[str, Any]] = None,
346
+ tools: Optional[ToolsType] = None,
346
347
  requires_async: bool = False,
347
- ) -> Tuple[Dict[str, Any], Optional[StreamingCallbackT]]:
348
+ ) -> tuple[dict[str, Any], Optional[StreamingCallbackT]]:
348
349
  """
349
350
  Prepares and formats parameters required to call the Amazon Bedrock Converse API.
350
351
 
@@ -358,7 +359,8 @@ class AmazonBedrockChatGenerator:
358
359
  - `stopSequences`: List of stop sequences to stop generation.
359
360
  - `temperature`: Sampling temperature.
360
361
  - `topP`: Nucleus sampling parameter.
361
- :param tools: Optional list of Tool objects or a Toolset that the model can use.
362
+ :param tools: A list of Tool and/or Toolset objects, or a single Toolset for which the model can prepare calls.
363
+ Each tool should have a unique name.
362
364
  :param requires_async: Boolean flag to indicate if an async-compatible streaming callback function is needed.
363
365
 
364
366
  :returns:
@@ -380,14 +382,12 @@ class AmazonBedrockChatGenerator:
380
382
 
381
383
  # Handle tools - either toolConfig or Haystack Tool objects but not both
382
384
  tools = tools or self.tools
383
- _check_duplicate_tool_names(list(tools or []))
385
+ flattened_tools = flatten_tools_or_toolsets(tools)
386
+ _check_duplicate_tool_names(flattened_tools)
384
387
  tool_config = merged_kwargs.pop("toolConfig", None)
385
- if tools:
386
- # Convert Toolset to list if needed
387
- if isinstance(tools, Toolset):
388
- tools = list(tools)
388
+ if flattened_tools:
389
389
  # Format Haystack tools to Bedrock format
390
- tool_config = _format_tools(tools)
390
+ tool_config = _format_tools(flattened_tools)
391
391
 
392
392
  # Any remaining kwargs go to additionalModelRequestFields
393
393
  additional_fields = merged_kwargs if merged_kwargs else None
@@ -419,14 +419,14 @@ class AmazonBedrockChatGenerator:
419
419
 
420
420
  return params, callback
421
421
 
422
- @component.output_types(replies=List[ChatMessage])
422
+ @component.output_types(replies=list[ChatMessage])
423
423
  def run(
424
424
  self,
425
- messages: List[ChatMessage],
425
+ messages: list[ChatMessage],
426
426
  streaming_callback: Optional[StreamingCallbackT] = None,
427
- generation_kwargs: Optional[Dict[str, Any]] = None,
428
- tools: Optional[Union[List[Tool], Toolset]] = None,
429
- ) -> Dict[str, List[ChatMessage]]:
427
+ generation_kwargs: Optional[dict[str, Any]] = None,
428
+ tools: Optional[ToolsType] = None,
429
+ ) -> dict[str, list[ChatMessage]]:
430
430
  """
431
431
  Executes a synchronous inference call to the Amazon Bedrock model using the Converse API.
432
432
 
@@ -439,7 +439,8 @@ class AmazonBedrockChatGenerator:
439
439
  - `stopSequences`: List of stop sequences to stop generation.
440
440
  - `temperature`: Sampling temperature.
441
441
  - `topP`: Nucleus sampling parameter.
442
- :param tools: Optional list of Tools that the model may call during execution.
442
+ :param tools: A list of Tool and/or Toolset objects, or a single Toolset for which the model can prepare calls.
443
+ Each tool should have a unique name.
443
444
 
444
445
  :returns:
445
446
  A dictionary containing the model-generated replies under the `"replies"` key.
@@ -479,14 +480,14 @@ class AmazonBedrockChatGenerator:
479
480
 
480
481
  return {"replies": replies}
481
482
 
482
- @component.output_types(replies=List[ChatMessage])
483
+ @component.output_types(replies=list[ChatMessage])
483
484
  async def run_async(
484
485
  self,
485
- messages: List[ChatMessage],
486
+ messages: list[ChatMessage],
486
487
  streaming_callback: Optional[StreamingCallbackT] = None,
487
- generation_kwargs: Optional[Dict[str, Any]] = None,
488
- tools: Optional[Union[List[Tool], Toolset]] = None,
489
- ) -> Dict[str, List[ChatMessage]]:
488
+ generation_kwargs: Optional[dict[str, Any]] = None,
489
+ tools: Optional[ToolsType] = None,
490
+ ) -> dict[str, list[ChatMessage]]:
490
491
  """
491
492
  Executes an asynchronous inference call to the Amazon Bedrock model using the Converse API.
492
493
 
@@ -499,7 +500,8 @@ class AmazonBedrockChatGenerator:
499
500
  - `stopSequences`: List of stop sequences to stop generation.
500
501
  - `temperature`: Sampling temperature.
501
502
  - `topP`: Nucleus sampling parameter.
502
- :param tools: Optional list of Tool objects or a Toolset that the model can use.
503
+ :param tools: A list of Tool and/or Toolset objects, or a single Toolset for which the model can prepare calls.
504
+ Each tool should have a unique name.
503
505
 
504
506
  :returns:
505
507
  A dictionary containing the model-generated replies under the `"replies"` key.
@@ -1,7 +1,7 @@
1
1
  import base64
2
2
  import json
3
3
  from datetime import datetime, timezone
4
- from typing import Any, Dict, List, Optional, Tuple
4
+ from typing import Any, Optional
5
5
 
6
6
  from botocore.eventstream import EventStream
7
7
  from haystack import logging
@@ -29,7 +29,7 @@ logger = logging.getLogger(__name__)
29
29
  IMAGE_SUPPORTED_FORMATS = ["png", "jpeg", "gif", "webp"]
30
30
 
31
31
  # see https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_MessageStopEvent.html
32
- FINISH_REASON_MAPPING: Dict[str, FinishReason] = {
32
+ FINISH_REASON_MAPPING: dict[str, FinishReason] = {
33
33
  "end_turn": "stop",
34
34
  "stop_sequence": "stop",
35
35
  "max_tokens": "length",
@@ -40,7 +40,7 @@ FINISH_REASON_MAPPING: Dict[str, FinishReason] = {
40
40
 
41
41
 
42
42
  # Haystack to Bedrock util methods
43
- def _format_tools(tools: Optional[List[Tool]] = None) -> Optional[Dict[str, Any]]:
43
+ def _format_tools(tools: Optional[list[Tool]] = None) -> Optional[dict[str, Any]]:
44
44
  """
45
45
  Format Haystack Tool(s) to Amazon Bedrock toolConfig format.
46
46
 
@@ -60,7 +60,7 @@ def _format_tools(tools: Optional[List[Tool]] = None) -> Optional[Dict[str, Any]
60
60
  return {"tools": tool_specs} if tool_specs else None
61
61
 
62
62
 
63
- def _format_tool_call_message(tool_call_message: ChatMessage) -> Dict[str, Any]:
63
+ def _format_tool_call_message(tool_call_message: ChatMessage) -> dict[str, Any]:
64
64
  """
65
65
  Format a Haystack ChatMessage containing tool calls into Bedrock format.
66
66
 
@@ -68,7 +68,7 @@ def _format_tool_call_message(tool_call_message: ChatMessage) -> Dict[str, Any]:
68
68
  :returns:
69
69
  Dictionary representing the tool call message in Bedrock's expected format
70
70
  """
71
- content: List[Dict[str, Any]] = []
71
+ content: list[dict[str, Any]] = []
72
72
 
73
73
  # tool call messages can contain reasoning content
74
74
  if reasoning_content := tool_call_message.reasoning:
@@ -85,7 +85,7 @@ def _format_tool_call_message(tool_call_message: ChatMessage) -> Dict[str, Any]:
85
85
  return {"role": tool_call_message.role.value, "content": content}
86
86
 
87
87
 
88
- def _format_tool_result_message(tool_call_result_message: ChatMessage) -> Dict[str, Any]:
88
+ def _format_tool_result_message(tool_call_result_message: ChatMessage) -> dict[str, Any]:
89
89
  """
90
90
  Format a Haystack ChatMessage containing tool call results into Bedrock format.
91
91
 
@@ -114,7 +114,7 @@ def _format_tool_result_message(tool_call_result_message: ChatMessage) -> Dict[s
114
114
  return {"role": "user", "content": tool_results}
115
115
 
116
116
 
117
- def _repair_tool_result_messages(bedrock_formatted_messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
117
+ def _repair_tool_result_messages(bedrock_formatted_messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
118
118
  """
119
119
  Repair and reorganize tool result messages to maintain proper ordering and grouping.
120
120
 
@@ -135,7 +135,7 @@ def _repair_tool_result_messages(bedrock_formatted_messages: List[Dict[str, Any]
135
135
  tool_result_messages.append((idx, msg))
136
136
 
137
137
  # Determine the tool call IDs for each tool call message
138
- group_to_tool_call_ids: Dict[int, Any] = {idx: [] for idx, _ in tool_call_messages}
138
+ group_to_tool_call_ids: dict[int, Any] = {idx: [] for idx, _ in tool_call_messages}
139
139
  for idx, tool_call in tool_call_messages:
140
140
  tool_use_contents = [c for c in tool_call["content"] if "toolUse" in c]
141
141
  for content in tool_use_contents:
@@ -162,7 +162,7 @@ def _repair_tool_result_messages(bedrock_formatted_messages: List[Dict[str, Any]
162
162
  repaired_tool_result_prompts.append((original_idx, {"role": "user", "content": regrouped_tool_result}))
163
163
 
164
164
  # Remove the tool result messages from bedrock_formatted_messages
165
- bedrock_formatted_messages_minus_tool_results: List[Tuple[int, Any]] = []
165
+ bedrock_formatted_messages_minus_tool_results: list[tuple[int, Any]] = []
166
166
  for idx, msg in enumerate(bedrock_formatted_messages):
167
167
  # Assumes the content of tool result messages only contains 'toolResult': {...} objects (e.g. no 'text')
168
168
  if msg.get("content") and "toolResult" not in msg["content"][0]:
@@ -176,7 +176,7 @@ def _repair_tool_result_messages(bedrock_formatted_messages: List[Dict[str, Any]
176
176
  return [msg for _, msg in repaired_bedrock_formatted_messages]
177
177
 
178
178
 
179
- def _format_reasoning_content(reasoning_content: ReasoningContent) -> List[Dict[str, Any]]:
179
+ def _format_reasoning_content(reasoning_content: ReasoningContent) -> list[dict[str, Any]]:
180
180
  """
181
181
  Format ReasoningContent to match Bedrock's expected structure.
182
182
 
@@ -194,7 +194,7 @@ def _format_reasoning_content(reasoning_content: ReasoningContent) -> List[Dict[
194
194
  return formatted_contents
195
195
 
196
196
 
197
- def _format_text_image_message(message: ChatMessage) -> Dict[str, Any]:
197
+ def _format_text_image_message(message: ChatMessage) -> dict[str, Any]:
198
198
  """
199
199
  Format a Haystack ChatMessage containing text and optional image content into Bedrock format.
200
200
 
@@ -204,7 +204,7 @@ def _format_text_image_message(message: ChatMessage) -> Dict[str, Any]:
204
204
  """
205
205
  content_parts = message._content
206
206
 
207
- bedrock_content_blocks: List[Dict[str, Any]] = []
207
+ bedrock_content_blocks: list[dict[str, Any]] = []
208
208
  # Add reasoning content if available as the first content block
209
209
  if message.reasoning:
210
210
  bedrock_content_blocks.extend(_format_reasoning_content(reasoning_content=message.reasoning))
@@ -231,7 +231,7 @@ def _format_text_image_message(message: ChatMessage) -> Dict[str, Any]:
231
231
  return {"role": message.role.value, "content": bedrock_content_blocks}
232
232
 
233
233
 
234
- def _format_messages(messages: List[ChatMessage]) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
234
+ def _format_messages(messages: list[ChatMessage]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
235
235
  """
236
236
  Format a list of Haystack ChatMessages to the format expected by Bedrock API.
237
237
 
@@ -262,7 +262,7 @@ def _format_messages(messages: List[ChatMessage]) -> Tuple[List[Dict[str, Any]],
262
262
  return system_prompts, repaired_bedrock_formatted_messages
263
263
 
264
264
 
265
- def _parse_completion_response(response_body: Dict[str, Any], model: str) -> List[ChatMessage]:
265
+ def _parse_completion_response(response_body: dict[str, Any], model: str) -> list[ChatMessage]:
266
266
  """
267
267
  Parse a Bedrock API response into Haystack ChatMessage objects.
268
268
 
@@ -346,7 +346,7 @@ def _parse_completion_response(response_body: Dict[str, Any], model: str) -> Lis
346
346
 
347
347
 
348
348
  def _convert_event_to_streaming_chunk(
349
- event: Dict[str, Any], model: str, component_info: ComponentInfo
349
+ event: dict[str, Any], model: str, component_info: ComponentInfo
350
350
  ) -> StreamingChunk:
351
351
  """
352
352
  Convert a Bedrock streaming event to a Haystack StreamingChunk.
@@ -433,7 +433,7 @@ def _convert_event_to_streaming_chunk(
433
433
 
434
434
  elif "metadata" in event:
435
435
  event_meta = event["metadata"]
436
- chunk_meta: Dict[str, Any] = {**base_meta}
436
+ chunk_meta: dict[str, Any] = {**base_meta}
437
437
 
438
438
  if "usage" in event_meta:
439
439
  usage = event_meta["usage"]
@@ -454,7 +454,7 @@ def _convert_event_to_streaming_chunk(
454
454
  return streaming_chunk
455
455
 
456
456
 
457
- def _process_reasoning_contents(chunks: List[StreamingChunk]) -> Optional[ReasoningContent]:
457
+ def _process_reasoning_contents(chunks: list[StreamingChunk]) -> Optional[ReasoningContent]:
458
458
  """
459
459
  Process reasoning contents from a list of StreamingChunk objects into the Bedrock expected format.
460
460
 
@@ -536,7 +536,7 @@ def _parse_streaming_response(
536
536
  streaming_callback: SyncStreamingCallbackT,
537
537
  model: str,
538
538
  component_info: ComponentInfo,
539
- ) -> List[ChatMessage]:
539
+ ) -> list[ChatMessage]:
540
540
  """
541
541
  Parse a streaming response from Bedrock.
542
542
 
@@ -547,7 +547,7 @@ def _parse_streaming_response(
547
547
  :return: List of ChatMessage objects
548
548
  """
549
549
  content_block_idxs = set()
550
- chunks: List[StreamingChunk] = []
550
+ chunks: list[StreamingChunk] = []
551
551
  for event in response_stream:
552
552
  streaming_chunk = _convert_event_to_streaming_chunk(event=event, model=model, component_info=component_info)
553
553
  content_block_idx = streaming_chunk.index
@@ -581,7 +581,7 @@ async def _parse_streaming_response_async(
581
581
  streaming_callback: AsyncStreamingCallbackT,
582
582
  model: str,
583
583
  component_info: ComponentInfo,
584
- ) -> List[ChatMessage]:
584
+ ) -> list[ChatMessage]:
585
585
  """
586
586
  Parse a streaming response from Bedrock.
587
587
 
@@ -592,7 +592,7 @@ async def _parse_streaming_response_async(
592
592
  :return: List of ChatMessage objects
593
593
  """
594
594
  content_block_idxs = set()
595
- chunks: List[StreamingChunk] = []
595
+ chunks: list[StreamingChunk] = []
596
596
  async for event in response_stream:
597
597
  streaming_chunk = _convert_event_to_streaming_chunk(event=event, model=model, component_info=component_info)
598
598
  content_block_idx = streaming_chunk.index
@@ -613,7 +613,7 @@ async def _parse_streaming_response_async(
613
613
  return [reply]
614
614
 
615
615
 
616
- def _validate_guardrail_config(guardrail_config: Optional[Dict[str, str]] = None, streaming: bool = False) -> None:
616
+ def _validate_guardrail_config(guardrail_config: Optional[dict[str, str]] = None, streaming: bool = False) -> None:
617
617
  """
618
618
  Validate the guardrail configuration.
619
619
 
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  import re
3
3
  import warnings
4
- from typing import Any, Callable, ClassVar, Dict, List, Literal, Optional, Type, Union, get_args
4
+ from typing import Any, Callable, ClassVar, Literal, Optional, Union, get_args
5
5
 
6
6
  from botocore.config import Config
7
7
  from botocore.exceptions import ClientError
@@ -62,7 +62,7 @@ class AmazonBedrockGenerator:
62
62
  supports Amazon Bedrock.
63
63
  """
64
64
 
65
- SUPPORTED_MODEL_PATTERNS: ClassVar[Dict[str, Type[BedrockModelAdapter]]] = {
65
+ SUPPORTED_MODEL_PATTERNS: ClassVar[dict[str, type[BedrockModelAdapter]]] = {
66
66
  r"([a-z]{2}\.)?amazon.titan-text.*": AmazonTitanAdapter,
67
67
  r"([a-z]{2}\.)?ai21.j2.*": AI21LabsJurassic2Adapter,
68
68
  r"([a-z]{2}\.)?cohere.command-[^r].*": CohereCommandAdapter,
@@ -72,7 +72,7 @@ class AmazonBedrockGenerator:
72
72
  r"([a-z]{2}\.)?mistral.*": MistralAdapter,
73
73
  }
74
74
 
75
- SUPPORTED_MODEL_FAMILIES: ClassVar[Dict[str, Type[BedrockModelAdapter]]] = {
75
+ SUPPORTED_MODEL_FAMILIES: ClassVar[dict[str, type[BedrockModelAdapter]]] = {
76
76
  "amazon.titan-text": AmazonTitanAdapter,
77
77
  "ai21.j2": AI21LabsJurassic2Adapter,
78
78
  "cohere.command": CohereCommandAdapter,
@@ -105,7 +105,7 @@ class AmazonBedrockGenerator:
105
105
  max_length: Optional[int] = None,
106
106
  truncate: Optional[bool] = None,
107
107
  streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
108
- boto3_config: Optional[Dict[str, Any]] = None,
108
+ boto3_config: Optional[dict[str, Any]] = None,
109
109
  model_family: Optional[MODEL_FAMILIES] = None,
110
110
  **kwargs: Any,
111
111
  ) -> None:
@@ -183,13 +183,13 @@ class AmazonBedrockGenerator:
183
183
  model_adapter_cls = self.get_model_adapter(model=model, model_family=model_family)
184
184
  self.model_adapter = model_adapter_cls(model_kwargs=model_input_kwargs, max_length=self.max_length)
185
185
 
186
- @component.output_types(replies=List[str], meta=Dict[str, Any])
186
+ @component.output_types(replies=list[str], meta=dict[str, Any])
187
187
  def run(
188
188
  self,
189
189
  prompt: str,
190
190
  streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
191
- generation_kwargs: Optional[Dict[str, Any]] = None,
192
- ) -> Dict[str, Union[List[str], Dict[str, Any]]]:
191
+ generation_kwargs: Optional[dict[str, Any]] = None,
192
+ ) -> dict[str, Union[list[str], dict[str, Any]]]:
193
193
  """
194
194
  Generates a list of string response to the given prompt.
195
195
 
@@ -240,7 +240,7 @@ class AmazonBedrockGenerator:
240
240
  return {"replies": replies, "meta": metadata}
241
241
 
242
242
  @classmethod
243
- def get_model_adapter(cls, model: str, model_family: Optional[str] = None) -> Type[BedrockModelAdapter]:
243
+ def get_model_adapter(cls, model: str, model_family: Optional[str] = None) -> type[BedrockModelAdapter]:
244
244
  """
245
245
  Gets the model adapter for the given model.
246
246
 
@@ -273,7 +273,7 @@ class AmazonBedrockGenerator:
273
273
  )
274
274
  raise AmazonBedrockConfigurationError(msg)
275
275
 
276
- def to_dict(self) -> Dict[str, Any]:
276
+ def to_dict(self) -> dict[str, Any]:
277
277
  """
278
278
  Serializes the component to a dictionary.
279
279
 
@@ -297,7 +297,7 @@ class AmazonBedrockGenerator:
297
297
  )
298
298
 
299
299
  @classmethod
300
- def from_dict(cls, data: Dict[str, Any]) -> "AmazonBedrockGenerator":
300
+ def from_dict(cls, data: dict[str, Any]) -> "AmazonBedrockGenerator":
301
301
  """
302
302
  Deserializes the component from a dictionary.
303
303
 
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List, Optional
1
+ from typing import Any, Optional
2
2
 
3
3
  from botocore.exceptions import ClientError
4
4
  from haystack import Document, component, default_from_dict, default_to_dict, logging
@@ -68,7 +68,7 @@ class AmazonBedrockRanker:
68
68
  aws_region_name: Optional[Secret] = Secret.from_env_var(["AWS_DEFAULT_REGION"], strict=False), # noqa: B008
69
69
  aws_profile_name: Optional[Secret] = Secret.from_env_var(["AWS_PROFILE"], strict=False), # noqa: B008
70
70
  max_chunks_per_doc: Optional[int] = None,
71
- meta_fields_to_embed: Optional[List[str]] = None,
71
+ meta_fields_to_embed: Optional[list[str]] = None,
72
72
  meta_data_separator: str = "\n",
73
73
  ) -> None:
74
74
  if not model:
@@ -122,7 +122,7 @@ class AmazonBedrockRanker:
122
122
  )
123
123
  raise AmazonBedrockConfigurationError(msg) from exception
124
124
 
125
- def to_dict(self) -> Dict[str, Any]:
125
+ def to_dict(self) -> dict[str, Any]:
126
126
  """
127
127
  Serializes the component to a dictionary.
128
128
 
@@ -144,7 +144,7 @@ class AmazonBedrockRanker:
144
144
  )
145
145
 
146
146
  @classmethod
147
- def from_dict(cls, data: Dict[str, Any]) -> "AmazonBedrockRanker":
147
+ def from_dict(cls, data: dict[str, Any]) -> "AmazonBedrockRanker":
148
148
  """
149
149
  Deserializes the component from a dictionary.
150
150
 
@@ -159,7 +159,7 @@ class AmazonBedrockRanker:
159
159
  )
160
160
  return default_from_dict(cls, data)
161
161
 
162
- def _prepare_bedrock_input_docs(self, documents: List[Document]) -> List[str]:
162
+ def _prepare_bedrock_input_docs(self, documents: list[Document]) -> list[str]:
163
163
  """
164
164
  Prepare the input by concatenating the document text with the metadata fields specified.
165
165
  :param documents: The list of Document objects.
@@ -176,8 +176,8 @@ class AmazonBedrockRanker:
176
176
 
177
177
  return concatenated_input_list
178
178
 
179
- @component.output_types(documents=List[Document])
180
- def run(self, query: str, documents: List[Document], top_k: Optional[int] = None) -> Dict[str, List[Document]]:
179
+ @component.output_types(documents=list[Document])
180
+ def run(self, query: str, documents: list[Document], top_k: Optional[int] = None) -> dict[str, list[Document]]:
181
181
  """
182
182
  Use the Amazon Bedrock Reranker to re-rank the list of documents based on the query.
183
183