Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/branch_off.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
description: 'URL of the bump version PR'
value: ${{ jobs.branch-off.outputs.bump_version_pr_url }}
env:
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"

permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ on:
- ".github/workflows/e2e.yml"

env:
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
HATCH_VERSION: "1.16.5"

Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/license_compliance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
schedule:
- cron: "0 0 * * *" # every day at midnight
env:
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"

permissions:
contents: read
Expand Down Expand Up @@ -47,9 +47,7 @@ jobs:
fail: "Copyleft,Other,Error"
# Exclusions in the vanilla distribution must be explicitly motivated
# - tqdm is MLP but there are no better alternatives
# - typing_extensions>=4.13.0 has a Python Software Foundation License 2.0 but pip-license-checker does not recognize it
# (https://github.com/pilosus/pip-license-checker/issues/143)
exclude: "(?i)^(tqdm|typing_extensions).*"
exclude: "(?i)^(tqdm).*"

# We keep the license inventory on FOSSA
- name: Send license report to Fossa
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/promote_unstable_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
- "!v1.[0-9]+.[0-9]+"

env:
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"

permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
reno:
runs-on: ubuntu-slim
env:
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/slow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ name: Slow Integration Tests

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"
HATCH_VERSION: "1.16.5"
HAYSTACK_MPS_ENABLED: false
HAYSTACK_XPU_ENABLED: false
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
PYTHON_VERSION: "3.10"
PYTHON_VERSION: "3.11"
# numpy's stubs require a 3.12+ interpreter, so type checking runs above the floor.
# See [tool.mypy] in pyproject.toml.
MYPY_PYTHON_VERSION: "3.12"
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ hatch run test:integration
### Type checking with mypy
hatch run test:types

Type checking targets Python 3.12, above the 3.10 floor in `requires-python`, because numpy's stubs use PEP 695 `type` statements that mypy rejects below 3.12. So mypy will not catch code that breaks on 3.10; the unit test matrix runs on 3.10 and covers that.
Type checking targets Python 3.12, above the 3.11 floor in `requires-python`, because numpy's stubs use PEP 695 `type` statements that mypy rejects below 3.12. So mypy will not catch code that breaks on 3.11; the unit test matrix runs on 3.11 and covers that.

To fix type issues, avoid `type: ignore`, casts, or assertions when possible. If they are necessary, explain why.

Expand Down
8 changes: 5 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ This helps maintainers and keeps the project ready for both human and AI contrib

### Setting up your development environment

*To run Haystack tests locally, ensure your development environment uses Python >=3.10 and <3.14.*
*To run Haystack tests locally, ensure your development environment uses Python >=3.11 and <3.16.*
PyTorch does not publish Python 3.15 wheels on PyPI yet, so on 3.15 create the test environment with
`UV_EXTRA_INDEX_URL=https://download.pytorch.org/whl/cpu UV_INDEX_STRATEGY=unsafe-best-match hatch env create test`.

Haystack makes heavy use of [Hatch](https://hatch.pypa.io/latest/), a Python project manager that we use to set up the
virtual environments, build the project, and publish packages. As you can imagine, the first step towards becoming a
Expand Down Expand Up @@ -288,10 +290,10 @@ To check for static type errors, run:
hatch run test:types
```

Note that type checking targets Python 3.12, even though Haystack supports 3.10 and above. numpy's
Note that type checking targets Python 3.12, even though Haystack supports 3.11 and above. numpy's
type stubs use [PEP 695](https://peps.python.org/pep-0695/) `type` statements, which mypy rejects as
a syntax error below 3.12. Support for the older versions is covered by the unit test matrix, which
runs on Python 3.10.
runs on Python 3.11.

To format your code and perform linting using Ruff (with automatic fixes), run:
```sh
Expand Down
4 changes: 1 addition & 3 deletions haystack/components/converters/image/image_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
from collections import defaultdict
from io import BytesIO
from pathlib import Path
from typing import TypedDict, Union

from typing_extensions import NotRequired
from typing import NotRequired, TypedDict, Union

from haystack import logging
from haystack.dataclasses import ByteStream, Document
Expand Down
2 changes: 1 addition & 1 deletion haystack/components/converters/multi_file_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from haystack.dataclasses import ByteStream


class ConverterMimeType(str, Enum):
class ConverterMimeType(str, Enum): # noqa: UP042 # StrEnum would change str(), which __init__() parses
CSV = "text/csv"
DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
HTML = "text/html"
Expand Down
3 changes: 1 addition & 2 deletions haystack/components/routers/conditional_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
import ast
import contextlib
from collections.abc import Callable, Mapping, Sequence
from typing import Any, TypedDict, get_args, get_origin
from typing import Any, NotRequired, TypedDict, get_args, get_origin

from jinja2 import Environment, TemplateSyntaxError
from jinja2.nativetypes import NativeEnvironment
from typing_extensions import NotRequired

from haystack import component, default_from_dict, default_to_dict, logging
from haystack.core.errors import DeserializationError
Expand Down
3 changes: 1 addition & 2 deletions haystack/core/pipeline/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
from datetime import datetime
from enum import IntEnum
from pathlib import Path
from typing import Any, TextIO, TypeVar, Union, get_args
from typing import Any, Self, TextIO, TypeVar, Union, get_args

import networkx
from typing_extensions import Self

from haystack import logging, tracing
from haystack.core.component import Component, InputSocket, OutputSocket, component
Expand Down
5 changes: 2 additions & 3 deletions haystack/core/type_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ def _resolve_parameter_types(target: Callable, *, include_extras: bool = False)
# TypeError is raised for objects that cannot carry annotations, NameError for names that are not
# importable at runtime. Either way we fall back to the unresolved annotations.
hints = {}
# Non-string annotations are kept as they are written: on Python 3.10 `get_type_hints` widens the annotation
# of a parameter defaulting to `None` into an optional. This was changed in Python 3.11, see
# https://docs.python.org/3/whatsnew/3.11.html#typing.
# Non-string annotations are kept as they are written: with `include_extras=False`, `get_type_hints` strips
# `Annotated` metadata, which would drop markers such as `Variadic` from annotations that need no resolving.
return {
name: hints.get(name, param.annotation) if isinstance(param.annotation, str) else param.annotation
for name, param in parameters.items()
Expand Down
2 changes: 1 addition & 1 deletion haystack/dataclasses/chat_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def _parse_openai_tool_call_arguments(raw_arguments: Any) -> dict[str, Any]:
return json.loads(raw_arguments) if raw_arguments else {}


class ChatRole(str, Enum):
class ChatRole(str, Enum): # noqa: UP042 # StrEnum would change str() of this public enum
"""
Enumeration representing the roles within a chat.
"""
Expand Down
3 changes: 1 addition & 2 deletions haystack/utils/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ def _parse_iso_date(value: str) -> datetime | None:
try:
return datetime.fromisoformat(value)
except ValueError:
# Python 3.10's fromisoformat rejects valid ISO 8601 spellings that later versions accept,
# most notably a trailing "Z".
# fromisoformat still rejects some valid ISO 8601 spellings, such as "24:00" for end-of-day midnight.
try:
return dateutil.parser.isoparse(value)
except (ValueError, OverflowError):
Expand Down
9 changes: 4 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dynamic = ["version"]
description = "LLM framework to build customizable, production-ready LLM applications. Connect components (models, vector DBs, file converters) to pipelines or agents that can interact with your data."
readme = "README.md"
license = "Apache-2.0"
requires-python = ">=3.10"
requires-python = ">=3.11"
authors = [{ name = "deepset.ai", email = "malte.pietsch@deepset.ai" }]
keywords = [
"BERT",
Expand All @@ -34,11 +34,11 @@ classifiers = [
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
dependencies = [
Expand All @@ -53,7 +53,6 @@ dependencies = [
"pyyaml",
"more-itertools", # DocumentSplitter
"networkx", # Pipeline graphs
"typing_extensions>=4.7", # Extended typing features (NotRequired, etc.)
"httpx",
"anyio>=4.14.2", # transitive via httpx and openai; floor for CVE-2026-63374 (GHSA-82r6-8w77-94w6)
"numpy",
Expand Down Expand Up @@ -193,9 +192,9 @@ asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "class"

[tool.mypy]
# Deliberately above the 3.10 floor in `requires-python`: numpy's stubs use PEP 695 `type`
# Deliberately above the 3.11 floor in `requires-python`: numpy's stubs use PEP 695 `type`
# statements, which mypy rejects as a syntax error below 3.12. The floor stays covered by the
# unit test matrix, which runs on 3.10.
# unit test matrix, which runs on 3.11.
python_version = "3.12"
check_untyped_defs = true
disallow_incomplete_defs = true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
upgrade:
- |
Dropped support for Python 3.10 since it reaches End of Life (EOL) in October 2026.
The minimum supported Python version is now 3.11.
If you are on Python 3.10, upgrade your Python version before upgrading Haystack.
enhancements:
- |
Haystack now officially supports Python 3.15.
Loading
Loading