Files
langflow/pyproject.toml
Gabriel Luiz Freitas Almeida 1955f8fae5 chore: support Python 3.14 (#13085)
* feat: upgrade Docker images to Python 3.14 (experimental)

## Changes
- Update pyproject.toml: requires-python from <3.14 to <3.15
- Update all 5 Dockerfiles to use Python 3.14:
  - Builder stage: python3.12-trixie-slim → python3.14-trixie-slim
  - Runtime stage: python:3.12-slim-trixie → python:3.14-slim-trixie

## Files Updated
- src/backend/base/pyproject.toml
- docker/build_and_push_base.Dockerfile
- docker/build_and_push.Dockerfile
- docker/build_and_push_backend.Dockerfile
- docker/build_and_push_with_extras.Dockerfile
- docker/build_and_push_ep.Dockerfile

## Rationale
Python 3.14.5 was released on May 10, 2026. Upgrading to the latest
Python version should help reduce CVE vulnerabilities in Docker images.

## Testing Strategy
This is an experimental change to test Python 3.14 compatibility:
- Docker images will use Python 3.14
- CI/CD workflows still test on Python 3.10-3.13
- Nightly build will validate if dependencies work with 3.14
- Can be reverted quickly if issues are found

## Next Steps
- Monitor nightly build for failures
- If successful, update CI/CD workflows to add Python 3.14 to test matrix
- If failures occur, revert and investigate compatibility issues

* chore: support Python 3.14

Bump requires-python upper bound to <3.15 across langflow, langflow-base,
lfx, and langflow-sdk, and add 3.14 to CI test matrices so PRs are gated
on 3.14 compatibility.

Conditional pins for transitive deps without 3.14 wheels at the existing
caps:
- onnxruntime: >=1.26 on 3.14 (existing <1.24 cap retained for 3.10)
- faiss-cpu: >=1.13.2 on 3.14 (existing ==1.9.0.post1 retained for <3.14)

* chore: marker-gate IBM watsonx packages for Python 3.14

ibm-watsonx-ai 1.3.x cannot import on Python 3.14: its StrEnum subclasses
override __init__ in a way that conflicts with 3.14's reworked enum
__set_name__ path (TypeError on KnowledgeBaseFieldRole class creation).

Cap ibm-watsonx-ai, langchain-ibm, and the ibm-watsonx-orchestrate-*
extras at python_version<'3.14' until upstream adapts. Watsonx component
imports are already lazy, so this surfaces only at component-access time
on 3.14, where the existing ImportError handler in
lfx.components.ibm.__init__ degrades it gracefully.

test_model_utils.py imports ChatWatsonx at module top to verify
get_model_name resolves model_id; guard that import so the test
module skips on 3.14 instead of breaking collection.

* [autofix.ci] apply automated fixes

* chore: upgrade remaining Docker images to Python 3.14

build_and_push*.Dockerfile already moved to 3.14 in the merge from
feat/upgrade-python-3.14-docker-images. Apply the same bump to the
dev, devcontainer, and lfx Docker images so all in-repo Dockerfiles
share one Python version.

* [autofix.ci] apply automated fixes

* chore: gate 3.14-broken extras to python_version<'3.14'

cuga (and its transitive fastembed -> py-rust-stemmers source build),
altk/agent-lifecycle-toolkit (re-pulls ibm-watsonx-ai which was already
gated), langchain-pinecone, langwatch, ragstack-ai-knowledge-store, and
OpenDsStar all pin themselves below 3.14 upstream. Without a marker
guard, the workspace lock still tries to install them and a Docker
`uv sync` then attempts source builds that require Rust (CI Docker
test failure with py-rust-stemmers 0.1.5).

Add python_version<'3.14' (or <'3.13' for ragstack) to each pin so
those packages are simply omitted from the 3.14 install set until
upstream catches up. uv pip check is now clean on 3.14.2.

* test: skip test_altk_agent on Python 3.14

altk (agent-lifecycle-toolkit) is gated to python_version<'3.14'
upstream and now via the langflow-base [altk] extra marker. The test
imports altk at module top to exercise ALTKAgentComponent; guard the
import so collection skips on 3.14 instead of failing.

* test: skip remaining altk tests on Python 3.14

Three more test modules import lfx.base.agents.altk_* at module top,
which transitively imports altk. Add the same module-level skip guard
as test_altk_agent.py:
- test_altk_agent_logic.py
- test_altk_agent_tool_conversion.py
- test_conversation_context_ordering.py

* fix(calculator): replace removed ast.Num with ast.Constant for Python 3.14

ast.Num was deprecated in Python 3.8 in favor of ast.Constant and
removed entirely in 3.14. The calculator tool and its core walker
crashed on 3.14 with 'module ast has no attribute Num'.

Switch the isinstance checks to ast.Constant + isinstance(node.value,
(int, float)), and drop the now-dead ast.Num backwards-compat branch
in calculator_core.py (the ast.Constant branch already handles every
input it would have matched). Update the parser unit-test fixtures
to construct ast.Constant nodes.

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* test: skip LangWatch HTTP instrumentation tests on Python 3.14

langwatch is gated to python_version<'3.14' upstream. The
TestLangWatchHttpInstrumentation class patches langwatch.setup and
langwatch.trace inside a fixture, which requires langwatch to be
importable; on 3.14 the test errors with ModuleNotFoundError.

Guard the class with a skipif on langwatch availability.

* test: allow IBM and altk components to be missing on Python 3.14

test_all_modules_importable enforces that every component in __all__
imports cleanly. On 3.14 the ibm-watsonx-ai, langchain-ibm, and altk
packages are gated upstream and intentionally not installed, so the
WatsonxAI, WatsonxEmbeddings, and ALTKAgent components fail to import
as designed.

- test_all_components_in_categories_importable: accept a known-gated
  deny-list on 3.14 instead of failing.
- test_all_lfx_component_modules_directly_importable: extend the
  existing 'missing optional dependency' allowlist with altk,
  langchain_ibm, and ibm_watsonx_ai.

* fix(lfx): pass ensure_exists to user_cache_dir for Python 3.14

Python 3.14 tightened PurePath.__init__ to reject unknown keyword
arguments. The KeyedWorkerLockManager constructor was passing
ensure_exists=True to Path() instead of user_cache_dir(), which
silently worked on 3.10-3.13 but raises TypeError on 3.14 and also
meant the cache directory was never actually being created.

Move the kwarg to user_cache_dir() where it belongs and update the
two unit tests that asserted the buggy call shape.

* [autofix.ci] apply automated fixes

* test: accept either ZIP or JSON error path on Python 3.14

Python 3.14's zipfile.is_zipfile() now validates the central-directory
signature in addition to EOCD, so the garbage+EOCD payload no longer
passes the is_zipfile() dispatch in the /flows/upload/ route. The
endpoint still returns 400 with a descriptive detail (now from the JSON
branch); only the message wording differs, which the test was asserting
verbatim.

* fix(lfx): normalize cors_origins ['*'] back to '*' on Python 3.14

Pydantic-settings on Python 3.14 parses the env var '*' into ['*']
before the cors_origins field validator runs (the list[str] | str union
resolves differently than on 3.10-3.13). Collapse that back to the
bare-string wildcard so downstream consumers — including
warn_about_future_cors_changes and the test suite — see the same shape
on every supported Python version.

---------

Co-authored-by: vijay kumar katuri <vijay.katuri@ibm.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-13 22:23:39 +00:00

452 lines
15 KiB
TOML

[project]
name = "langflow"
version = "1.10.0"
description = "A Python package with a built-in web application"
requires-python = ">=3.10,<3.15"
license = "MIT"
keywords = ["nlp", "langchain", "openai", "gpt", "gui"]
readme = "README.md"
maintainers = [
{ name = "Carlos Coelho", email = "carlos@langflow.org" },
{ name = "Cristhian Zanforlin", email = "cristhian.lousa@gmail.com" },
{ name = "Gabriel Almeida", email = "gabriel@langflow.org" },
{ name = "Lucas Eduoli", email = "lucaseduoli@gmail.com" },
{ name = "Otávio Anovazzi", email = "otavio2204@gmail.com" },
{ name = "Rodrigo Nader", email = "rodrigo@langflow.org" },
{ name = "Italo dos Anjos", email = "italojohnnydosanjos@gmail.com" },
]
# Define your main dependencies here
dependencies = [
"langflow-base[complete]>=0.10.0",
]
[dependency-groups]
dev = [
"pytest-instafail>=0.5.0",
"ipykernel>=6.29.0",
"ruff~=0.13.1",
"httpx>=0.28.1",
"pytest>=8.2.0",
"requests>=2.33.0",
"pytest-cov>=5.0.0",
"pytest-mock>=3.14.0",
"pytest-xdist>=3.6.0",
"pytest-sugar>=1.0.0",
"respx>=0.21.1",
"pytest-asyncio>=0.23.0",
"pytest-profiling>=1.7.0",
"pre-commit>=3.7.0",
"vulture>=2.11",
"dictdiffer>=0.9.0",
"pytest-split>=0.9.0",
"pytest-flakefinder>=1.1.0",
"packaging>=24.1,<25.0",
"asgi-lifespan>=2.1.0",
"pytest-github-actions-annotate-failures>=0.2.0",
"blockbuster>=1.5.20,<1.6",
"types-aiofiles>=24.1.0.20240626",
"codeflash>=0.8.4",
"hypothesis>=6.123.17",
"locust~=2.40.5",
"pytest-rerunfailures>=15.0",
"scrapegraph-py>=1.10.2",
'elevenlabs==1.58.1; python_version == "3.12"',
'elevenlabs>=1.52.0; python_version != "3.12"',
"faker>=37.0.0",
"pytest-timeout>=2.3.1",
"pyyaml>=6.0.2",
"pyleak>=0.1.14",
"mcp-server-fetch>=2025.1.17",
"onnxruntime>=1.20,<1.24; python_version<'3.14'", # >=1.24 does not support Python 3.10; <1.24 allows 1.23.x for agent-lifecycle-toolkit
"onnxruntime>=1.26; python_version>='3.14'",
"fakeredis>=2.0.0",
]
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
[tool.uv.sources]
langflow-base = { workspace = true }
langflow = { workspace = true }
lfx = { workspace = true }
langflow-sdk = { workspace = true }
torch = { index = "pytorch-cpu" }
torchvision = { index = "pytorch-cpu" }
[tool.uv.workspace]
members = [
"src/backend/base",
".",
"src/lfx",
"src/sdk",
]
[tool.hatch.build.targets.wheel]
packages = ["src/backend/langflow"]
[project.urls]
Repository = "https://github.com/langflow-ai/langflow"
Documentation = "https://docs.langflow.org"
[project.optional-dependencies]
docling = [
"langchain-docling>=1.1.0",
"tesserocr>=2.8.0",
"rapidocr-onnxruntime>=1.4.4",
"ocrmac>=1.0.0; sys_platform == 'darwin'",
# CPU-only PyTorch required for docling features
# Loose version ranges intentionally used to avoid conflicts with transitive dependencies (e.g., altk)
# Updated to 2.6.0+ to address CVE: RCE vulnerability in torch.load() with weights_only=True
"torch>=2.6.0",
"torchvision>=0.21.0", # torchvision 0.21+ is compatible with PyTorch 2.6+
]
audio = [
"webrtcvad>=2.0.10",
]
couchbase = [
"couchbase>=4.2.1"
]
cassio = [
"cassio>=0.1.7"
]
local = [
"sentence-transformers>=2.3.1",
"ctransformers>=0.2.10"
]
clickhouse-connect = [
"clickhouse-connect==0.7.19"
]
nv-ingest = [
"nv-ingest-api>=26.1.0,<27.0.0 ; python_version >= '3.12'",
"nv-ingest-client>=26.1.0,<27.0.0 ; python_version >= '3.12'",
]
postgresql = [
"sqlalchemy[postgresql_psycopg2binary]>=2.0.38,<3.0.0",
"sqlalchemy[postgresql_psycopg]>=2.0.38,<3.0.0",
]
[tool.uv]
override-dependencies = [
# temporary force a newer python-pptx
"python-pptx>=1.0.2",
# z3-solver 4.15.7 dropped Linux wheels, pin until codeflash is removed
"z3-solver<4.15.7",
# Security: Force upgraded versions to prevent transitive deps from pulling older vulnerable versions
"orjson>=3.11.6",
"gunicorn>=25.3.0",
"pypdf>=6.10.0", # Force pypdf 6.10+ to prevent vulnerable 6.6.x versions
"nltk>=3.9.4",
"Markdown>=3.8.0",
"dynaconf>=3.2.13",
"pillow>=12.1.1", # Force Pillow 12.1.1+ to prevent CVE-vulnerable versions
"playwright>=1.59.0", # Latest available on PyPI; ensures updated Chromium with CVE fixes
]
[project.scripts]
langflow = "langflow.langflow_launcher:main"
[tool.codespell]
skip = '.git,*.pdf,*.svg,*.pdf,*.yaml,*.ipynb,poetry.lock,*.min.js,*.css,package-lock.json,*.trig.,**/node_modules/**,./stuff/*,*.csv'
# Ignore latin etc
ignore-regex = '.*(Stati Uniti|Tense=Pres).*'
[tool.pytest.ini_options]
timeout = 150
timeout_method = "signal"
minversion = "6.0"
testpaths = ["src/backend/tests", "src/lfx/tests"]
console_output_style = "progress"
filterwarnings = [
"ignore::DeprecationWarning",
"ignore::ResourceWarning",
"ignore:Skipped unsupported reflection:sqlalchemy.exc.SAWarning",
"ignore:.*SQL-parsed foreign key constraint:sqlalchemy.exc.SAWarning",
"ignore:autogenerate skipping metadata-specified expression-based index:UserWarning",
]
log_cli = true
log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)"
log_cli_date_format = "%Y-%m-%d %H:%M:%S"
markers = [
"async_test",
"api_key_required",
"no_blockbuster",
"benchmark",
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow-running tests",
"security: Security regression tests (IDOR, auth, access control)"
]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
addopts = "-p no:benchmark"
[tool.coverage.run]
command_line = """
-m pytest --ignore=tests/integration
--cov --cov-report=term --cov-report=html
--instafail -ra -n auto -m "not api_key_required"
"""
source = ["src/backend/base/langflow/"]
omit = ["*/alembic/*", "tests/*", "*/__init__.py"]
[tool.coverage.report]
sort = "Stmts"
skip_empty = true
show_missing = false
ignore_errors = true
[tool.coverage.html]
directory = "coverage"
[tool.ruff]
target-version = "py310"
exclude = [
"src/backend/base/langflow/alembic/*",
"src/frontend/tests/assets/*",
"src/lfx/src/lfx/_assets/component_index.json",
"docs/**/API-Reference/python-examples/**",
]
line-length = 120
[tool.ruff.lint]
flake8-annotations.mypy-init-return = true
flake8-bugbear.extend-immutable-calls = [
"fastapi.Depends",
"fastapi.File",
"fastapi.Query",
"typer.Option",
]
flake8-type-checking.runtime-evaluated-base-classes = [
"pydantic.BaseModel",
"typing.TypedDict", # Needed by fastapi
"typing_extensions.TypedDict", # Needed by fastapi
]
pydocstyle.convention = "google"
select = ["ALL"]
ignore = [
"C90", # McCabe complexity
"CPY", # Missing copyright
"COM812", # Messes with the formatter
"ERA", # Eradicate commented-out code
"FIX002", # Line contains TODO
"ISC001", # Messes with the formatter
"PERF203", # Rarely useful
"PLR09", # Too many something (arg, statements, etc)
"RUF012", # Pydantic models are currently not well detected. See https://github.com/astral-sh/ruff/issues/13630
"TD002", # Missing author in TODO
"TD003", # Missing issue link in TODO
"TRY301", # A bit too harsh (Abstract `raise` to an inner function)
"PLC0415", # Inline imports
"D10", # Missing docstrings
"PLW1641", # Object does not implement `__hash__` method (mutable objects shouldn't be hashable)
# Rules that are TODOs
"ANN"
]
# Preview rules that are not yet activated
external = ["RUF027"]
[tool.ruff.lint.per-file-ignores]
"scripts/*" = ["D1", "INP", "T201"]
"scripts/gp/tests/*" = [
"D1", # Missing docstrings
"PLR2004", # Magic value comparisons
"S101", # Use of assert (required for pytest)
"TRY003", # Long exception messages
"EM101", # String literals in exceptions
]
"src/backend/base/langflow/alembic/versions/*" = ["INP001", "D415", "PGH003"]
"src/backend/base/langflow/custom/__init__.py" = [
"I001", # Import order affects initialization - must import custom module first
]
"src/backend/base/langflow/api/v1/*" = [
"TCH", # FastAPI needs to evaluate types at runtime
]
"src/backend/base/langflow/api/v2/*" = [
"TCH", # FastAPI needs to evaluate types at runtime
]
"src/backend/base/langflow/__main__.py" = [
"B008", # Typer CLI requires function calls in defaults
]
"src/backend/base/langflow/services/cache/*" = [
"S301", # Pickle usage is intentional for caching
]
"src/backend/base/langflow/services/tracing/*" = [
"SLF001", # Third-party library private member access (langwatch, opik)
]
"src/lfx/src/lfx/base/curl/parse.py" = [
"S105", # False positive: 'token' variable name, not a password
]
"src/lfx/src/lfx/services/auth/service.py" = [
"ARG002", # No-op impl: unused args required by interface
"EM101", # NotImplementedError messages as literals
"TC003", # Type-only imports used in signatures
]
"src/lfx/src/lfx/base/mcp/util.py" = [
"SLF001", # MCP library private member access
]
"src/lfx/src/lfx/cli/common.py" = [
"S104", # Intentional binding to all interfaces for server
"S105", # False positive: GITHUB_TOKEN_ENV is an env var name
]
"src/lfx/src/lfx/components/__init__.py" = [
"SLF001", # Accessing _dynamic_imports from modules
]
"src/lfx/src/lfx/components/data/save_file.py" = [
"SLF001", # Google API client private member access
]
"src/lfx/src/lfx/components/datastax/astradb_vectorstore.py" = [
"S110", # Try-except-pass for optional metadata
]
"src/lfx/src/lfx/components/google/google_generative_ai_embeddings.py" = [
"SLF001", # Google AI library private member access
]
"src/lfx/src/lfx/components/knowledge_bases/retrieval.py" = [
"SLF001", # Chroma client private member access
]
"src/lfx/src/lfx/components/mongodb/mongodb_atlas.py" = [
"SLF001", # MongoDB collection private member access
]
"src/lfx/src/lfx/components/tools/{python_code_structured_tool.py,searxng.py}" = [
"S102", # Use of exec/eval for dynamic code execution
"SLF001", # Component internal member access
]
"src/lfx/src/lfx/components/vectorstores/astradb.py" = [
"S110", # Try-except-pass for optional metadata
]
"src/lfx/src/lfx/custom/{code_parser/code_parser.py,validate.py}" = [
"S102", # Use of exec for code validation
]
"src/lfx/src/lfx/custom/custom_component/component.py" = [
"SLF001", # Component internal state management
]
"src/lfx/src/lfx/custom/directory_reader/directory_reader.py" = [
"SLF001", # Component introspection
]
"src/lfx/src/lfx/custom/utils.py" = [
"SLF001", # Component code analysis
]
"src/lfx/src/lfx/graph/graph/ascii.py" = [
"SLF001", # Grandalf library private member access
]
"src/lfx/src/lfx/inputs/input_mixin.py" = [
"S105", # False positive: PASSWORD is a type constant
]
"src/lfx/src/lfx/__main__.py" = [
"B008", # Typer CLI requires function calls in argument defaults
"FBT001", # Bool flags are the standard typer pattern
"FBT003", # Boolean positional values in typer.Option() calls
]
"src/lfx/src/lfx/cli/_setup_commands.py" = [
"B008", # Typer CLI requires function calls in argument defaults
"FBT001", # Bool flags are the standard typer pattern
"FBT003", # Boolean positional values in typer.Option() calls
]
"src/lfx/src/lfx/cli/_authoring_commands.py" = [
"B008", # Typer CLI requires function calls in argument defaults
"FBT001", # Bool flags are the standard typer pattern
"FBT003", # Boolean positional values in typer.Option() calls
]
"src/lfx/src/lfx/cli/_running_commands.py" = [
"B008", # Typer CLI requires function calls in argument defaults
"FBT001", # Bool flags are the standard typer pattern
"FBT003", # Boolean positional values in typer.Option() calls
]
"src/lfx/src/lfx/cli/_remote_commands.py" = [
"B008", # Typer CLI requires function calls in argument defaults
"FBT001", # Bool flags are the standard typer pattern
"FBT003", # Boolean positional values in typer.Option() calls
]
"src/backend/base/langflow/api/utils/*" = [
"TCH", # Imports are used at runtime (FastAPI deps, SQLAlchemy queries, model constructors)
]
"src/sdk/src/langflow_sdk/_http.py" = [
"TCH", # httpx is used at runtime (response object methods)
]
"src/sdk/src/langflow_sdk/environments.py" = [
"TRY003", # Contextual error messages are necessary here
"EM102", # f-string messages assigned before raise where possible
]
"src/sdk/tests/*" = [
"S101", # assert is the standard pytest assertion style
"INP001", # Not a package namespace issue in tests
"TC003", # pytest fixture type hints are evaluated at runtime
]
"src/lfx/src/lfx/schema/table.py" = [
"S105", # False positive: PASSWORD is a formatter type
]
"src/lfx/src/lfx/services/mcp_composer/service.py" = [
"S104", # Intentional binding to all interfaces for server
"S110", # Try-except-pass for optional error logging
]
"src/backend/tests/*" = [
"D1",
"PLR2004",
"S101",
"SLF001",
"BLE001", # allow broad-exception catching in tests
]
"src/backend/base/langflow/tests/*" = [
"D1",
"PLR2004",
"S101",
"SLF001",
"BLE001", # allow broad-exception catching in tests
]
"src/lfx/tests/*" = [
"D1",
"PLR2004",
"S101",
"SLF001",
"BLE001", # allow broad-exception catching in tests
"S104", # Binding to all interfaces (test servers)
"S108", # Insecure temp file usage (safe in tests)
]
"src/backend/tests/locust/*" = [
"D1", # Missing docstrings (CLI tools don't need full docstrings)
"T201", # Print statements (needed for CLI output)
"S603", # Subprocess calls (needed for running commands)
"S607", # Starting process with partial executable path
"S104", # Binding to all interfaces (needed for Langflow server)
"S105", # Hardcoded passwords (test credentials)
"FBT001", # Boolean-typed positional arguments (common in CLI)
"FBT002", # Boolean default arguments (common in CLI tools)
"TRY002", # Custom exceptions (generic exceptions OK for CLI)
"TRY003", # Long exception messages (descriptive errors for users)
"TRY300", # Consider moving to else block (return patterns)
"EM101", # String literals in exceptions (user-facing messages)
"EM102", # F-string literals in exceptions (user-facing messages)
"EXE001", # Shebang without executable (may be run via python)
"D415", # First line punctuation (CLI docstrings)
"F401", # Unused imports (imports for availability checking)
"PTH123", # Use Path.open (open() is fine for simple cases)
"PTH107", # Use Path.unlink (os.remove is fine for simple cases)
"PTH207", # Use Path.glob (glob.glob is fine for simple cases)
"B007", # Unused loop variables (tuple unpacking)
"PLW0602", # Global variable usage (needed for locust state)
"PLW0603", # Global variable updates (needed for locust state)
"DTZ005", # Datetime without timezone (local time is fine for logs)
"G004", # Logging f-strings (detailed error logging)
"SIM102", # Nested if statements (readability over optimization)
"E501", # Line too long (some CLI commands are naturally long)
]
[tool.ruff.lint.flake8-builtins]
builtins-allowed-modules = [ "io", "logging", "socket"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"