Skip to content

Commit d6a5424

Browse files
authored
chore!: anthropic - drop Python 3.9 and use X|Y typing (#2688)
1 parent 5b8b2ce commit d6a5424

5 files changed

Lines changed: 55 additions & 63 deletions

File tree

integrations/anthropic/pyproject.toml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,22 @@ name = "anthropic-haystack"
77
dynamic = ["version"]
88
description = 'An integration of Anthropic Claude models into the Haystack framework.'
99
readme = "README.md"
10-
requires-python = ">=3.9"
10+
requires-python = ">=3.10"
1111
license = "Apache-2.0"
1212
keywords = []
1313
authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }]
1414
classifiers = [
1515
"License :: OSI Approved :: Apache Software License",
1616
"Development Status :: 4 - Beta",
1717
"Programming Language :: Python",
18-
"Programming Language :: Python :: 3.9",
1918
"Programming Language :: Python :: 3.10",
2019
"Programming Language :: Python :: 3.11",
2120
"Programming Language :: Python :: 3.12",
2221
"Programming Language :: Python :: 3.13",
2322
"Programming Language :: Python :: Implementation :: CPython",
2423
"Programming Language :: Python :: Implementation :: PyPy",
2524
]
26-
dependencies = ["haystack-ai>=2.19.0", "anthropic>=0.47.0"]
25+
dependencies = ["haystack-ai>=2.22.0", "anthropic>=0.47.0"]
2726

2827
[project.urls]
2928
Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/anthropic#readme"
@@ -77,7 +76,6 @@ disallow_incomplete_defs = true
7776

7877

7978
[tool.ruff]
80-
target-version = "py39"
8179
line-length = 120
8280

8381
[tool.ruff.lint]
@@ -126,10 +124,6 @@ ignore = [
126124
"ARG002",
127125
"ARG005",
128126
]
129-
unfixable = [
130-
# Don't touch unused imports
131-
"F401",
132-
]
133127

134128
[tool.ruff.lint.isort]
135129
known-first-party = ["haystack_integrations"]

integrations/anthropic/src/haystack_integrations/components/generators/anthropic/chat/chat_generator.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime, timezone
2-
from typing import Any, ClassVar, Optional, Union
2+
from typing import Any, ClassVar
33

44
from haystack import component, default_from_dict, default_to_dict, logging
55
from haystack.components.generators.utils import _convert_streaming_chunks_to_chat_message
@@ -113,13 +113,13 @@ def __init__(
113113
self,
114114
api_key: Secret = Secret.from_env_var("ANTHROPIC_API_KEY"), # noqa: B008
115115
model: str = "claude-sonnet-4-5",
116-
streaming_callback: Optional[StreamingCallbackT] = None,
117-
generation_kwargs: Optional[dict[str, Any]] = None,
116+
streaming_callback: StreamingCallbackT | None = None,
117+
generation_kwargs: dict[str, Any] | None = None,
118118
ignore_tools_thinking_messages: bool = True,
119-
tools: Optional[ToolsType] = None,
119+
tools: ToolsType | None = None,
120120
*,
121-
timeout: Optional[float] = None,
122-
max_retries: Optional[int] = None,
121+
timeout: float | None = None,
122+
max_retries: int | None = None,
123123
):
124124
"""
125125
Creates an instance of AnthropicChatGenerator.
@@ -229,8 +229,8 @@ def from_dict(cls, data: dict[str, Any]) -> "AnthropicChatGenerator":
229229
def _prepare_request_params(
230230
self,
231231
messages: list[ChatMessage],
232-
generation_kwargs: Optional[dict[str, Any]] = None,
233-
tools: Optional[ToolsType] = None,
232+
generation_kwargs: dict[str, Any] | None = None,
233+
tools: ToolsType | None = None,
234234
) -> tuple[list[TextBlockParam], list[MessageParam], dict[str, Any], list[ToolParam]]:
235235
"""
236236
Prepare the parameters for the Anthropic API request.
@@ -277,8 +277,8 @@ def _prepare_request_params(
277277

278278
def _process_response(
279279
self,
280-
response: Union[Message, Stream[RawMessageStreamEvent]],
281-
streaming_callback: Optional[SyncStreamingCallbackT] = None,
280+
response: Message | Stream[RawMessageStreamEvent],
281+
streaming_callback: SyncStreamingCallbackT | None = None,
282282
) -> dict[str, list[ChatMessage]]:
283283
"""
284284
Process the response from the Anthropic API.
@@ -291,7 +291,7 @@ def _process_response(
291291
# we cannot use isinstance(Stream)
292292
if not isinstance(response, Message):
293293
chunks: list[StreamingChunk] = []
294-
model: Optional[str] = None
294+
model: str | None = None
295295
tool_call_index = -1
296296
input_tokens = None
297297
component_info = ComponentInfo.from_component(self)
@@ -345,7 +345,7 @@ def _process_response(
345345
async def _process_response_async(
346346
self,
347347
response: Any,
348-
streaming_callback: Optional[AsyncStreamingCallbackT] = None,
348+
streaming_callback: AsyncStreamingCallbackT | None = None,
349349
) -> dict[str, list[ChatMessage]]:
350350
"""
351351
Process the response from the Anthropic API asynchronously.
@@ -359,7 +359,7 @@ async def _process_response_async(
359359
# workaround for https://github.com/DataDog/dd-trace-py/issues/12562
360360
if not isinstance(response, Message):
361361
chunks: list[StreamingChunk] = []
362-
model: Optional[str] = None
362+
model: str | None = None
363363
tool_call_index = -1
364364
input_tokens = None
365365
component_info = ComponentInfo.from_component(self)
@@ -418,9 +418,9 @@ async def _process_response_async(
418418
def run(
419419
self,
420420
messages: list[ChatMessage],
421-
streaming_callback: Optional[StreamingCallbackT] = None,
422-
generation_kwargs: Optional[dict[str, Any]] = None,
423-
tools: Optional[ToolsType] = None,
421+
streaming_callback: StreamingCallbackT | None = None,
422+
generation_kwargs: dict[str, Any] | None = None,
423+
tools: ToolsType | None = None,
424424
) -> dict[str, list[ChatMessage]]:
425425
"""
426426
Invokes the Anthropic API with the given messages and generation kwargs.
@@ -460,9 +460,9 @@ def run(
460460
async def run_async(
461461
self,
462462
messages: list[ChatMessage],
463-
streaming_callback: Optional[StreamingCallbackT] = None,
464-
generation_kwargs: Optional[dict[str, Any]] = None,
465-
tools: Optional[ToolsType] = None,
463+
streaming_callback: StreamingCallbackT | None = None,
464+
generation_kwargs: dict[str, Any] | None = None,
465+
tools: ToolsType | None = None,
466466
) -> dict[str, list[ChatMessage]]:
467467
"""
468468
Async version of the run method. Invokes the Anthropic API with the given messages and generation kwargs.

integrations/anthropic/src/haystack_integrations/components/generators/anthropic/chat/utils.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Literal, Optional, Union, cast, get_args
1+
from typing import Any, Literal, cast, get_args
22

33
from haystack.dataclasses.chat_message import (
44
ChatMessage,
@@ -46,14 +46,12 @@
4646
def _update_anthropic_message_with_tool_call_results(
4747
tool_call_results: list[ToolCallResult],
4848
content: list[
49-
Union[
50-
TextBlockParam,
51-
ToolUseBlockParam,
52-
ToolResultBlockParam,
53-
ImageBlockParam,
54-
ThinkingBlockParam,
55-
RedactedThinkingBlockParam,
56-
]
49+
TextBlockParam
50+
| ToolUseBlockParam
51+
| ToolResultBlockParam
52+
| ImageBlockParam
53+
| ThinkingBlockParam
54+
| RedactedThinkingBlockParam
5755
],
5856
) -> None:
5957
"""
@@ -130,14 +128,12 @@ def _convert_messages_to_anthropic_format(
130128
continue
131129

132130
content: list[
133-
Union[
134-
TextBlockParam,
135-
ToolUseBlockParam,
136-
ToolResultBlockParam,
137-
ImageBlockParam,
138-
ThinkingBlockParam,
139-
RedactedThinkingBlockParam,
140-
]
131+
TextBlockParam
132+
| ToolUseBlockParam
133+
| ToolResultBlockParam
134+
| ImageBlockParam
135+
| ThinkingBlockParam
136+
| RedactedThinkingBlockParam
141137
] = []
142138

143139
# Handle multimodal content (text and images) preserving order
@@ -222,7 +218,7 @@ def _convert_messages_to_anthropic_format(
222218

223219
# Anthropic only supports assistant and user roles in messages. User role is also used for tool messages.
224220
# System messages are passed separately.
225-
role: Union[Literal["assistant"], Literal["user"]] = "user"
221+
role: Literal["assistant"] | Literal["user"] = "user"
226222
if message._role == ChatRole.ASSISTANT:
227223
role = "assistant"
228224

@@ -358,7 +354,7 @@ def _convert_anthropic_chunk_to_streaming_chunk(
358354
)
359355

360356

361-
def _process_reasoning_contents(chunks: list[StreamingChunk]) -> Optional[ReasoningContent]:
357+
def _process_reasoning_contents(chunks: list[StreamingChunk]) -> ReasoningContent | None:
362358
"""
363359
Process reasoning contents from a list of StreamingChunk objects into the Anthropic expected format.
364360

integrations/anthropic/src/haystack_integrations/components/generators/anthropic/chat/vertex_chat_generator.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
2-
from typing import Any, Callable, Optional
2+
from collections.abc import Callable
3+
from typing import Any
34

45
from haystack import component, default_from_dict, default_to_dict, logging
56
from haystack.dataclasses import StreamingChunk
@@ -70,13 +71,13 @@ def __init__(
7071
region: str,
7172
project_id: str,
7273
model: str = "claude-sonnet-4@20250514",
73-
streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
74-
generation_kwargs: Optional[dict[str, Any]] = None,
74+
streaming_callback: Callable[[StreamingChunk], None] | None = None,
75+
generation_kwargs: dict[str, Any] | None = None,
7576
ignore_tools_thinking_messages: bool = True,
76-
tools: Optional[ToolsType] = None,
77+
tools: ToolsType | None = None,
7778
*,
78-
timeout: Optional[float] = None,
79-
max_retries: Optional[int] = None,
79+
timeout: float | None = None,
80+
max_retries: int | None = None,
8081
):
8182
"""
8283
Creates an instance of AnthropicVertexChatGenerator.

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Any, Callable, ClassVar, Optional, Union
1+
from collections.abc import Callable
2+
from typing import Any, ClassVar
23

34
from haystack import component, default_from_dict, default_to_dict, logging
45
from haystack.dataclasses import StreamingChunk
@@ -61,12 +62,12 @@ def __init__(
6162
self,
6263
api_key: Secret = Secret.from_env_var("ANTHROPIC_API_KEY"), # noqa: B008
6364
model: str = "claude-sonnet-4-20250514",
64-
streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
65-
system_prompt: Optional[str] = None,
66-
generation_kwargs: Optional[dict[str, Any]] = None,
65+
streaming_callback: Callable[[StreamingChunk], None] | None = None,
66+
system_prompt: str | None = None,
67+
generation_kwargs: dict[str, Any] | None = None,
6768
*,
68-
timeout: Optional[float] = None,
69-
max_retries: Optional[int] = None,
69+
timeout: float | None = None,
70+
max_retries: int | None = None,
7071
):
7172
"""
7273
Initialize the AnthropicGenerator.
@@ -147,9 +148,9 @@ def from_dict(cls, data: dict[str, Any]) -> "AnthropicGenerator":
147148
def run(
148149
self,
149150
prompt: str,
150-
generation_kwargs: Optional[dict[str, Any]] = None,
151-
streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
152-
) -> dict[str, Union[list[str], list[dict[str, Any]]]]:
151+
generation_kwargs: dict[str, Any] | None = None,
152+
streaming_callback: Callable[[StreamingChunk], None] | None = None,
153+
) -> dict[str, list[str] | list[dict[str, Any]]]:
153154
"""
154155
Generate replies using the Anthropic API.
155156
@@ -174,7 +175,7 @@ def run(
174175

175176
streaming_callback = streaming_callback or self.streaming_callback
176177
stream = streaming_callback is not None
177-
response: Union[Message, Stream[MessageStreamEvent]] = self.client.messages.create(
178+
response: Message | Stream[MessageStreamEvent] = self.client.messages.create(
178179
max_tokens=filtered_generation_kwargs.pop("max_tokens", 512),
179180
system=self.system_prompt if self.system_prompt else filtered_generation_kwargs.pop("system", ""),
180181
model=self.model,
@@ -244,7 +245,7 @@ def run(
244245
if self.include_thinking and len(thinkings) == len(completions):
245246
completions = [
246247
f"{self.thinking_tag_start}{thinking}{self.thinking_tag_end}{completion}"
247-
for thinking, completion in zip(thinkings, completions)
248+
for thinking, completion in zip(thinkings, completions, strict=True)
248249
]
249250

250251
meta.update(

0 commit comments

Comments
 (0)