Skip to content

Commit 15057d2

Browse files
authored
Merge branch 'main' into gemini-embedding-2-updates
2 parents 26fc3ef + 78b139e commit 15057d2

12 files changed

Lines changed: 741 additions & 637 deletions

File tree

integrations/anthropic/CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## [integrations/anthropic-v5.8.0] - 2026-04-28
4+
5+
### 🚀 Features
6+
7+
- Add anthropic foundry support (#3238)
8+
9+
### 🧪 Testing
10+
11+
- Test compatible integrations with python 3.14; update pyproject (#3001)
12+
- Track test coverage for all integrations (#3065)
13+
- Add explicit integration test for structured output (#3226)
14+
15+
### 🧹 Chores
16+
17+
- Add missing -> None return type annotations to anthropic __init__ methods (#2972)
18+
- Enforce ruff docstring rules (D102/D103/D205/D209/D213/D417/D419) in first 10 integrations (#3008)
19+
20+
321
## [integrations/anthropic-v5.7.0] - 2026-03-13
422

523
### 🌀 Miscellaneous

integrations/anthropic/pydoc/config_docusaurus.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ loaders:
33
- haystack_integrations.components.generators.anthropic.generator
44
- haystack_integrations.components.generators.anthropic.chat.chat_generator
55
- haystack_integrations.components.generators.anthropic.chat.vertex_chat_generator
6+
- haystack_integrations.components.generators.anthropic.chat.foundry_chat_generator
67
search_path: [../src]
78
processors:
89
- type: filter

integrations/anthropic/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ classifiers = [
2323
"Programming Language :: Python :: Implementation :: CPython",
2424
"Programming Language :: Python :: Implementation :: PyPy",
2525
]
26-
dependencies = ["haystack-ai>=2.24.1", "anthropic>=0.47.0"]
26+
dependencies = ["haystack-ai>=2.24.1", "anthropic>=0.74.0"]
2727

2828
[project.urls]
2929
Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/anthropic#readme"

integrations/anthropic/src/haystack_integrations/components/generators/anthropic/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
#
33
# SPDX-License-Identifier: Apache-2.0
44
from .chat.chat_generator import AnthropicChatGenerator
5+
from .chat.foundry_chat_generator import AnthropicFoundryChatGenerator
56
from .chat.vertex_chat_generator import AnthropicVertexChatGenerator
67
from .generator import AnthropicGenerator
78

8-
__all__ = ["AnthropicChatGenerator", "AnthropicGenerator", "AnthropicVertexChatGenerator"]
9+
__all__ = [
10+
"AnthropicChatGenerator",
11+
"AnthropicFoundryChatGenerator",
12+
"AnthropicGenerator",
13+
"AnthropicVertexChatGenerator",
14+
]
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
# SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import os
6+
from collections.abc import Callable
7+
from typing import Any, ClassVar
8+
9+
from haystack import component, default_from_dict, default_to_dict, logging
10+
from haystack.dataclasses import ChatMessage, StreamingChunk
11+
from haystack.dataclasses.streaming_chunk import StreamingCallbackT
12+
from haystack.tools import (
13+
ToolsType,
14+
_check_duplicate_tool_names,
15+
deserialize_tools_or_toolset_inplace,
16+
flatten_tools_or_toolsets,
17+
serialize_tools_or_toolset,
18+
)
19+
from haystack.utils import (
20+
Secret,
21+
deserialize_callable,
22+
deserialize_secrets_inplace,
23+
serialize_callable,
24+
)
25+
26+
from anthropic import AnthropicFoundry, AsyncAnthropicFoundry
27+
28+
from .chat_generator import AnthropicChatGenerator
29+
30+
logger = logging.getLogger(__name__)
31+
32+
33+
@component
34+
class AnthropicFoundryChatGenerator(AnthropicChatGenerator):
35+
"""
36+
Enables text generation using Anthropic's Claude models via Azure Foundry.
37+
38+
A variety of Claude models (Opus, Sonnet, Haiku, and others) are available through Azure Foundry.
39+
40+
To use AnthropicFoundryChatGenerator, you must have an Azure subscription with Foundry enabled
41+
and the desired Anthropic model deployed in your Foundry resource.
42+
43+
For more details, refer to the [Anthropic Foundry documentation](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/lib/foundry.md).
44+
45+
Any valid text generation parameters for the Anthropic messaging API can be passed to
46+
the AnthropicFoundry API. Users can provide these parameters directly to the component via
47+
the `generation_kwargs` parameter in `__init__` or the `run` method.
48+
49+
For more details on the parameters supported by the Anthropic API, refer to the
50+
Anthropic Message API [documentation](https://docs.anthropic.com/en/api/messages).
51+
52+
```python
53+
from haystack_integrations.components.generators.anthropic import AnthropicFoundryChatGenerator
54+
from haystack.dataclasses import ChatMessage
55+
from haystack.utils import Secret
56+
57+
messages = [ChatMessage.from_user("What's Natural Language Processing?")]
58+
59+
client = AnthropicFoundryChatGenerator(
60+
model="claude-sonnet-4-5",
61+
api_key=Secret.from_env_var("ANTHROPIC_FOUNDRY_API_KEY"),
62+
resource="my-resource",
63+
)
64+
65+
response = client.run(messages)
66+
print(response)
67+
>> {'replies': [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[TextContent(text=
68+
>> "Natural Language Processing (NLP) is a field of artificial intelligence that
69+
>> focuses on enabling computers to understand, interpret, and generate human language. It involves developing
70+
>> techniques and algorithms to analyze and process text or speech data, allowing machines to comprehend and
71+
>> communicate in natural languages like English, Spanish, or Chinese.")],
72+
>> _name=None, _meta={'model': 'claude-sonnet-4-5', 'index': 0, 'finish_reason': 'end_turn',
73+
>> 'usage': {'input_tokens': 15, 'output_tokens': 64}})]}
74+
```
75+
76+
For more details on supported models and their capabilities, refer to the Anthropic
77+
[documentation](https://docs.anthropic.com/claude/docs/intro-to-claude).
78+
"""
79+
80+
SUPPORTED_MODELS: ClassVar[list[str]] = [
81+
"claude-opus-4-6",
82+
"claude-sonnet-4-6",
83+
"claude-sonnet-4-5",
84+
"claude-opus-4-5",
85+
"claude-opus-4-1",
86+
"claude-haiku-4-5",
87+
]
88+
"""A non-exhaustive list of chat models supported by this component.
89+
The actual availability depends on your Azure Foundry resource configuration."""
90+
91+
def __init__(
92+
self,
93+
*,
94+
api_key: Secret | None = Secret.from_env_var("ANTHROPIC_FOUNDRY_API_KEY", strict=True), # noqa: B008
95+
resource: str | None = None,
96+
endpoint: str | None = None,
97+
model: str = "claude-sonnet-4-5",
98+
streaming_callback: Callable[[StreamingChunk], None] | None = None,
99+
generation_kwargs: dict[str, Any] | None = None,
100+
ignore_tools_thinking_messages: bool = True,
101+
tools: ToolsType | None = None,
102+
timeout: float | None = None,
103+
max_retries: int | None = None,
104+
azure_ad_token_provider: Callable[[], str] | None = None,
105+
) -> None:
106+
"""
107+
Creates an instance of AnthropicFoundryChatGenerator.
108+
109+
:param api_key: The API key to use for authentication.
110+
Defaults to the `ANTHROPIC_FOUNDRY_API_KEY` environment variable.
111+
Can be `None` when using `azure_ad_token_provider` instead.
112+
:param resource: The Foundry resource name. Can also be set via the `ANTHROPIC_FOUNDRY_RESOURCE`
113+
environment variable. Either `resource` or `endpoint` must be provided.
114+
:param endpoint: The full Foundry endpoint URL (e.g.,
115+
"https://your-resource.openai.azure.com/anthropic").
116+
Either `resource` or `endpoint` must be provided.
117+
:param model: The name of the model to use (deployment name in Foundry).
118+
:param streaming_callback: A callback function that is called when a new token is received from the stream.
119+
The callback function accepts StreamingChunk as an argument.
120+
:param generation_kwargs: Other parameters to use for the model. These parameters are all sent directly to
121+
the AnthropicFoundry endpoint. See Anthropic [documentation](https://docs.anthropic.com/claude/reference/messages_post)
122+
for more details.
123+
Supported generation_kwargs parameters are:
124+
- `system`: The system message to be passed to the model.
125+
- `max_tokens`: The maximum number of tokens to generate.
126+
- `metadata`: A dictionary of metadata to be passed to the model.
127+
- `stop_sequences`: A list of strings that the model should stop generating at.
128+
- `temperature`: The temperature to use for sampling.
129+
- `top_p`: The top_p value to use for nucleus sampling.
130+
- `top_k`: The top_k value to use for top-k sampling.
131+
- `extra_headers`: A dictionary of extra headers to be passed to the model (i.e. for beta features).
132+
:param ignore_tools_thinking_messages: Anthropic's approach to tools (function calling) resolution involves a
133+
"chain of thought" messages before returning the actual function names and parameters in a message. If
134+
`ignore_tools_thinking_messages` is `True`, the generator will drop so-called thinking messages when tool
135+
use is detected. See the Anthropic [tools](https://docs.anthropic.com/en/docs/tool-use#chain-of-thought-tool-use)
136+
for more details.
137+
:param tools: A list of Tool and/or Toolset objects, or a single Toolset, that the model can use.
138+
Each tool should have a unique name.
139+
:param timeout:
140+
Timeout for Anthropic client calls. If not set, it defaults to the default set by the Anthropic client.
141+
:param max_retries:
142+
Maximum number of retries to attempt for failed requests. If not set, it defaults to the default set by
143+
the Anthropic client.
144+
:param azure_ad_token_provider: A function that returns an Azure AD token for authentication.
145+
Can be used instead of `api_key` for enhanced security.
146+
See [Azure Identity documentation](https://learn.microsoft.com/en-us/azure/developer/python/sdk/authentication/overview)
147+
for more details.
148+
"""
149+
if api_key is None and azure_ad_token_provider is None:
150+
msg = "Please provide an API key or an azure_ad_token_provider."
151+
raise ValueError(msg)
152+
153+
_check_duplicate_tool_names(flatten_tools_or_toolsets(tools))
154+
155+
self.api_key: Secret | None = api_key # type: ignore[assignment]
156+
self.resource = resource or os.environ.get("ANTHROPIC_FOUNDRY_RESOURCE")
157+
self.endpoint = endpoint
158+
self.model = model
159+
self.generation_kwargs = generation_kwargs or {}
160+
self.streaming_callback = streaming_callback
161+
self.ignore_tools_thinking_messages = ignore_tools_thinking_messages
162+
self.tools = tools
163+
self.timeout = timeout
164+
self.max_retries = max_retries
165+
self.azure_ad_token_provider = azure_ad_token_provider
166+
167+
if not self.resource and not self.endpoint:
168+
msg = (
169+
"Either 'resource' or 'endpoint' must be provided. "
170+
"Set ANTHROPIC_FOUNDRY_RESOURCE environment variable or pass resource/endpoint parameter."
171+
)
172+
raise ValueError(msg)
173+
174+
# Clients are created lazily in warm_up()
175+
self.client = None # type: ignore[assignment]
176+
self.async_client = None # type: ignore[assignment]
177+
self._is_warmed_up = False
178+
179+
def warm_up(self) -> None:
180+
"""
181+
Create the AnthropicFoundry clients.
182+
183+
This method is idempotent — it only creates clients once.
184+
"""
185+
if self._is_warmed_up:
186+
return
187+
188+
client_kwargs: dict[str, Any] = {}
189+
190+
if self.azure_ad_token_provider:
191+
client_kwargs["azure_ad_token_provider"] = self.azure_ad_token_provider
192+
else:
193+
client_kwargs["api_key"] = self.api_key.resolve_value() # type: ignore[union-attr]
194+
195+
if self.endpoint:
196+
client_kwargs["base_url"] = self.endpoint
197+
else:
198+
client_kwargs["resource"] = self.resource
199+
200+
if self.timeout is not None:
201+
client_kwargs["timeout"] = self.timeout
202+
203+
if self.max_retries is not None:
204+
client_kwargs["max_retries"] = self.max_retries
205+
206+
self.client = AnthropicFoundry(**client_kwargs) # type: ignore[assignment]
207+
self.async_client = AsyncAnthropicFoundry(**client_kwargs) # type: ignore[assignment]
208+
self._is_warmed_up = True
209+
210+
@component.output_types(replies=list[ChatMessage])
211+
def run(
212+
self,
213+
messages: list[ChatMessage],
214+
streaming_callback: StreamingCallbackT | None = None,
215+
generation_kwargs: dict[str, Any] | None = None,
216+
tools: ToolsType | None = None,
217+
) -> dict[str, list[ChatMessage]]:
218+
"""
219+
Invokes the AnthropicFoundry API with the given messages and generation kwargs.
220+
221+
:param messages: A list of ChatMessage instances representing the input messages.
222+
:param streaming_callback: A callback function that is called when a new token is received from the stream.
223+
:param generation_kwargs: Optional arguments to pass to the Anthropic generation endpoint.
224+
:param tools: A list of Tool and/or Toolset objects, or a single Toolset, that the model can use.
225+
Each tool should have a unique name. If set, it will override the `tools` parameter set during component
226+
initialization.
227+
:returns: A dictionary with the following keys:
228+
- `replies`: The responses from the model
229+
"""
230+
if not self._is_warmed_up:
231+
self.warm_up()
232+
return super(AnthropicFoundryChatGenerator, self).run( # noqa: UP008
233+
messages=messages, streaming_callback=streaming_callback, generation_kwargs=generation_kwargs, tools=tools
234+
)
235+
236+
@component.output_types(replies=list[ChatMessage])
237+
async def run_async(
238+
self,
239+
messages: list[ChatMessage],
240+
streaming_callback: StreamingCallbackT | None = None,
241+
generation_kwargs: dict[str, Any] | None = None,
242+
tools: ToolsType | None = None,
243+
) -> dict[str, list[ChatMessage]]:
244+
"""
245+
Async version of the run method. Invokes the AnthropicFoundry API with the given messages and generation kwargs.
246+
247+
:param messages: A list of ChatMessage instances representing the input messages.
248+
:param streaming_callback: A callback function that is called when a new token is received from the stream.
249+
:param generation_kwargs: Optional arguments to pass to the Anthropic generation endpoint.
250+
:param tools: A list of Tool and/or Toolset objects, or a single Toolset, that the model can use.
251+
Each tool should have a unique name. If set, it will override the `tools` parameter set during component
252+
initialization.
253+
:returns: A dictionary with the following keys:
254+
- `replies`: The responses from the model
255+
"""
256+
if not self._is_warmed_up:
257+
self.warm_up()
258+
return await super(AnthropicFoundryChatGenerator, self).run_async( # noqa: UP008
259+
messages=messages, streaming_callback=streaming_callback, generation_kwargs=generation_kwargs, tools=tools
260+
)
261+
262+
def to_dict(self) -> dict[str, Any]:
263+
"""
264+
Serialize this component to a dictionary.
265+
266+
:returns:
267+
The serialized component as a dictionary.
268+
"""
269+
callback_name = serialize_callable(self.streaming_callback) if self.streaming_callback else None
270+
azure_ad_token_provider_name = (
271+
serialize_callable(self.azure_ad_token_provider) if self.azure_ad_token_provider else None
272+
)
273+
return default_to_dict(
274+
self,
275+
api_key=self.api_key.to_dict() if self.api_key else None,
276+
resource=self.resource,
277+
endpoint=self.endpoint,
278+
model=self.model,
279+
streaming_callback=callback_name,
280+
generation_kwargs=self.generation_kwargs,
281+
ignore_tools_thinking_messages=self.ignore_tools_thinking_messages,
282+
tools=serialize_tools_or_toolset(self.tools),
283+
timeout=self.timeout,
284+
max_retries=self.max_retries,
285+
azure_ad_token_provider=azure_ad_token_provider_name,
286+
)
287+
288+
@classmethod
289+
def from_dict(cls, data: dict[str, Any]) -> "AnthropicFoundryChatGenerator":
290+
"""
291+
Deserialize this component from a dictionary.
292+
293+
:param data: The dictionary representation of this component.
294+
:returns:
295+
The deserialized component instance.
296+
"""
297+
deserialize_tools_or_toolset_inplace(data["init_parameters"], key="tools")
298+
deserialize_secrets_inplace(data["init_parameters"], keys=["api_key"])
299+
300+
init_params = data.get("init_parameters", {})
301+
if serialized_callback := init_params.get("streaming_callback"):
302+
data["init_parameters"]["streaming_callback"] = deserialize_callable(serialized_callback)
303+
if serialized_token_provider := init_params.get("azure_ad_token_provider"):
304+
data["init_parameters"]["azure_ad_token_provider"] = deserialize_callable(serialized_token_provider)
305+
306+
return default_from_dict(cls, data)

0 commit comments

Comments
 (0)