mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 13:52:34 +08:00
* feat(bundles): add lfx-bundles metapackage skeleton
Creates the manifest-less lfx-bundles distribution (the langchain-community
model) that the long-tail providers will move into. Empty skeleton for now;
the bulk move (scripts/migrate/consolidate_bundles.py) populates the provider
folders + per-provider extras later.
- src/bundles/lfx-bundles: a single pyproject declaring the lfx.bundles
entry-point (lfx_bundles = "lfx_bundles"), an lfx>=1.10.0,<2.0.0 pin, and a
generated (currently empty) `all` extra; plus a bare lfx_bundles namespace
package and a README documenting the model + install stories
- wired into the root workspace via the existing bundle marker blocks:
dep lfx-bundles[all]>=1.0,<2.0, uv source, and member
- hyphen dir name so release.yml's src/bundles/*/pyproject.toml glob builds
it with zero workflow change
Verified: the wheel builds (entry_points.txt carries the lfx.bundles group);
load_lfx_bundles_extensions (PR-1) discovers the entry point and the empty
skeleton registers zero providers with no error.
* feat(bundles): consolidate first long-tail tranche into lfx-bundles
Adds scripts/migrate/consolidate_bundles.py (the inverse of port_bundle.py --
moves in-tree providers into the manifest-less lfx-bundles metapackage) and
runs it on a verified 5-provider tranche: tavily, exa, wikipedia, yahoosearch,
wolframalpha.
Per provider the script:
- moves src/lfx/src/lfx/components/<p>/ -> lfx_bundles/<p>/ (lowercase names);
- leaves a fail-soft import shim (first line `# lfx-bundles-shim`) so
`from lfx.components.<p> import X` keeps working when lfx-bundles is
installed, and raises an actionable ImportError otherwise;
- merges the provider's third-party deps into a PEP 685-normalized lfx-bundles
extra and regenerates the `all` aggregate. Dep parity holds: `uv sync` is a
no-op because those deps were already pulled via langflow-base[complete];
- appends the 4-entry migration block per Component class (28 entries) so saved
flows referencing lfx.components.<p>.<Class> migrate to
ext:<p>:<Class>@official.
To avoid double registration, the in-tree component walk
(_load_components_dynamically) now skips shimmed provider dirs, and
component_index.json is regenerated (355->348 components, 95->90 modules); the
moved providers load only at @official via lfx.bundles discovery.
Verified: discovery finds all 5 at @official with no errors; shims resolve;
`import lfx.components` still works; the index drops the 7 moved component
entries (residual `tools`-category name refs resolve via the shim); ruff clean.
First tranche proves the engine; the remaining long-tail scales by extending
PROVIDER_DEPS (each provider's deps verified individually -- the careful part).
* feat(bundles): consolidate 30-provider tranche 2 into lfx-bundles
Extends PROVIDER_DEPS with 30 individually-verified providers and runs the
consolidation: vector stores (chroma, clickhouse, couchbase, milvus, mongodb,
pgvector, pinecone, qdrant, supabase, upstash, weaviate), model providers
(groq, mistral, ollama, perplexity, sambanova), and tools/memory/data (apify,
assemblyai, confluence, firecrawl, git, glean, icosacomputing, mem0, needle,
scrapegraph, serpapi, unstructured, youtube, zep).
Dep verification (the careful part): every spec comes from langflow-base's
per-provider extras or its direct dependencies; langchain-community providers
carry the wrapper plus the SDK the wrapper lazy-imports (e.g. pgvector,
atlassian-python-api); requests is declared explicitly where imported (it is
only transitive in today's env); pinecone keeps its python_version<'3.14'
marker verbatim. Tranche excludes: providers with langflow imports (vlmrun),
provider-specific lfx.base dirs (composio/huggingface/langwatch), case-
sensitive names (FAISS/Notion), the openai-SDK family (azure/aiml/deepseek/
litellm/lmstudio/novita/openrouter/vllm/xai/cometapi -- cleaner after PR-8),
and the partner set.
Dep parity verified at the resolution level: the uv.lock diff is +220 lines of
lfx-bundles extras metadata with ZERO packages added or removed (`name =` diff
empty), so pip install langflow resolves the identical set.
Also: 192 append-only migration entries (48 classes x 4, zero bare-name
ambiguities); component_index.json regenerated 348->300 components / 90->60
modules (exactly the moved set, no stale standalone entries); mongodb_atlas.py
SLF001 per-file-ignore and the mem0/mongodb detect-secrets baseline entries
migrated to the new paths (lint/secrets exceptions travel with moved files);
35 bundles now discover at @official with 55 components; shims verified across
categories; 449 extension tests pass.
* feat(bundles): consolidate openai-SDK family tranche 3 into lfx-bundles
10 providers that ride the langchain-openai wrapper, deferred from tranche 2
until the partner graduation settled the openai-SDK dep story: aiml, azure,
cometapi, deepseek, litellm, lmstudio, novita, openrouter, vllm, xai.
Dep verification: every provider declares langchain-openai>=1.1.6; the openai
SDK is declared only where a component imports it directly (aiml, deepseek,
litellm, lmstudio, vllm, xai -- wrapper-transitive elsewhere); requests
declared where imported (cometapi, deepseek, novita, xai); lmstudio's lazy
NVIDIAEmbeddings path gets langchain-nvidia-ai-endpoints~=1.0.0. The litellm
component drives LiteLLM-served endpoints through the OpenAI client and does
NOT import the litellm package -- langflow-base's litellm extra stays put.
These providers use the lazy _dynamic_imports __init__ shape; it survives the
move unchanged (import_mod resolves via __spec__.parent and
lfx.components._importing remains a core helper).
Dep parity: uv.lock diff has zero package additions/removals (all specs
already resolved via langflow-base[complete]).
Also: 56 append-only migration entries (14 classes x 4, zero ambiguities);
component_index.json regenerated 300->286 components / 60->50 modules (exactly
the moved set); detect-secrets baseline re-keyed; 45 bundles now discover at
@official with 69 components; shims verified (azure/xai/litellm/deepseek);
ruff clean.
* fix(ci): nightly bundle rename follows [extras] refs and self-refs
Two gaps in update_bundle_versions.py around extras suffixes, both fatal
or silently wrong for the first nightly carrying the lfx-bundles
metapackage:
- update_root_pyproject_for_bundle's dep regex required the version
specifier immediately after the name, so "lfx-bundles[all]>=1.0,<2.0"
(a MAIN dep) was left unrewritten while the workspace member was
renamed to lfx-bundles-nightly; uv lock then tries to resolve stable
lfx-bundles from PyPI, where it does not exist. The same gap silently
left "lfx-docling[local]>=0.1.0" optional-dep refs pointing at the
stable distribution. The regex now tolerates an [extras] group and
carries it into the replacement.
- rename_bundle_pyproject skipped self-referencing extras, so the
metapackage's generated `all` extra kept 45 "lfx-bundles[<provider>]"
members after the rename, pulling the stable distribution (same
lfx_bundles import package, install collision) once published. Self
refs now follow the rename, idempotently.
Tests drive the real script module, mirroring test_bundle_lfx_pin.py.
This closes the PR-2 audit item flagged when the metapackage was
introduced. The canonical-pre-release cutover would retire the nightly
rename entirely; until it lands, the rename must be correct.
* feat(bundles): graduate partner set to standalone lfx-<provider> packages (#13573)
* feat(bundles): graduate partner set to standalone lfx-<provider> packages
Extracts the five partner/flagship providers into manifest-shipping
distributions: lfx-openai, lfx-anthropic, lfx-amazon, lfx-datastax,
lfx-cohere. Each ships extension.json (lfx.compat ["1"]), a
langflow.extensions entry-point, an lfx>=1.10.0,<2.0.0 pin, and is wired into
the root workspace marker blocks. Zero flow impact by the bundle-name
invariant: ext:<provider>:<Class>@official ids are unchanged.
Mechanics:
- adopts the five-phase scripts/migrate/port_bundle.py from
feat/bundle-mass-extraction (handles shared-base moves, consumer rewrites,
surgical index updates, migration-table appends) and runs it per partner
with --migration-release 1.11.0
- datastax's shared lfx.base.datastax moves into lfx_datastax.base; its
backend unit tests move to src/bundles/datastax/tests (74 pass); its ruff
per-file-ignore and its check_component_env_writes ALLOWLIST entry travel;
10 repo consumers rewritten (incl. the vector_store_rag starter project)
- amazon_bedrock_converse.py legacy langflow.* imports rewritten to the lfx.*
equivalents (thin re-export aliases; behavior-identical) pre-extraction
- runtime deps pinned from langflow-base's extras / direct deps (wrapper-
guaranteed SDKs stay transitive, parity-exact); --remove-base-extra dropped
the openai/anthropic/cohere/aws extras from langflow-base[complete] in favor
of the bundle pins; the astradb extra stays (also used outside datastax)
- in-tree dirs replaced with marker shims pointing at lfx_<p>.components.<p>
(skipped by the in-tree walk; legacy from lfx.components.<p> imports keep
working); lfx/components/__init__.py entries kept, consistent with the
consolidated providers
- validate.py: accept classes whose base is a *derived* Component base
(LCVectorStoreComponent / LCToolComponent / ...) -- they inherit the
class-level outputs declaration and only override the output method, which
the AST-only check could not see; bare Component subclasses keep the strict
build/outputs requirement (+ regression test)
- BUNDLE_API.md changelog entries added for the branch's surface changes: the
lfx.bundles manifest-less discovery group + precedence tier +
bundle-discovery-malformed code (from the foundation PR) and the validator
acceptance above
- 100 append-only migration entries (20 classes x 5 partners x 4 shapes);
component_index.json surgically updated 300->280 components / 60->55
modules, sha256 recomputed and verified
- detect-secrets baseline re-keyed for moved partner files
Dep parity: lock diff adds only the five lfx-* package names; no third-party
package added or removed. One benign transitive re-resolution: anthropic SDK
0.105.2 -> 0.109.0 (allowed by langchain-anthropic's range on both sides).
Verified: all five register at @official via the installed-manifest tier
(extension_id/distribution = lfx-<p>, 20 components); manifest-less
lfx_bundles unchanged (35 bundles, disjoint); all 5 shims import; engine-safe;
`lfx extension validate` passes for all five; 450 extension unit tests, 60
pilot-upgrade migration tests, 74 moved datastax tests pass; ruff clean.
* fix(bundles): shim lfx.base.datastax so stored-flow imports keep resolving
The datastax graduation moved lfx.base.datastax into lfx_datastax.base,
but saved flows and starter templates (Hybrid Search RAG,
TemplateAssistant) embed `from lfx.base.datastax.astradb_base import
AstraDBBaseComponent` inside their stored component code fields, which
is re-executed verbatim at flow build time. Without a shim that import
raises ModuleNotFoundError and the flows fail to build
(test-starter-projects red).
Mirror the lfx.components shim contract: module-aliasing to
lfx_datastax.base, narrow except that only translates a missing bundle
(transitive dep failures re-raise untouched), marker-tagged single-file
dir, removed at M4 together with the components shims.
* chore(bundles): bounded version ranges for curated lfx-* packages (#13576)
langflow's pyproject is the release-coordination point now that lfx and the
lfx-* bundles release independently. Every curated lfx-* dependency declares a
BOUNDED range (>=A,<B), never an exact pin -- exact pins in reusable library
metadata create resolver conflicts for downstreams that depend on a different
version. Exact pins for reproducibility live in uv.lock and the release-build
manifests.
- the 4 pilots + 5 graduated partners: >=0.1.0 -> >=0.1.0,<1.0.0 (bundles
follow their own 0.1.x cadence)
- lfx-bundles[all]>=1.0,<2.0 unchanged (versions with the metapackage
contract)
- the three lfx-docling[...] optional-dependency refs bounded the same way
- policy documented inline in the bundle-deps marker block; the cross-bundle
CI matrix verifies the ranges resolve together across the supported lfx axis
Verified: uv lock resolves with zero package/version changes (bounds are
metadata-only today); the nightly rename's dep regex already matches the
bounded form.
* test(bundles): lock the in-tree shim contract + breakage audit (#13577)
* chore(bundles): bounded version ranges for curated lfx-* packages
langflow's pyproject is the release-coordination point now that lfx and the
lfx-* bundles release independently. Every curated lfx-* dependency declares a
BOUNDED range (>=A,<B), never an exact pin -- exact pins in reusable library
metadata create resolver conflicts for downstreams that depend on a different
version. Exact pins for reproducibility live in uv.lock and the release-build
manifests.
- the 4 pilots + 5 graduated partners: >=0.1.0 -> >=0.1.0,<1.0.0 (bundles
follow their own 0.1.x cadence)
- lfx-bundles[all]>=1.0,<2.0 unchanged (versions with the metapackage
contract)
- the three lfx-docling[...] optional-dependency refs bounded the same way
- policy documented inline in the bundle-deps marker block; the cross-bundle
CI matrix verifies the ranges resolve together across the supported lfx axis
Verified: uv lock resolves with zero package/version changes (bounds are
metadata-only today); the nightly rename's dep regex already matches the
bounded form.
* test(bundles): lock the in-tree shim contract + breakage audit
Locks the five-point contract for the 50 lfx-bundles import shims under
lfx/components/ (45 metapackage + 5 graduated partners):
1. first line is the `# lfx-bundles-shim` marker (keys the in-tree walk's
double-registration skip);
2. a shim dir is exactly one __init__.py -- no impls, no deps;
3. sys.modules module-aliasing (submodule trees resolve);
4. bundle missing -> actionable ModuleNotFoundError whose wording is part
of the contract ("pip install lfx-bundles" / "pip install lfx-<p>");
5. a missing *transitive* dep re-raises untouched.
Test shape (env-adaptive by design -- the lfx test env prunes the venv, so
nothing here assumes bundles are installed):
- source-level sweep of every real shim, parameterized (no imports);
- hermetic mechanism tests against a synthetic shim + synthetic target
(alias-resolves / locked-message / transitive-reraise);
- the walk-skip detector and the marker sweep must agree exactly;
- one adaptive live test on lfx.components.tavily exercising whichever
branch the running env is in.
106 tests pass in the pruned lfx env (where the live test exercises the
bare-engine locked-message branch).
Breakage audit (gh code search, recorded in the PR): ~20-25 public repos
reference lfx.components.*; the majority are langflow forks (vendored tree,
unaffected by packaging). Genuine library consumers exist and the moved
providers have real surface (openai: 11 external repos, datastax: 17) --
covered by the shims wherever bundles are co-installed (every
`pip install langflow`). Decision: deprecation warnings NOT warranted now;
revisit at M4 (shim removal), where one loud minor release before removal
is the right shape.
* test(bundles): extend the shim contract sweep to lfx.base shims
The datastax graduation moved lfx.base.datastax into lfx_datastax.base
and left a module-aliasing shim behind (stored flow code fields embed
the legacy import and are re-executed verbatim at build time). Sweep
lfx/base for marker shims with the same source-level contract as the
components sweep: one-file stub, module-aliasing to the bundle's base
subpackage, narrow name-checked except, locked install message.
* docs(bundles): install shapes, override rule, bundle_api_version (#13578)
Documents the deliberately small user-facing rule surface of the metapackage
split (1.11):
- extensions-overview.mdx: the two install stories (pip install langflow =
everything, same as today; pip install lfx = engine only, bring your own
bundles); the bundle-name-is-identity invariant; the current package table
(lfx-bundles metapackage + the 5 partner packages + the 4 pilots); the
legacy-import shim behavior with the locked ModuleNotFoundError wording and
the migration recipe for direct lfx users (switch to lfx[bundles] or pin
lfx-<provider> packages); the override rule ("ship a manifest -- a manifest
always wins", with the bundle-shadowed warning); bundle_api_version as the
single compat number (lfx.compat ["1"]; manifest-less packages ride their
PEP 508 lfx pin) and the no-version-arithmetic rule.
- deployment-lfx-compatibility.mdx: lfx[bundles] as the headless/serverless
deployment footnote (engine + lfx-bundles[all]; slimmer per-provider
alternative; intentionally no lfx[all]).
Both files verified MDX-safe (no unbackticked angle brackets).
* fix(bundles): stack-review fixes — symlink containment, idempotency, docs accuracy (#13579)
* chore(bundles): bounded version ranges for curated lfx-* packages
langflow's pyproject is the release-coordination point now that lfx and the
lfx-* bundles release independently. Every curated lfx-* dependency declares a
BOUNDED range (>=A,<B), never an exact pin -- exact pins in reusable library
metadata create resolver conflicts for downstreams that depend on a different
version. Exact pins for reproducibility live in uv.lock and the release-build
manifests.
- the 4 pilots + 5 graduated partners: >=0.1.0 -> >=0.1.0,<1.0.0 (bundles
follow their own 0.1.x cadence)
- lfx-bundles[all]>=1.0,<2.0 unchanged (versions with the metapackage
contract)
- the three lfx-docling[...] optional-dependency refs bounded the same way
- policy documented inline in the bundle-deps marker block; the cross-bundle
CI matrix verifies the ranges resolve together across the supported lfx axis
Verified: uv lock resolves with zero package/version changes (bounds are
metadata-only today); the nightly rename's dep regex already matches the
bounded form.
* test(bundles): lock the in-tree shim contract + breakage audit
Locks the five-point contract for the 50 lfx-bundles import shims under
lfx/components/ (45 metapackage + 5 graduated partners):
1. first line is the `# lfx-bundles-shim` marker (keys the in-tree walk's
double-registration skip);
2. a shim dir is exactly one __init__.py -- no impls, no deps;
3. sys.modules module-aliasing (submodule trees resolve);
4. bundle missing -> actionable ModuleNotFoundError whose wording is part
of the contract ("pip install lfx-bundles" / "pip install lfx-<p>");
5. a missing *transitive* dep re-raises untouched.
Test shape (env-adaptive by design -- the lfx test env prunes the venv, so
nothing here assumes bundles are installed):
- source-level sweep of every real shim, parameterized (no imports);
- hermetic mechanism tests against a synthetic shim + synthetic target
(alias-resolves / locked-message / transitive-reraise);
- the walk-skip detector and the marker sweep must agree exactly;
- one adaptive live test on lfx.components.tavily exercising whichever
branch the running env is in.
106 tests pass in the pruned lfx env (where the live test exercises the
bare-engine locked-message branch).
Breakage audit (gh code search, recorded in the PR): ~20-25 public repos
reference lfx.components.*; the majority are langflow forks (vendored tree,
unaffected by packaging). Genuine library consumers exist and the moved
providers have real surface (openai: 11 external repos, datastax: 17) --
covered by the shims wherever bundles are co-installed (every
`pip install langflow`). Decision: deprecation warnings NOT warranted now;
revisit at M4 (shim removal), where one loud minor release before removal
is the right shape.
* docs(bundles): install shapes, override rule, bundle_api_version
Documents the deliberately small user-facing rule surface of the metapackage
split (1.11):
- extensions-overview.mdx: the two install stories (pip install langflow =
everything, same as today; pip install lfx = engine only, bring your own
bundles); the bundle-name-is-identity invariant; the current package table
(lfx-bundles metapackage + the 5 partner packages + the 4 pilots); the
legacy-import shim behavior with the locked ModuleNotFoundError wording and
the migration recipe for direct lfx users (switch to lfx[bundles] or pin
lfx-<provider> packages); the override rule ("ship a manifest -- a manifest
always wins", with the bundle-shadowed warning); bundle_api_version as the
single compat number (lfx.compat ["1"]; manifest-less packages ride their
PEP 508 lfx pin) and the no-version-arithmetic rule.
- deployment-lfx-compatibility.mdx: lfx[bundles] as the headless/serverless
deployment footnote (engine + lfx-bundles[all]; slimmer per-provider
alternative; intentionally no lfx[all]).
Both files verified MDX-safe (no unbackticked angle brackets).
* fix(bundles): review fixes — symlink containment, idempotency, docs accuracy
Fixes from the multi-agent stack review (kept as a separate PR so the
reviewed PRs' diffs stay frozen for QA's independent pass):
- _bundles_root.py: directory-level symlink containment — a provider dir
that resolves outside the bundle root is skipped with a typed
bundle-discovery-malformed warning, mirroring the seed-directory walk's
is_within rule (+ regression test). Anchors the trust boundary to the
installed package tree.
- consolidate_bundles.py: migration-append idempotency guard — entries are
deduped on full content so a partially-failed earlier run cannot duplicate
rows on re-run (table stays append-only).
- langflow-base pyproject: documented WHY the aws extra is deliberately
retained after the lfx-amazon graduation (boto3 also backs the server's
own S3 storage backend and lfx's S3 ingestion — review suggested removing
it; that would regress S3 storage support).
- extensions-overview.mdx: note that `lfx extension list` shows
manifest-shipping extensions only; manifest-less packages load at startup
but are not listed.
93 loader+migration tests pass (incl. the new symlink test); ruff clean.
* refactor(bundles): stabilize import_mod as a public BUNDLE_API utility
The lazy-import helper that bundle packages call from their
__getattr__-based __init__.py files lived at lfx.components._importing -- an
internal path with no stability contract, imported by 35 separately-installed
bundle __init__ files (30 lfx-bundles providers + the 5 graduated partners).
- canonical home is now lfx.utils.lazy_import.import_mod (code unchanged);
lfx.components._importing re-exports it so in-tree callers and any external
code on the old path keep working
- all 35 bundle-side imports rewritten to the stable path
- BUNDLE_API.md: surface table entry + changelog (additive)
- contract tests: re-export identity, both call forms, the AttributeError
conversion
Verified: identity holds across both paths; lazy __init__ loads work through
the new path for both bundle families; 110 tests pass; ruff clean.
* fix(bundles): tombstone the broken legacy ZepChatMemory build method (#13580)
build_message_history targeted the zep-python v1 SDK (ZepClient +
zep_python.langchain.ZepChatMessageHistory); both were removed in
zep-python 2.x and the zep extra pins 2.0.2, so the method has been
unable to run for as long as the pin has existed -- its ImportError
guard misleadingly told users to 'pip install zep-python' (already
installed). The component is legacy=True with helpers.Memory as its
designated replacement, so rather than hand-write a new integration
against the 2.x SDK, the method now raises a clear RuntimeError
pointing at the Message History component.
Flow identity is preserved: class/component name, display_name,
description, inputs and the memory output are byte-identical, so saved
flows keep loading, i18n locale keys are unchanged, and
migration_table.json needs no edits. New bundle tests pin the stub
contract (identity, actionable error, no zep_python import).
* test(bundles): extend the shim contract sweep to lfx.base shims
The datastax graduation moved lfx.base.datastax into lfx_datastax.base
and left a module-aliasing shim behind (stored flow code fields embed
the legacy import and are re-executed verbatim at build time). Sweep
lfx/base for marker shims with the same source-level contract as the
components sweep: one-file stub, module-aliasing to the bundle's base
subpackage, narrow name-checked except, locked install message.
* test(bundles): extras-drift guard for the lfx-bundles metapackage
Risk-2 of the metapackage split: the generated `all` extra and the
per-provider extras must never drift by hand-edit, or `pip install
langflow` silently loses a provider's deps. Guard the four invariants:
extras <-> provider dirs (PEP 685-normalized), `all` == the exact
self-ref set, normalized keys collision-free, and the metapackage
provider set disjoint from the graduated partner distributions.
* fix(extension): symlink-escaped providers reject with path-escape, matching the documented contract
The lfx.bundles provider containment check (stack-review symlink fix)
emitted bundle-discovery-malformed, whose template and hint describe a
broken entry-point declaration. The changelog's path-safety entry
already documents symlink escapes as path-escape on every other
discovery path; use the same code here. Also resolves the semantic
merge conflict with the per-mode code split (the removed
_malformed_error location kwarg).
* fix(tests): repoint lfx tests off moved providers; complete provider fallback map
CI fail-fast had been masking these: the LFX test job runs in an
engine-only env where openai/anthropic/chroma are now bundle-package
shims, so every test that used them as the example category failed on
all Python versions (3.14 just reported first), and the backend Group 2
leg failed collecting test_lfx_bundles_extras.py on Python 3.10.
- flow_requirements: complete _PROVIDER_PACKAGE_FALLBACKS for the nine
moved model providers (OpenAI, Anthropic, Amazon Bedrock, Groq,
Google Generative AI, SambaNova, IBM watsonx.ai, Ollama + existing
Azure OpenAI). In engine-only installs MODEL_PROVIDERS_DICT registers
only in-tree providers, so the dynamic source-inspection path cannot
resolve moved providers; the static fallbacks keep lfx run/serve
requirements inference working. A dict hit still wins.
- test_dynamic_imports / test_import_utils: use composio (still-in-tree
lazy category with its SDK absent in the bare env) as the example
category instead of openai/anthropic/chroma; patch import_module at
its canonical home lfx.utils.lazy_import.
- flow-builder tests (build_flow_from_spec, flow_builder_tools,
propose_field_edit): specs use LanguageModelComponent (in-tree)
instead of OpenAIModel.
- test_lfx_bundles_extras: tomli fallback for Python 3.10 (tomllib is
stdlib 3.11+; pytest guarantees tomli on <3.11).
Full lfx unit suite: 4550 passed. Backend extras+pin tests: 24 passed.
* fix(tests): repoint MCP client-server tests off graduated OpenAIModel key
Same fail-fast-masked class as 14dfa7f268, backend side: since the
partner graduation, extension components register in /api/v1/all under
their namespaced ids (ext:openai:OpenAIModelComponent@official), so the
bare 'OpenAIModel' key these tests hardcoded no longer exists on any
Python version (the 'py3.14-only' Group 2 failure was the only leg that
ran; 3.10 was cancelled by fail-fast). The MCP feature itself is fine —
search returns the namespaced ids and add_component accepts them.
- Use LanguageModelComponent (in-tree, stable bare key; SecretStr
api_key, real_time_refresh fields, advanced 'stream', LanguageModel
output) for the redaction/configure/search/describe/spec tests.
- configure_dynamic_field: model_name -> api_key (the refresh field on
LanguageModelComponent).
- prompt-template-variables spec: drop the model node — server-side
validation builds the graph and a model without an API key fails its
build; the test's subject is the dynamic {var} fields.
Full file: 69 passed.
* Update test_mongodb_atlas.py
* Frontend tests
527 lines
18 KiB
TOML
527 lines
18 KiB
TOML
[project]
|
|
name = "langflow"
|
|
version = "1.11.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.11.0",
|
|
# langflow-extensions:bundle-deps-start
|
|
# Release coordination: langflow declares BOUNDED ranges (>=A,<B) for every
|
|
# curated lfx-* package, never exact pins -- exact pins in reusable library
|
|
# metadata create resolver conflicts for downstreams that depend on a
|
|
# different version. Exact pins for reproducibility live in uv.lock and the
|
|
# release-build manifests. Bundles follow their own 0.1.x cadence (bound
|
|
# <1.0.0); lfx-bundles versions with the metapackage contract (bound <2.0).
|
|
# The cross-bundle CI matrix verifies these ranges resolve together across
|
|
# the supported lfx axis.
|
|
"lfx-duckduckgo>=0.1.0,<1.0.0",
|
|
"lfx-arxiv>=0.1.0,<1.0.0",
|
|
"lfx-ibm>=0.1.0,<1.0.0",
|
|
"lfx-docling>=0.1.0,<1.0.0",
|
|
"lfx-bundles[all]>=1.0,<2.0",
|
|
"lfx-datastax>=0.1.0,<1.0.0",
|
|
"lfx-openai>=0.1.0,<1.0.0",
|
|
"lfx-anthropic>=0.1.0,<1.0.0",
|
|
"lfx-amazon>=0.1.0,<1.0.0",
|
|
"lfx-cohere>=0.1.0,<1.0.0",
|
|
# langflow-extensions:bundle-deps-end
|
|
]
|
|
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest-instafail>=0.5.0",
|
|
"ipykernel>=6.29.0",
|
|
"ruff~=0.13.1",
|
|
"httpx>=0.28.1",
|
|
"pytest>=9.0.3",
|
|
"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.43.4",
|
|
"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 }
|
|
# langflow-extensions:bundle-sources-start
|
|
lfx-duckduckgo = { workspace = true }
|
|
lfx-arxiv = { workspace = true }
|
|
lfx-ibm = { workspace = true }
|
|
lfx-docling = { workspace = true }
|
|
lfx-bundles = { workspace = true }
|
|
lfx-datastax = { workspace = true }
|
|
lfx-openai = { workspace = true }
|
|
lfx-anthropic = { workspace = true }
|
|
lfx-amazon = { workspace = true }
|
|
lfx-cohere = { workspace = true }
|
|
# langflow-extensions:bundle-sources-end
|
|
torch = { index = "pytorch-cpu" }
|
|
torchvision = { index = "pytorch-cpu" }
|
|
|
|
[tool.uv.workspace]
|
|
members = [
|
|
"src/backend/base",
|
|
".",
|
|
"src/lfx",
|
|
"src/langflow-stepflow",
|
|
"src/sdk",
|
|
# langflow-extensions:bundle-members-start
|
|
"src/bundles/duckduckgo",
|
|
"src/bundles/arxiv",
|
|
"src/bundles/ibm",
|
|
"src/bundles/docling",
|
|
"src/bundles/lfx-bundles",
|
|
"src/bundles/datastax",
|
|
"src/bundles/openai",
|
|
"src/bundles/anthropic",
|
|
"src/bundles/amazon",
|
|
"src/bundles/cohere",
|
|
# langflow-extensions:bundle-members-end
|
|
]
|
|
|
|
[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 = [
|
|
"lfx-docling[local]>=0.1.0,<1.0.0",
|
|
]
|
|
|
|
docling-chunking = [
|
|
"lfx-docling[chunking]>=0.1.0,<1.0.0",
|
|
]
|
|
|
|
docling-image-description = [
|
|
"lfx-docling[image-description]>=0.1.0,<1.0.0",
|
|
]
|
|
|
|
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
|
|
# Transitive dependency CVE fixes
|
|
"lxml>=6.1.0,<7.0.0", # CVE-2026-41066
|
|
"mako>=1.3.12,<2.0.0", # CVE-2026-44307
|
|
"urllib3>=2.7.0,<3.0.0", # CVE-2026-44431, CVE-2026-44432
|
|
"python-liquid>=2.2.0,<3.0.0", # CVE-2026-45017
|
|
]
|
|
|
|
[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", "src/bundles/*/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.Argument",
|
|
"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"]
|
|
"src/backend/tests/stress/*" = [
|
|
"D1", # Docstrings: this is a manual CLI tool, not a test target
|
|
"INP001", # Stress tests live outside the unit test package
|
|
"S101", # Asserts ok in stress harness
|
|
"S311", # Standard PRNG is fine for stress payload jitter
|
|
"T201", # Print statements: this is a CLI script
|
|
]
|
|
"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
|
|
]
|
|
"scripts/ci/test_*.py" = [
|
|
"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/cli/upgrade.py" = [
|
|
"TC003", # Path is used at runtime, not just in type hints
|
|
]
|
|
"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/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/bundles/lfx-bundles/src/lfx_bundles/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/bundles/*/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)
|
|
"INP001", # Bundle test dirs are not import packages
|
|
]
|
|
"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"
|