Commit Graph

633 Commits

Author SHA1 Message Date
5a8c17b22d ci: backport bundle/nightly CI fixes from main (#13206, #13207, #13208) (#13210)
* ci: stop publishing -nightly bundle variants; release bundles on demand (#13206)

Bundles (lfx-arxiv, lfx-duckduckgo) change infrequently enough that a
nightly cadence is overkill. Drop the rename-to-`-nightly` and bundle
publish lanes from the nightly pipeline and add a dedicated
workflow_dispatch-only release_bundles.yml for purposeful releases.

- nightly_build.yml: drop update_bundle_versions.py invocation and the
  src/bundles/*/pyproject.toml git-add guard.
- release_nightly.yml: remove build-nightly-bundles and
  publish-nightly-bundles jobs; untether test-cross-platform and
  publish-nightly-main from them.
- release_bundles.yml (new): build all src/bundles/* wheels, run a
  cross-OS install smoke test, then publish to PyPI under their stable
  names. Tolerates already-published versions so re-runs without a
  version bump are no-ops.

release.yml still owns bundle publishing as part of main releases.

Manual follow-up (PyPI admin): delete the lfx-arxiv-nightly and
lfx-duckduckgo-nightly projects from PyPI.

* ci(release_bundles): build lfx wheel locally for smoke test (#13207)

The release_bundles.yml smoke test installs bundle wheels into a fresh
venv, which makes uv resolve their `lfx>=X.Y,<Z` pin against PyPI. When
bundles are released ahead of a matching lfx (typical case — bundles
ship more often than lfx bumps land on PyPI), the resolve fails:

  Because only lfx<=0.4.3 is available and lfx-arxiv==0.1.0 depends on
  lfx>=0.5.0,<0.6.0, we can conclude that lfx-arxiv==0.1.0 cannot be
  used.

Build the lfx wheel from the current ref in the build-bundles job and
install it alongside the bundle wheels in the smoke test. The lfx
wheel ships as a separate `dist-lfx-smoketest` artifact and is NOT
included in the publish-bundles step — only the `dist-bundles`
artifact gets pushed to PyPI.

* ci(nightly): re-pin bundle lfx deps to lfx-nightly during nightly build (#13208)

The nightly pipeline renames the workspace `lfx` package to `lfx-nightly`
in `src/lfx/pyproject.toml` and the root workspace dep, but leaves each
`src/bundles/*/pyproject.toml`'s `"lfx>=X.Y,<Z"` pin unchanged. With the
workspace `lfx` gone and PyPI still on lfx 0.4.3, the create-nightly-tag
job's `uv lock` fails:

  × No solution found when resolving dependencies for split [...]
  ╰─▶ Because only lfx<=0.4.3 is available and lfx-arxiv depends on
      lfx>=0.5.0,<0.6.0, we can conclude that lfx-arxiv's requirements
      are unsatisfiable.

`update_lfx_version.py` now also rewrites the `lfx` (or already-rewritten
`lfx-nightly`) specifier in every `src/bundles/*/pyproject.toml` to
`lfx-nightly==<dev version>`, mirroring how `update_lf_base_dependency`
re-pins the lfx dep inside `langflow-base`. The lookahead in the regex
prevents matching sibling packages like `lfx-arxiv` / `lfx-duckduckgo`.

No-op when no bundle pyprojects exist (e.g. on main today), so this is
safe to merge ahead of the bundle extraction landing on main. Restored
the guarded `git add src/bundles/*/pyproject.toml` step so the modified
bundle pyprojects ride the same commit/tag as the rest of the nightly
version bumps.

Cherry-pick to release-1.10.0 to unblock nightlies cut from that branch.
2026-05-19 10:12:54 -07:00
c901602bc2 fix(ci): rename bundles to -nightly during nightly build (#13193)
fix: rename bundles for nightly build
2026-05-18 18:32:44 -07:00
cc009c9133 feat(lfx): Bundle Separation and LFX Extension Framework (#13043)
* feat(lfx): installed-package + seed-directory discovery for production install (LE-1022)

Adds the read-only production install path for Modes A, B, and C of the
Bundle Separation iteration. Manifest-shipping pip-installed
distributions and seed-directory subdirectories are discovered at server
startup and registered as Extensions at @official.

* discovery.py: walks importlib.metadata.distributions() + the
  $LANGFLOW_SEED_DIR / /opt/langflow/bundles seed root; produces
  DiscoveredExtension records and typed errors for malformed manifests
  / configured-but-missing seed dirs.
* registry.py: ExtensionRegistry service with the immutability
  invariant for installed and seed entries. Mutation verbs (uninstall,
  disable, enable, install, update_entry) all raise
  ExtensionImmutableError carrying the typed
  installed-extension-immutable / seed-directory-immutable code so the
  invariant is testable today; the CLI uninstall surface ships in B4.
* lfx extension list: read-only inspector with text and JSON output
  for operators inspecting Mode B/C images.
* Errors: four new typed codes
  (installed-extension-immutable, seed-directory-immutable,
  seed-directory-not-found, duplicate-extension-id) plus snapshot
  coverage in tests/unit/extension/test_errors.py.
* Tests: 165 extension tests pass, including the LE-1022 acceptance
  cases -- three pip-installed wheels visible at @official, three seed
  bundles visible at @official, and the parametrized service-layer
  immutability check across every mutation verb.
* Docs: docs/Deployment/deployment-extensions-production.mdx covers
  the Dockerfile template, k8s deployment notes, the bundle packaging
  convention (extension.json shipped via package-data), and
  troubleshooting for the typed error codes.

* feat(lfx): add single-Bundle loader and LANGFLOW_COMPONENTS_PATH discovery (#12967)

* feat(lfx): add extension manifest schema, validate CLI, and error formatter (LE-1014)

Foundation for the Bundle Separation iteration. Defines what a valid
extension.json looks like (Pydantic models + Draft 2020-12 JSON Schema),
ships the offline `lfx extension validate` command, and ships the single
`format_extension_error` function that every other extension-system module
will use to render structured errors.

What's in lfx.extension:

- `ExtensionManifest` / `BundleRef` / `LangflowCompat` Pydantic models with
  `extra="forbid"` so unknown fields fail loudly. Deferred fields (`services`,
  `routes`, `hooks`, `starter_projects`, `userConfig`) are reserved as
  None-only so non-null values produce a dedicated
  `field-deferred-in-this-milestone` error instead of a generic schema wall.
  `bundles` accepts a list but rejects length > 1 with
  `multi-bundle-deferred-in-this-milestone` (validator-enforced; the loader
  re-checks at install time in LE-1015).
- `schema.build_schema()` + `build_schema_json()` produce the publishable
  artifact at schemas.langflow.org/extension/v1.json.
- `ExtensionError` typed envelope and `format_extension_error` -- one branch
  per discriminant. Codes are registered in `ERROR_CODES`; an
  `ExtensionError` constructed with an unknown code raises at construction
  time, preventing producers from shipping without a matching renderer.
- `validate_extension` runs four passes: manifest discovery + schema,
  path-safety (no `..`, no absolute paths, no symlink escape), AST
  inspection of every `.py` (syntax, Component subclass present, build()
  declared, top-level `import *`, top-level I/O primitives), and an opt-in
  `--execute-imports` that runs each module in a subprocess with a
  temporary HOME / TMPDIR / LANGFLOW_CONFIG_DIR and LANGFLOW_*/LFX_* env
  vars stripped.
- `lfx extension validate` and `lfx extension schema` typer subcommands.

Acceptance-criteria coverage in tests/unit/extension/:

- Round-trips every v0 manifest field; deferred fields rejected; multi-bundle
  rejected with the dedicated discriminant.
- JSON Schema validates the v0 example and rejects 12 malformed manifests
  with distinct error paths (>= 10 required by the ticket).
- Median default-validate runtime < 100ms on the basic template.
- Crafted side-effect bundle: default validate does NOT execute it (canary
  file is never written); `--execute-imports` DOES execute it and the canary
  appears, while LANGFLOW_* env vars are NOT inherited by the subprocess.
- Snapshot tests for every code in ERROR_CODES; a guard test verifies
  ERROR_CODES and the snapshot table are in lockstep so future additions
  cannot ship without a format branch and a snapshot.

Wiring: `lfx extension` is a sub-app under the Authoring help panel so
future tickets (LE-1016 init/dev, LE-1018 reload) can attach without
colliding with the existing `lfx validate` (which validates flow JSON, not
extensions).

* fix(lfx): fall back to tomli on Python 3.10 in extension manifest loader

tomllib is stdlib only on 3.11+, but lfx supports 3.10-3.13. Use the
existing tomli runtime dependency as the 3.10 fallback (same API, so the
import alias keeps the rest of the module unchanged).

Fixes ModuleNotFoundError seen in CI on the 3.10 job.

* Update src/lfx/tests/unit/extension/test_schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/extension/schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/cli/_extension_commands.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* feat(lfx): add single-Bundle loader and LANGFLOW_COMPONENTS_PATH discovery (LE-1015)

Introduces lfx.extension.loader: the runtime that turns an Extension on disk
into LoadedComponent records keyed by ext:<bundle>:<Class>@<slot>. Two paths in:

  - load_extension(root): one manifest, one Bundle, registered at @official.
    Re-checks multi-bundle at runtime (defense-in-depth vs. the schema).
  - discover_inline_bundles(paths): each subfolder of LANGFLOW_COMPONENTS_PATH
    is a Bundle at @extra. Walk order is platform-independent (sorted dirs,
    user-declared path order). First-wins on duplicate names; second emits
    duplicate-inline-bundle warning that names both paths.

Manifest-first precedence helpers (installed_extension_roots,
manifest_owning_distributions, filter_plugin_entry_points) let callers of
the legacy langflow.plugins entry-point loader skip distributions that ship
a manifest, so component entry-points are not double-registered.

New typed error codes: module-import-failed, duplicate-component-name,
duplicate-distribution, duplicate-inline-bundle, inline-bundle-name-invalid.
Each ships with a format branch and a snapshot test.

Tests cover the AC: single-bundle happy path, multi-bundle rejection,
missing/empty/no-Component bundle, duplicate class names, deterministic
walk order, recursive discovery, inline-bundle first-wins + dot-dir skip
+ bundle.json metadata, manifest-first precedence partition, PEP-503
distribution-name canonicalization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lfx): address LE-1015 review feedback

- Drop the unproduced duplicate-distribution error code; LE-1022 will add
  it once startup-time discovery has a place to surface it.  Restores the
  invariant that every code in ERROR_CODES has a producer.
- Clarify that intra-bundle relative imports are NOT supported in v0; only
  absolute references between bundle modules work in this milestone.
- Use strict=False when re-resolving bundle_root in the walker; the path
  was already existence-checked, and a concurrent removal in the narrow
  window should not raise across the loader's public boundary.
- Hoist the json import out of _read_inline_bundle_json's body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(lfx): split extension loader into a small subpackage (LE-1015)

Addresses the file-size feedback from the LE-1015 review.  The single
870-line loader.py becomes a flat package keyed off the four section
banners that already existed inline:

    loader/
      __init__.py     # re-exports the public surface
      _types.py       # SLOT constants, LoadedComponent, LoadResult
      _discovery.py   # filesystem walk + importlib.util orchestration
      _detection.py   # Component subclass identification (MRO heuristic)
      _orchestrator.py # load_extension, discover_inline_bundles
      _plugins.py     # manifest-first precedence over langflow.plugins

Largest file is now _orchestrator.py at 440 LOC (was 870); every file is
well under the 800-LOC project guideline.  No behavior change: the public
import paths from lfx.extension are unchanged, all 38 loader tests still
pass.  ``_canonicalize_distribution`` is now exported as
``canonicalize_distribution`` from ``loader._plugins`` (it's a stable
PEP-503 helper that downstream modules will reach for, so it loses the
private underscore).

The test suite is split to mirror the package:

    tests/unit/extension/loader/
      conftest.py             # shared fixtures, FakeDist, autouse scrub
      test_load_extension.py  # @official slot, identity, failure modes
      test_inline_bundles.py  # LANGFLOW_COMPONENTS_PATH, @extra slot
      test_plugins.py         # manifest-first precedence helpers
      test_types.py           # LoadedComponent, LoadResult, code parity

Each test file imports its own slice of the public API and pulls fixtures
from conftest, so a reader looking at one banner can read it in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: LangflowCompat -> LfxCompat

* fix: Remove references to ticket

* fix: encode maxItems constraint in schema

* fix: deferred fields and schema version

* Fix ruff errors

* fix: align loader tests with renamed manifest API and strip ticket refs

The merge of feat/extension-validate brought in the LfxCompat / compat
rename, but the loader test fixtures still used LangflowCompat / bundle_api
and failed at the manifest layer. Update conftest.py + test_load_extension.py
to the post-rename API.

Strip 17 LE-XXXX ticket references from loader source files, errors.py
loader-specific comments, and the four loader test docstrings, matching the
convention applied to LE-1014. Descriptive prose preserved.

Promote _BUNDLE_NAME_RE to BUNDLE_NAME_RE on the manifest module so the
loader's orchestrator no longer reaches across modules into a private name.

* refactor(lfx): clarify broad except, log malformed bundle.json, expand test docstring

- Document why _discovery.import_bundle_module catches BaseException (startup-time loader, must surface bad bundles as typed errors rather than abort).
- Add debug-level logging when bundle.json is malformed or non-object so a stale-cache footgun is at least observable.
- Expand test_skips_re_imported_class docstring so future maintainers don't accidentally weaken the __module__-equality guard if package-style relative imports get added later.

Also drops the stale duplicate-distribution claim from the PR description; that code is correctly deferred to LE-1022 along with /all integration.

* feat(lfx): wire Extension System into /all, pathsep-split LANGFLOW_COMPONENTS_PATH, emit duplicate-distribution

Closes the four AC gaps the previous reviewer flagged on PR #12967:

1. /all integration: get_and_cache_all_types_dict now also calls a new
   import_extension_components() that loads installed Extensions via
   load_installed_extensions, loads inline bundles via discover_inline_bundles,
   and builds frontend-node templates with extension/bundle/extension_version
   fields stamped on. Failures are logged and skipped per bundle.

2. LANGFLOW_COMPONENTS_PATH is now split on os.pathsep so multi-entry env vars
   (e.g. /a:/b on POSIX) produce multiple components-path entries instead of
   one literal non-existent path. Empty segments and missing paths are skipped.

3. duplicate-distribution is a real producer: load_installed_extensions
   surfaces a typed warning on the winner LoadResult when two distributions
   share a canonical name, naming every involved manifest path.

4. Manifest-first precedence runtime wiring: new filter_component_entry_points
   loads each entry-point and only skips ones that resolve to a Component
   subclass on a manifest-shipping distribution. plugin_routes.load_plugin_routes
   now applies it so non-component entry-points (route registrars) keep
   loading per the AC's 'unaffected' promise. Added the previously-missing
   AC test for same-distribution component+non-component partition.

Also makes _distribution_canonical_name defensive against MagicMock test seams.

* fix(lfx): register extension components under namespaced ID; promote duplicate-distribution to error

Addresses the latest review of PR #12967:

P1: /all integration now keys the cache inner dict by LoadedComponent.namespaced_id
(ext:<bundle>:<Class>@<slot>) rather than the bare class name. Templates also carry
the namespaced_id as an explicit field so consumers that look at the value (not the
key) still see the canonical address. This is the form the LE-1020 migration table
will rewrite legacy class-name references to.

P2: load_installed_extensions now appends duplicate-distribution to result.errors
instead of result.warnings, so LoadResult.ok=False when two distributions share a
canonical name. The winner's components still appear in result.components so flows
already pinned to them keep working; only the conflict status changes. Updated test
to assert errors + ok=False; added explicit assertion that the winner's components
are still present.

* fix(lfx): installed-distribution discovery accepts pyproject.toml manifest form

Closes the latest review finding on PR #12967: the installed-distribution
scan only looked for extension.json, ignoring distributions whose manifest
lives in [tool.langflow.extension] inside pyproject.toml. The AC explicitly
treats both as valid manifest forms.

_distribution_manifest_path now:
- Returns extension.json immediately when present (preserves precedence
  matching load_manifest's discovery order).
- Falls back to pyproject.toml only when extension.json is absent AND the
  pyproject's [tool.langflow.extension] section is parseable.

Validation reuses load_manifest itself so the rule lives in exactly one
place; a stray pyproject.toml without the section is correctly ignored.

Tests cover: pyproject-only discovery, pyproject-without-section ignored,
extension.json wins on collision, end-to-end pyproject load at @official,
and pyproject-form manifest-first entry-point suppression.

* refactor(lfx): tighten loader invariants, surface silent skips, harden tests

Addresses the latest review feedback on PR #12967:

Type-level invariants (_types.py):
- LoadedComponent.__post_init__ enforces that @extra components must NOT
  carry a distribution. The reverse (@official without distribution) is
  permitted because load_extension is also used for dev-mode loads
  against a working tree before pip install.
- LoadResult docstring documents the partial-success contract: components
  may be non-empty when errors is non-empty (some files imported, others
  failed). Callers branching on ok get strict success.

Silent-failure fixes (_orchestrator.py + settings/base.py):
- inline-path-missing: a non-existent / non-dir LANGFLOW_COMPONENTS_PATH
  entry now produces a typed warning per skipped path so a typo no
  longer yields zero diagnostics. Settings-layer skip bumped from debug
  to warning for the same reason.
- bundle-json-invalid: a malformed or non-object bundle.json now surfaces
  a typed warning instead of silently rewriting the user-declared
  id/version to derived values under the same bundle name.
- no-component-subclass gating uses a call-local counter instead of
  result.errors so the diagnostic stays accurate when a future caller
  reuses a LoadResult (multi-bundle / batch wrapper scenarios).

Test hardening:
- test_re_imported_class_is_skipped_via_module_filter rewritten to
  actually exercise the __module__-equality check via sys.modules
  injection; previously passed via module-import-failed (relative-import
  failure), which would silently weaken if package registration changes.
- test_user_declared_path_order_is_preserved: AC #8's multi-path order
  case (distinct bundles in [path_b, path_a]) was unasserted; added.
- test_inline_module_import_failure_attributes_identity: AC #10's
  identity-on-partial-failure was covered for @official but not @extra;
  added.
- test_uses_real_distributions_by_default tightened to assert ep
  placement (in kept, not in skipped) instead of exact-list equality, so
  a future Langflow-shipped manifest doesn't silently flip the assertion.

bumped 64 -> 175 passing extension tests; 20 backend integration tests
still pass.

* fix(lfx): malformed pyproject manifests surface manifest-invalid instead of disappearing

Closes the latest review finding on PR #12967: a pyproject.toml with a
[tool.langflow.extension] section that has missing/invalid required
fields was silently dropped because _pyproject_has_extension_section
ran full schema validation via load_manifest and returned False on
ValueError/TypeError. That conflated 'no section' with 'section
malformed'.

Fix: detect section presence only. _pyproject_has_extension_section now
calls _read_pyproject_extension (TOML parse + key lookup, no schema
check). Behavior:
- Section absent or pyproject TOML unparseable -> False (treat as
  regular non-manifest package).
- Section present and is a table (valid OR schema-invalid) -> True.
- Section present but is not a table -> True; the author intended to
  declare an extension and load_extension will surface the typed error.

This way a typo'd pyproject Extension produces a typed manifest-invalid
LoadResult with extension_id attribution, and manifest-first precedence
still suppresses its legacy component entry-points -- matching the
'typed load results on success/failure' contract for the supported
pyproject manifest form.

Tests: two new cases pin the behavior. test_malformed_pyproject_section_
surfaces_manifest_invalid asserts a typed load-failure result with
distribution attribution; the second test pins manifest-first
suppression for malformed pyproject distributions.

* refactor(lfx): emit inline-path-unreadable, document reload contract, trim rot

Closes the remaining nits on PR #12967:

- inline-path-unreadable (new typed error code): a configured
  LANGFLOW_COMPONENTS_PATH entry that raises OSError on iterdir
  (typically permission-denied) now produces a typed LoadResult error
  carrying str(exc) instead of silently swallowing the message.
- duplicate-inline-bundle hint trimmed: removed forward promise about
  hard-error-in-a-later-release; same actionability, no expiration.
- discover_inline_bundles docstring trimmed from three paragraphs to
  two sentences (per CLAUDE.md anti-multi-paragraph rule).
- _distribution_manifest_path docstring trimmed to one-liner.
- installed_extension_roots dropped Used-by caller-narration list;
  manifest_owning_distributions docstring rewritten to call out the
  shadow-load risk for direct callers and point to load_installed_
  extensions for the typed warning surface.
- _discovery.import_bundle_module: replaced 'until that lands in a
  later milestone' rot with a single line ('Absolute imports only
  between bundle modules; relative imports unsupported.') AND added
  the single-load-per-process contract note documenting why LE-1018
  reload must scrub registry/sys.modules before re-invoking the loader.
- load_extension docstring grew a 'Single-load-per-process contract'
  block telling direct callers not to rely on this function for refresh.

Tests: new test_unreadable_path_emits_inline_path_unreadable pins the
OSError -> typed-error path.

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(lfx): atomic-swap Bundle reload pipeline + endpoint + CLI (LE-1018) (#12979)

* feat(lfx): add single-Bundle loader and LANGFLOW_COMPONENTS_PATH discovery (LE-1015)

Introduces lfx.extension.loader: the runtime that turns an Extension on disk
into LoadedComponent records keyed by ext:<bundle>:<Class>@<slot>. Two paths in:

  - load_extension(root): one manifest, one Bundle, registered at @official.
    Re-checks multi-bundle at runtime (defense-in-depth vs. the schema).
  - discover_inline_bundles(paths): each subfolder of LANGFLOW_COMPONENTS_PATH
    is a Bundle at @extra. Walk order is platform-independent (sorted dirs,
    user-declared path order). First-wins on duplicate names; second emits
    duplicate-inline-bundle warning that names both paths.

Manifest-first precedence helpers (installed_extension_roots,
manifest_owning_distributions, filter_plugin_entry_points) let callers of
the legacy langflow.plugins entry-point loader skip distributions that ship
a manifest, so component entry-points are not double-registered.

New typed error codes: module-import-failed, duplicate-component-name,
duplicate-distribution, duplicate-inline-bundle, inline-bundle-name-invalid.
Each ships with a format branch and a snapshot test.

Tests cover the AC: single-bundle happy path, multi-bundle rejection,
missing/empty/no-Component bundle, duplicate class names, deterministic
walk order, recursive discovery, inline-bundle first-wins + dot-dir skip
+ bundle.json metadata, manifest-first precedence partition, PEP-503
distribution-name canonicalization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lfx): address LE-1015 review feedback

- Drop the unproduced duplicate-distribution error code; LE-1022 will add
  it once startup-time discovery has a place to surface it.  Restores the
  invariant that every code in ERROR_CODES has a producer.
- Clarify that intra-bundle relative imports are NOT supported in v0; only
  absolute references between bundle modules work in this milestone.
- Use strict=False when re-resolving bundle_root in the walker; the path
  was already existence-checked, and a concurrent removal in the narrow
  window should not raise across the loader's public boundary.
- Hoist the json import out of _read_inline_bundle_json's body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(lfx): split extension loader into a small subpackage (LE-1015)

Addresses the file-size feedback from the LE-1015 review.  The single
870-line loader.py becomes a flat package keyed off the four section
banners that already existed inline:

    loader/
      __init__.py     # re-exports the public surface
      _types.py       # SLOT constants, LoadedComponent, LoadResult
      _discovery.py   # filesystem walk + importlib.util orchestration
      _detection.py   # Component subclass identification (MRO heuristic)
      _orchestrator.py # load_extension, discover_inline_bundles
      _plugins.py     # manifest-first precedence over langflow.plugins

Largest file is now _orchestrator.py at 440 LOC (was 870); every file is
well under the 800-LOC project guideline.  No behavior change: the public
import paths from lfx.extension are unchanged, all 38 loader tests still
pass.  ``_canonicalize_distribution`` is now exported as
``canonicalize_distribution`` from ``loader._plugins`` (it's a stable
PEP-503 helper that downstream modules will reach for, so it loses the
private underscore).

The test suite is split to mirror the package:

    tests/unit/extension/loader/
      conftest.py             # shared fixtures, FakeDist, autouse scrub
      test_load_extension.py  # @official slot, identity, failure modes
      test_inline_bundles.py  # LANGFLOW_COMPONENTS_PATH, @extra slot
      test_plugins.py         # manifest-first precedence helpers
      test_types.py           # LoadedComponent, LoadResult, code parity

Each test file imports its own slice of the public API and pulls fixtures
from conftest, so a reader looking at one banner can read it in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(lfx): atomic-swap Bundle reload pipeline + endpoint + CLI (LE-1018)

Five-stage reload (parallel staging load -> validate -> swap under write
lock -> cleanup -> emit) for installed Bundles in Mode A.  In-flight
flows keep the pre-swap class via existing references; new flows pick up
the post-swap class atomically; concurrent reloads on the same Bundle
are rejected with reload-in-progress.

Adds:

* lfx/extension/registry.py -- BundleRegistry with per-bundle
  reload-in-progress guard and components_index.json writer
* lfx/extension/reload.py -- the five-stage pipeline; events emission
  is stubbed (TODO LE-1017) so the swap mechanics can ship before the
  events service lands
* loader: optional module_namespace param so Stage 1 lands in
  __reload_staging__.<id> instead of the live _lfx_ext.* namespace
* errors: four new typed reload codes (reload-in-progress,
  reload-bundle-not-installed, reload-bundle-name-mismatch,
  reload-source-missing) with branch templates and snapshot tests
* HTTP: POST /api/v1/extensions/{id}/bundles/{name}/reload, gated by
  the existing get_current_active_user dependency, returns 409 with a
  typed body for the in-progress collision case
* CLI: lfx extension reload <id> [--bundle <name>] -- HTTP client
  against the dev server with text/json output and proper exit codes
  (--all is gated until LE-1019 lands the list endpoint)
* tests: 16 reload-pipeline tests covering the AC matrix (rename
  round-trip, broken-bundle isolation, concurrent readers, in-flight
  flow, double-reload guard, bundle-name mismatch) plus 9 CLI client
  tests

Mode A only.  In Mode B/C bundle changes require a Docker image rebuild
and the reload path is not exercised.

The events emission in Stage 5 is intentionally stubbed -- the LE-1017
ticket will swap the body of _emit_bundle_reload_event in one place
without touching the pipeline core.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

* feat(lfx): add `extension init` and `extension dev` CLIs (LE-1016) (#12968)

* feat(lfx): add extension manifest schema, validate CLI, and error formatter (LE-1014)

Foundation for the Bundle Separation iteration. Defines what a valid
extension.json looks like (Pydantic models + Draft 2020-12 JSON Schema),
ships the offline `lfx extension validate` command, and ships the single
`format_extension_error` function that every other extension-system module
will use to render structured errors.

What's in lfx.extension:

- `ExtensionManifest` / `BundleRef` / `LangflowCompat` Pydantic models with
  `extra="forbid"` so unknown fields fail loudly. Deferred fields (`services`,
  `routes`, `hooks`, `starter_projects`, `userConfig`) are reserved as
  None-only so non-null values produce a dedicated
  `field-deferred-in-this-milestone` error instead of a generic schema wall.
  `bundles` accepts a list but rejects length > 1 with
  `multi-bundle-deferred-in-this-milestone` (validator-enforced; the loader
  re-checks at install time in LE-1015).
- `schema.build_schema()` + `build_schema_json()` produce the publishable
  artifact at schemas.langflow.org/extension/v1.json.
- `ExtensionError` typed envelope and `format_extension_error` -- one branch
  per discriminant. Codes are registered in `ERROR_CODES`; an
  `ExtensionError` constructed with an unknown code raises at construction
  time, preventing producers from shipping without a matching renderer.
- `validate_extension` runs four passes: manifest discovery + schema,
  path-safety (no `..`, no absolute paths, no symlink escape), AST
  inspection of every `.py` (syntax, Component subclass present, build()
  declared, top-level `import *`, top-level I/O primitives), and an opt-in
  `--execute-imports` that runs each module in a subprocess with a
  temporary HOME / TMPDIR / LANGFLOW_CONFIG_DIR and LANGFLOW_*/LFX_* env
  vars stripped.
- `lfx extension validate` and `lfx extension schema` typer subcommands.

Acceptance-criteria coverage in tests/unit/extension/:

- Round-trips every v0 manifest field; deferred fields rejected; multi-bundle
  rejected with the dedicated discriminant.
- JSON Schema validates the v0 example and rejects 12 malformed manifests
  with distinct error paths (>= 10 required by the ticket).
- Median default-validate runtime < 100ms on the basic template.
- Crafted side-effect bundle: default validate does NOT execute it (canary
  file is never written); `--execute-imports` DOES execute it and the canary
  appears, while LANGFLOW_* env vars are NOT inherited by the subprocess.
- Snapshot tests for every code in ERROR_CODES; a guard test verifies
  ERROR_CODES and the snapshot table are in lockstep so future additions
  cannot ship without a format branch and a snapshot.

Wiring: `lfx extension` is a sub-app under the Authoring help panel so
future tickets (LE-1016 init/dev, LE-1018 reload) can attach without
colliding with the existing `lfx validate` (which validates flow JSON, not
extensions).

* fix(lfx): fall back to tomli on Python 3.10 in extension manifest loader

tomllib is stdlib only on 3.11+, but lfx supports 3.10-3.13. Use the
existing tomli runtime dependency as the 3.10 fallback (same API, so the
import alias keeps the rest of the module unchanged).

Fixes ModuleNotFoundError seen in CI on the 3.10 job.

* Update src/lfx/tests/unit/extension/test_schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/extension/schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/cli/_extension_commands.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* feat(lfx): add single-Bundle loader and LANGFLOW_COMPONENTS_PATH discovery (LE-1015)

Introduces lfx.extension.loader: the runtime that turns an Extension on disk
into LoadedComponent records keyed by ext:<bundle>:<Class>@<slot>. Two paths in:

  - load_extension(root): one manifest, one Bundle, registered at @official.
    Re-checks multi-bundle at runtime (defense-in-depth vs. the schema).
  - discover_inline_bundles(paths): each subfolder of LANGFLOW_COMPONENTS_PATH
    is a Bundle at @extra. Walk order is platform-independent (sorted dirs,
    user-declared path order). First-wins on duplicate names; second emits
    duplicate-inline-bundle warning that names both paths.

Manifest-first precedence helpers (installed_extension_roots,
manifest_owning_distributions, filter_plugin_entry_points) let callers of
the legacy langflow.plugins entry-point loader skip distributions that ship
a manifest, so component entry-points are not double-registered.

New typed error codes: module-import-failed, duplicate-component-name,
duplicate-distribution, duplicate-inline-bundle, inline-bundle-name-invalid.
Each ships with a format branch and a snapshot test.

Tests cover the AC: single-bundle happy path, multi-bundle rejection,
missing/empty/no-Component bundle, duplicate class names, deterministic
walk order, recursive discovery, inline-bundle first-wins + dot-dir skip
+ bundle.json metadata, manifest-first precedence partition, PEP-503
distribution-name canonicalization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lfx): address LE-1015 review feedback

- Drop the unproduced duplicate-distribution error code; LE-1022 will add
  it once startup-time discovery has a place to surface it.  Restores the
  invariant that every code in ERROR_CODES has a producer.
- Clarify that intra-bundle relative imports are NOT supported in v0; only
  absolute references between bundle modules work in this milestone.
- Use strict=False when re-resolving bundle_root in the walker; the path
  was already existence-checked, and a concurrent removal in the narrow
  window should not raise across the loader's public boundary.
- Hoist the json import out of _read_inline_bundle_json's body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(lfx): split extension loader into a small subpackage (LE-1015)

Addresses the file-size feedback from the LE-1015 review.  The single
870-line loader.py becomes a flat package keyed off the four section
banners that already existed inline:

    loader/
      __init__.py     # re-exports the public surface
      _types.py       # SLOT constants, LoadedComponent, LoadResult
      _discovery.py   # filesystem walk + importlib.util orchestration
      _detection.py   # Component subclass identification (MRO heuristic)
      _orchestrator.py # load_extension, discover_inline_bundles
      _plugins.py     # manifest-first precedence over langflow.plugins

Largest file is now _orchestrator.py at 440 LOC (was 870); every file is
well under the 800-LOC project guideline.  No behavior change: the public
import paths from lfx.extension are unchanged, all 38 loader tests still
pass.  ``_canonicalize_distribution`` is now exported as
``canonicalize_distribution`` from ``loader._plugins`` (it's a stable
PEP-503 helper that downstream modules will reach for, so it loses the
private underscore).

The test suite is split to mirror the package:

    tests/unit/extension/loader/
      conftest.py             # shared fixtures, FakeDist, autouse scrub
      test_load_extension.py  # @official slot, identity, failure modes
      test_inline_bundles.py  # LANGFLOW_COMPONENTS_PATH, @extra slot
      test_plugins.py         # manifest-first precedence helpers
      test_types.py           # LoadedComponent, LoadResult, code parity

Each test file imports its own slice of the public API and pulls fixtures
from conftest, so a reader looking at one banner can read it in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(lfx): add `extension init` and `extension dev` CLIs (LE-1016)

The two scaffolding CLIs an Extension author types:

  - `lfx extension init <target>` writes the basic single-Bundle
    template (manifest with $schema, README, .gitignore, one Component
    subclass + a pytest smoke test).  AC #1: the generated extension
    validates clean against LE-1014.  AC #2: the generated test file is
    a valid pytest module that exercises the component's build() method.
    AC #3: any --template other than 'basic' fails with a typed
    template-deferred-in-this-milestone error and a non-zero exit.
    Refuses to scaffold over a non-empty target dir.

  - `lfx extension dev <target>` validates the local extension, records
    its absolute path in <config_dir>/extensions/dev_extensions.json,
    prints reload instructions, and execs `langflow run` (or
    `python -m langflow` when langflow isn't on PATH).  --skip-launch
    registers without launching (used by tests + external dev-server
    scripts); --skip-validate lets authors register a known-broken
    manifest to debug it under the loader.

Stack:
  - Wave 0: error codes (extension-target-exists,
    extension-target-invalid, local-extension-missing) with format
    branches and snapshot tests.
  - Wave 1: lfx.extension.init_template -- pure-data scaffolder, no
    Typer dependency so the CLI is a thin shell over it.
  - Wave 1: lfx.extension.dev_registry -- atomic JSON state file under
    the langflow user-cache dir; helpers for register / list /
    unregister / load_dev_extensions / dev_extension_component_paths.
  - Wave 2: lfx.cli._extension_commands gains init/dev subcommands.
  - Wave 2: langflow.main lifespan hook reads the dev registry after
    bundle loading and extends components_path with each registered
    bundle dir, so the existing palette discovery picks up dev
    extensions.  Missing paths surface as local-extension-missing
    warnings (AC #5) without aborting startup.

Tests (84 new, 197 total in tests/unit/extension/):
  - test_init_template.py: AC scenarios + identifier derivation +
    deterministic file shape.
  - test_dev_registry.py: register/list/unregister round-trip,
    idempotent re-register refreshes timestamp, malformed state file
    treated as empty, missing-path warning, recovery when path
    reappears, env-var override precedence.
  - test_cli.py: AC #1 init->validate, AC #3 deferred templates,
    --skip-launch registers without launching, --skip-validate
    short-circuits the pre-flight pass.
  - test_errors.py: snapshot rows for all three new codes; the
    every-known-code-has-a-snapshot test enforces parity.

LE-1018 (reload) reuses load_dev_extensions; LE-1022 (installed-pkg
discovery) shares the components_path extension pattern.  AC #4 ("boots
Langflow with the extension visible in the palette within 5s") is
delivered jointly by `extension dev` (registers + execs) and the
lifespan hook (loads on startup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lfx): address LE-1016 review feedback

Fixes the four HIGH issues + the elevated LOW from the review pass:

1. dev_extension_component_paths now forwards EVERY warning, not just
   local-extension-missing.  Previously a duplicate-component-name (or
   any future warning code) was silently dropped, hiding real signal
   from the lifespan hook's logs.

2. Defensive emit when a LoadResult has components but source_path is
   None.  The current loader always sets source_path, but a future
   hand-built LoadResult could violate that contract; we now surface a
   typed local-extension-missing error rather than dropping the
   extension silently.

3. Replaced the fragile ``min(len(parts))`` bundle-root selection with
   a relative-to-source-path measurement.  Handles deep-vs-shallow
   sibling extensions correctly without depending on absolute path
   depth.

4. Generated README now documents the langflow/lfx prerequisite under
   the Develop section so authors know `pytest` requires the lfx
   environment, not just Python.

5. Forced LANGFLOW_LAZY_LOAD_COMPONENTS=false unconditionally in the
   `extension dev` exec env (was setdefault, which let a developer's
   global lazy-loading export silently hide their dev components from
   the palette and miss AC #4's 5s budget).

Plus three MEDIUMs:

  - sys.modules cleanup in test_generated_test_file_runs_against_generated_component
    so a later test importing the same dotted path doesn't pick up a
    stale module from a deleted tmp_path.  Component instantiation
    moved inside the try block because Component.__init__ uses
    inspect.getsourcefile against self.__class__'s still-live module.
  - Added regex-drift test that pins the init_template patterns to
    match manifest.py's so a schema regex change can't quietly produce
    invalid scaffolded manifests.
  - Added two new dev_registry tests covering the forward-all-warnings
    contract and the source_path=None defense.

Tests: 201 passing (4 new); ruff check + format clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: LangflowCompat -> LfxCompat

* fix: Remove references to ticket

* fix: encode maxItems constraint in schema

* fix: deferred fields and schema version

* Fix ruff errors

* fix: align loader tests with renamed manifest API and strip ticket refs

The merge of feat/extension-validate brought in the LfxCompat / compat
rename, but the loader test fixtures still used LangflowCompat / bundle_api
and failed at the manifest layer. Update conftest.py + test_load_extension.py
to the post-rename API.

Strip 17 LE-XXXX ticket references from loader source files, errors.py
loader-specific comments, and the four loader test docstrings, matching the
convention applied to LE-1014. Descriptive prose preserved.

Promote _BUNDLE_NAME_RE to BUNDLE_NAME_RE on the manifest module so the
loader's orchestrator no longer reaches across modules into a private name.

* fix(lfx): align init template with renamed manifest API

After merging feat/extension-loader into this branch, two surfaces
fell out of sync with the renamed manifest schema:

- init_template generates extension.json with `lfx: {bundle_api: [1]}`,
  but the validator now requires `lfx: {compat: ["1"]}` per LfxCompat.
  This made `test_basic_template_validates_clean` fail with
  manifest-invalid (`lfx.compat: Field required; lfx.bundle_api: Extra
  inputs are not permitted`).
- test_init_template_regexes_match_manifest_schema reads
  `manifest_mod._BUNDLE_NAME_RE`, but that symbol was promoted to public
  `BUNDLE_NAME_RE` in c63f84a591. Update the test to reference the
  public name; init_template's local copy is still private since it
  also covers `_EXTENSION_ID_RE`, which remains private upstream.

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(lfx): append-only migration table + flow deserializer rewrite hook (#13024)

* feat(lfx): add extension manifest schema, validate CLI, and error formatter (LE-1014)

Foundation for the Bundle Separation iteration. Defines what a valid
extension.json looks like (Pydantic models + Draft 2020-12 JSON Schema),
ships the offline `lfx extension validate` command, and ships the single
`format_extension_error` function that every other extension-system module
will use to render structured errors.

What's in lfx.extension:

- `ExtensionManifest` / `BundleRef` / `LangflowCompat` Pydantic models with
  `extra="forbid"` so unknown fields fail loudly. Deferred fields (`services`,
  `routes`, `hooks`, `starter_projects`, `userConfig`) are reserved as
  None-only so non-null values produce a dedicated
  `field-deferred-in-this-milestone` error instead of a generic schema wall.
  `bundles` accepts a list but rejects length > 1 with
  `multi-bundle-deferred-in-this-milestone` (validator-enforced; the loader
  re-checks at install time in LE-1015).
- `schema.build_schema()` + `build_schema_json()` produce the publishable
  artifact at schemas.langflow.org/extension/v1.json.
- `ExtensionError` typed envelope and `format_extension_error` -- one branch
  per discriminant. Codes are registered in `ERROR_CODES`; an
  `ExtensionError` constructed with an unknown code raises at construction
  time, preventing producers from shipping without a matching renderer.
- `validate_extension` runs four passes: manifest discovery + schema,
  path-safety (no `..`, no absolute paths, no symlink escape), AST
  inspection of every `.py` (syntax, Component subclass present, build()
  declared, top-level `import *`, top-level I/O primitives), and an opt-in
  `--execute-imports` that runs each module in a subprocess with a
  temporary HOME / TMPDIR / LANGFLOW_CONFIG_DIR and LANGFLOW_*/LFX_* env
  vars stripped.
- `lfx extension validate` and `lfx extension schema` typer subcommands.

Acceptance-criteria coverage in tests/unit/extension/:

- Round-trips every v0 manifest field; deferred fields rejected; multi-bundle
  rejected with the dedicated discriminant.
- JSON Schema validates the v0 example and rejects 12 malformed manifests
  with distinct error paths (>= 10 required by the ticket).
- Median default-validate runtime < 100ms on the basic template.
- Crafted side-effect bundle: default validate does NOT execute it (canary
  file is never written); `--execute-imports` DOES execute it and the canary
  appears, while LANGFLOW_* env vars are NOT inherited by the subprocess.
- Snapshot tests for every code in ERROR_CODES; a guard test verifies
  ERROR_CODES and the snapshot table are in lockstep so future additions
  cannot ship without a format branch and a snapshot.

Wiring: `lfx extension` is a sub-app under the Authoring help panel so
future tickets (LE-1016 init/dev, LE-1018 reload) can attach without
colliding with the existing `lfx validate` (which validates flow JSON, not
extensions).

* fix(lfx): fall back to tomli on Python 3.10 in extension manifest loader

tomllib is stdlib only on 3.11+, but lfx supports 3.10-3.13. Use the
existing tomli runtime dependency as the 3.10 fallback (same API, so the
import alias keeps the rest of the module unchanged).

Fixes ModuleNotFoundError seen in CI on the 3.10 job.

* Update src/lfx/tests/unit/extension/test_schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/extension/schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/cli/_extension_commands.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* feat(lfx): add single-Bundle loader and LANGFLOW_COMPONENTS_PATH discovery (LE-1015)

Introduces lfx.extension.loader: the runtime that turns an Extension on disk
into LoadedComponent records keyed by ext:<bundle>:<Class>@<slot>. Two paths in:

  - load_extension(root): one manifest, one Bundle, registered at @official.
    Re-checks multi-bundle at runtime (defense-in-depth vs. the schema).
  - discover_inline_bundles(paths): each subfolder of LANGFLOW_COMPONENTS_PATH
    is a Bundle at @extra. Walk order is platform-independent (sorted dirs,
    user-declared path order). First-wins on duplicate names; second emits
    duplicate-inline-bundle warning that names both paths.

Manifest-first precedence helpers (installed_extension_roots,
manifest_owning_distributions, filter_plugin_entry_points) let callers of
the legacy langflow.plugins entry-point loader skip distributions that ship
a manifest, so component entry-points are not double-registered.

New typed error codes: module-import-failed, duplicate-component-name,
duplicate-distribution, duplicate-inline-bundle, inline-bundle-name-invalid.
Each ships with a format branch and a snapshot test.

Tests cover the AC: single-bundle happy path, multi-bundle rejection,
missing/empty/no-Component bundle, duplicate class names, deterministic
walk order, recursive discovery, inline-bundle first-wins + dot-dir skip
+ bundle.json metadata, manifest-first precedence partition, PEP-503
distribution-name canonicalization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lfx): address LE-1015 review feedback

- Drop the unproduced duplicate-distribution error code; LE-1022 will add
  it once startup-time discovery has a place to surface it.  Restores the
  invariant that every code in ERROR_CODES has a producer.
- Clarify that intra-bundle relative imports are NOT supported in v0; only
  absolute references between bundle modules work in this milestone.
- Use strict=False when re-resolving bundle_root in the walker; the path
  was already existence-checked, and a concurrent removal in the narrow
  window should not raise across the loader's public boundary.
- Hoist the json import out of _read_inline_bundle_json's body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(lfx): split extension loader into a small subpackage (LE-1015)

Addresses the file-size feedback from the LE-1015 review.  The single
870-line loader.py becomes a flat package keyed off the four section
banners that already existed inline:

    loader/
      __init__.py     # re-exports the public surface
      _types.py       # SLOT constants, LoadedComponent, LoadResult
      _discovery.py   # filesystem walk + importlib.util orchestration
      _detection.py   # Component subclass identification (MRO heuristic)
      _orchestrator.py # load_extension, discover_inline_bundles
      _plugins.py     # manifest-first precedence over langflow.plugins

Largest file is now _orchestrator.py at 440 LOC (was 870); every file is
well under the 800-LOC project guideline.  No behavior change: the public
import paths from lfx.extension are unchanged, all 38 loader tests still
pass.  ``_canonicalize_distribution`` is now exported as
``canonicalize_distribution`` from ``loader._plugins`` (it's a stable
PEP-503 helper that downstream modules will reach for, so it loses the
private underscore).

The test suite is split to mirror the package:

    tests/unit/extension/loader/
      conftest.py             # shared fixtures, FakeDist, autouse scrub
      test_load_extension.py  # @official slot, identity, failure modes
      test_inline_bundles.py  # LANGFLOW_COMPONENTS_PATH, @extra slot
      test_plugins.py         # manifest-first precedence helpers
      test_types.py           # LoadedComponent, LoadResult, code parity

Each test file imports its own slice of the public API and pulls fixtures
from conftest, so a reader looking at one banner can read it in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: LangflowCompat -> LfxCompat

* fix: Remove references to ticket

* fix: encode maxItems constraint in schema

* fix: deferred fields and schema version

* Fix ruff errors

* fix: align loader tests with renamed manifest API and strip ticket refs

The merge of feat/extension-validate brought in the LfxCompat / compat
rename, but the loader test fixtures still used LangflowCompat / bundle_api
and failed at the manifest layer. Update conftest.py + test_load_extension.py
to the post-rename API.

Strip 17 LE-XXXX ticket references from loader source files, errors.py
loader-specific comments, and the four loader test docstrings, matching the
convention applied to LE-1014. Descriptive prose preserved.

Promote _BUNDLE_NAME_RE to BUNDLE_NAME_RE on the manifest module so the
loader's orchestrator no longer reaches across modules into a private name.

* feat(lfx): append-only migration table + flow deserializer rewrite hook

Adds the migration layer of the Extension System: an append-only JSON table
that maps three legacy component reference shapes (bare class name, old
import path, pre-Phase-A namespaced slot) to the post-Phase-A canonical
ext:<bundle>:<Class>@<slot> identifier, plus a deserializer hook that
rewrites a saved-flow payload in place against that table on load.

What landed:

  * lfx.extension.migration.schema -- Pydantic models for MigrationEntry +
    MigrationTable with per-entry validators (exactly one of bare/import/slot
    populated; canonical target shape) and table-level uniqueness check.
  * lfx.extension.migration.loader -- canonical in-repo path, threadsafe
    process-lifetime cache, typed errors on every failure mode.
  * lfx.extension.migration.rewrite -- node-by-node rewrite, idempotent on
    canonical refs, difflib-backed closest-match suggestion for unmapped
    references, cross-bucket ambiguity surfaces component-name-ambiguous
    instead of silently loading into the wrong bundle.
  * Wired into Graph.from_payload before validate_flow_for_current_settings
    so every saved-flow load goes through migration first.
  * scripts/migrate/check_migration_append_only.py -- CI guard that diffs
    the working-tree table against origin/main and rejects removals or
    target mutations; reordering and additions are allowed.
  * 34 new unit tests covering rewrite paths, loader failure modes, schema
    invariants, and the CI script behavior.

What is deliberately deferred:

  * flow-migrated event emission. The events pipeline is unavailable in this
    iteration; the wiring point in Graph.from_payload is marked TODO and the
    MigrationReport already carries every field a future emitter needs.

The shipped migration_table.json starts empty; entries land alongside the
pilot bundle extraction in a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(frontend): palette Bundle reload action + loading + toasts (#13025)

* feat(lfx): add extension manifest schema, validate CLI, and error formatter (LE-1014)

Foundation for the Bundle Separation iteration. Defines what a valid
extension.json looks like (Pydantic models + Draft 2020-12 JSON Schema),
ships the offline `lfx extension validate` command, and ships the single
`format_extension_error` function that every other extension-system module
will use to render structured errors.

What's in lfx.extension:

- `ExtensionManifest` / `BundleRef` / `LangflowCompat` Pydantic models with
  `extra="forbid"` so unknown fields fail loudly. Deferred fields (`services`,
  `routes`, `hooks`, `starter_projects`, `userConfig`) are reserved as
  None-only so non-null values produce a dedicated
  `field-deferred-in-this-milestone` error instead of a generic schema wall.
  `bundles` accepts a list but rejects length > 1 with
  `multi-bundle-deferred-in-this-milestone` (validator-enforced; the loader
  re-checks at install time in LE-1015).
- `schema.build_schema()` + `build_schema_json()` produce the publishable
  artifact at schemas.langflow.org/extension/v1.json.
- `ExtensionError` typed envelope and `format_extension_error` -- one branch
  per discriminant. Codes are registered in `ERROR_CODES`; an
  `ExtensionError` constructed with an unknown code raises at construction
  time, preventing producers from shipping without a matching renderer.
- `validate_extension` runs four passes: manifest discovery + schema,
  path-safety (no `..`, no absolute paths, no symlink escape), AST
  inspection of every `.py` (syntax, Component subclass present, build()
  declared, top-level `import *`, top-level I/O primitives), and an opt-in
  `--execute-imports` that runs each module in a subprocess with a
  temporary HOME / TMPDIR / LANGFLOW_CONFIG_DIR and LANGFLOW_*/LFX_* env
  vars stripped.
- `lfx extension validate` and `lfx extension schema` typer subcommands.

Acceptance-criteria coverage in tests/unit/extension/:

- Round-trips every v0 manifest field; deferred fields rejected; multi-bundle
  rejected with the dedicated discriminant.
- JSON Schema validates the v0 example and rejects 12 malformed manifests
  with distinct error paths (>= 10 required by the ticket).
- Median default-validate runtime < 100ms on the basic template.
- Crafted side-effect bundle: default validate does NOT execute it (canary
  file is never written); `--execute-imports` DOES execute it and the canary
  appears, while LANGFLOW_* env vars are NOT inherited by the subprocess.
- Snapshot tests for every code in ERROR_CODES; a guard test verifies
  ERROR_CODES and the snapshot table are in lockstep so future additions
  cannot ship without a format branch and a snapshot.

Wiring: `lfx extension` is a sub-app under the Authoring help panel so
future tickets (LE-1016 init/dev, LE-1018 reload) can attach without
colliding with the existing `lfx validate` (which validates flow JSON, not
extensions).

* fix(lfx): fall back to tomli on Python 3.10 in extension manifest loader

tomllib is stdlib only on 3.11+, but lfx supports 3.10-3.13. Use the
existing tomli runtime dependency as the 3.10 fallback (same API, so the
import alias keeps the rest of the module unchanged).

Fixes ModuleNotFoundError seen in CI on the 3.10 job.

* Update src/lfx/tests/unit/extension/test_schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/extension/schema.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/cli/_extension_commands.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* feat(lfx): add single-Bundle loader and LANGFLOW_COMPONENTS_PATH discovery (LE-1015)

Introduces lfx.extension.loader: the runtime that turns an Extension on disk
into LoadedComponent records keyed by ext:<bundle>:<Class>@<slot>. Two paths in:

  - load_extension(root): one manifest, one Bundle, registered at @official.
    Re-checks multi-bundle at runtime (defense-in-depth vs. the schema).
  - discover_inline_bundles(paths): each subfolder of LANGFLOW_COMPONENTS_PATH
    is a Bundle at @extra. Walk order is platform-independent (sorted dirs,
    user-declared path order). First-wins on duplicate names; second emits
    duplicate-inline-bundle warning that names both paths.

Manifest-first precedence helpers (installed_extension_roots,
manifest_owning_distributions, filter_plugin_entry_points) let callers of
the legacy langflow.plugins entry-point loader skip distributions that ship
a manifest, so component entry-points are not double-registered.

New typed error codes: module-import-failed, duplicate-component-name,
duplicate-distribution, duplicate-inline-bundle, inline-bundle-name-invalid.
Each ships with a format branch and a snapshot test.

Tests cover the AC: single-bundle happy path, multi-bundle rejection,
missing/empty/no-Component bundle, duplicate class names, deterministic
walk order, recursive discovery, inline-bundle first-wins + dot-dir skip
+ bundle.json metadata, manifest-first precedence partition, PEP-503
distribution-name canonicalization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lfx): address LE-1015 review feedback

- Drop the unproduced duplicate-distribution error code; LE-1022 will add
  it once startup-time discovery has a place to surface it.  Restores the
  invariant that every code in ERROR_CODES has a producer.
- Clarify that intra-bundle relative imports are NOT supported in v0; only
  absolute references between bundle modules work in this milestone.
- Use strict=False when re-resolving bundle_root in the walker; the path
  was already existence-checked, and a concurrent removal in the narrow
  window should not raise across the loader's public boundary.
- Hoist the json import out of _read_inline_bundle_json's body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(lfx): split extension loader into a small subpackage (LE-1015)

Addresses the file-size feedback from the LE-1015 review.  The single
870-line loader.py becomes a flat package keyed off the four section
banners that already existed inline:

    loader/
      __init__.py     # re-exports the public surface
      _types.py       # SLOT constants, LoadedComponent, LoadResult
      _discovery.py   # filesystem walk + importlib.util orchestration
      _detection.py   # Component subclass identification (MRO heuristic)
      _orchestrator.py # load_extension, discover_inline_bundles
      _plugins.py     # manifest-first precedence over langflow.plugins

Largest file is now _orchestrator.py at 440 LOC (was 870); every file is
well under the 800-LOC project guideline.  No behavior change: the public
import paths from lfx.extension are unchanged, all 38 loader tests still
pass.  ``_canonicalize_distribution`` is now exported as
``canonicalize_distribution`` from ``loader._plugins`` (it's a stable
PEP-503 helper that downstream modules will reach for, so it loses the
private underscore).

The test suite is split to mirror the package:

    tests/unit/extension/loader/
      conftest.py             # shared fixtures, FakeDist, autouse scrub
      test_load_extension.py  # @official slot, identity, failure modes
      test_inline_bundles.py  # LANGFLOW_COMPONENTS_PATH, @extra slot
      test_plugins.py         # manifest-first precedence helpers
      test_types.py           # LoadedComponent, LoadResult, code parity

Each test file imports its own slice of the public API and pulls fixtures
from conftest, so a reader looking at one banner can read it in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(lfx): atomic-swap Bundle reload pipeline + endpoint + CLI (LE-1018)

Five-stage reload (parallel staging load -> validate -> swap under write
lock -> cleanup -> emit) for installed Bundles in Mode A.  In-flight
flows keep the pre-swap class via existing references; new flows pick up
the post-swap class atomically; concurrent reloads on the same Bundle
are rejected with reload-in-progress.

Adds:

* lfx/extension/registry.py -- BundleRegistry with per-bundle
  reload-in-progress guard and components_index.json writer
* lfx/extension/reload.py -- the five-stage pipeline; events emission
  is stubbed (TODO LE-1017) so the swap mechanics can ship before the
  events service lands
* loader: optional module_namespace param so Stage 1 lands in
  __reload_staging__.<id> instead of the live _lfx_ext.* namespace
* errors: four new typed reload codes (reload-in-progress,
  reload-bundle-not-installed, reload-bundle-name-mismatch,
  reload-source-missing) with branch templates and snapshot tests
* HTTP: POST /api/v1/extensions/{id}/bundles/{name}/reload, gated by
  the existing get_current_active_user dependency, returns 409 with a
  typed body for the in-progress collision case
* CLI: lfx extension reload <id> [--bundle <name>] -- HTTP client
  against the dev server with text/json output and proper exit codes
  (--all is gated until LE-1019 lands the list endpoint)
* tests: 16 reload-pipeline tests covering the AC matrix (rename
  round-trip, broken-bundle isolation, concurrent readers, in-flight
  flow, double-reload guard, bundle-name mismatch) plus 9 CLI client
  tests

Mode A only.  In Mode B/C bundle changes require a Docker image rebuild
and the reload path is not exercised.

The events emission in Stage 5 is intentionally stubbed -- the LE-1017
ticket will swap the body of _emit_bundle_reload_event in one place
without touching the pipeline core.

* feat(frontend): palette Bundle reload action + loading + toasts

Adds the frontend half of the bundle reload flow.  When the Bundle
header is right-clicked or its overflow ("⋮") icon clicked, a Reload
action fires POST /api/v1/extensions/{id}/bundles/{name}/reload and
surfaces the result via the existing alert-store toast system.

What landed (frontend only):

  * src/controllers/API/queries/extensions/ -- typed wire-format models
    (ReloadBundleResponse, ExtensionErrorPayload, ReloadInProgressDetail)
    and the useReloadBundle mutation hook.  The hook unwraps the 409
    `reload-in-progress` detail into a stable, parseable Error message so
    the UI can branch without reading status codes.
  * components/bundleHeaderActions.tsx -- new Select-based overflow menu
    next to the Bundle header chevron.  Three toast paths: success
    (green, with components +/- delta), structural failure (red, with
    typed errors and inline hints), reload-in-progress (notice).
    Loading state swaps the kebab icon for a spinning Loader2 while
    the request is in flight.  Renders nothing when no extension_id is
    on the bundle, so the static SIDEBAR_BUNDLES list is unaffected.
  * components/bundleItems.tsx -- wires the new actions in next to the
    chevron, plus a context-menu (right-click) capture that opens the
    same overflow trigger so keyboard / mouse / right-click all share
    one source of truth.
  * types/index.ts -- BundleItemProps.item gains an optional
    extension_id; took the opportunity to extract the SidebarBundle
    interface and tighten three pre-existing `any` types.
  * customization/feature-flags.ts -- ENABLE_EXTENSION_RELOAD gate, off
    by default until the bundle-list endpoint that populates extension_id
    per bundle ships.
  * controllers/API/helpers/constants.ts -- EXTENSIONS URL constant.
  * Tests: 7 component tests + 3 mutation-hook tests, all green.  Total
    sidebar + extensions test count: 479 passing.

Deferred:

  * Event-pipeline subscription is left as an inline TODO.  The mutation
    response carries enough information to drive the toasts on its own
    today; once the events service lands the toast wiring will move to
    a `bundle_reloaded` / `bundle_reload_failed` listener so multi-tab
    and multi-worker swaps surface exactly once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: End to end bundle installation

* fix: Review comments addressed

* Update component_index.json

* Update component_index.json

* Update router.py

* fix(docker): copy src/bundles before uv sync so workspace bundles resolve

Each directory under ``src/bundles`` is a uv workspace member referenced by
``langflow-base`` (and the root project) as a path dependency.  The Docker
builders ran ``uv sync --no-install-project`` after copying only the
top-level pyproject.toml files, so resolution failed with
``Distribution not found at: file:///app/src/bundles/<name>``.

Copying the whole ``src/bundles`` tree (rather than enumerating each
bundle) means a new bundle dropped under that dir does not require a
Dockerfile edit.  The full ``./src`` copy a few lines later produces the
same final layer either way; this earlier copy just unblocks the
dependency-resolution sync.

Touched all builders that run a workspace-resolving uv sync:

  * docker/build_and_push.Dockerfile
  * docker/build_and_push_base.Dockerfile
  * docker/build_and_push_ep.Dockerfile
  * docker/build_and_push_with_extras.Dockerfile
  * docker/dev.Dockerfile (bind-mount instead of COPY)

* Revert "fix(docker): copy src/bundles before uv sync so workspace bundles resolve"

This reverts commit 5aa008a3cd.

* feat: DuckDuckGo as Extension in new Bundle System (#13044)

* feat: DuckDuckGo Extension for bundles

* fix ruff errors

* Update test_pilot_duckduckgo_upgrade.py

* test(lfx): handle non-empty canonical migration table + editable installs

- test_path_override_bypasses_cache: mirror the cached canonical table
  into the temp file instead of asserting it's empty.  Drift in
  migration_table.json (the duckduckgo entries shipped with the B1
  pilot) no longer breaks the cache-bypass invariant.

- test_lfx_duckduckgo_ships_manifest: editable installs (pip install -e)
  surface only dist-info entries in dist.files, so we cannot use that
  path to find extension.json in the workspace venv.  Detect editable
  mode via direct_url.json's PEP 660 marker and fall through to walking
  the source tree; non-editable wheel installs still exercise the
  dist.files path the loader uses at runtime.

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* fix(docker): copy src/bundles before uv sync so workspace bundles resolve

Each directory under ``src/bundles`` is a uv workspace member referenced by
``langflow-base`` (and the root project) as a path dependency.  The Docker
builders ran ``uv sync --no-install-project`` after copying only the
top-level pyproject.toml files, so resolution failed with
``Distribution not found at: file:///app/src/bundles/<name>``.

Copying the whole ``src/bundles`` tree (rather than enumerating each
bundle) means a new bundle dropped under that dir does not require a
Dockerfile edit.  The full ``./src`` copy a few lines later produces the
same final layer either way; this earlier copy just unblocks the
dependency-resolution sync.

Touched all builders that run a workspace-resolving uv sync:

  * docker/build_and_push.Dockerfile
  * docker/build_and_push_base.Dockerfile
  * docker/build_and_push_ep.Dockerfile
  * docker/build_and_push_with_extras.Dockerfile
  * docker/dev.Dockerfile (bind-mount instead of COPY)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix: Review sweep

* fix: More review comments addressed

* Ruff check

* docs: add bundle porting guide

Step-by-step recipe for extracting a provider package from the in-tree
``src/lfx/src/lfx/components/<provider>/`` directory into a standalone
Extension Bundle distribution under ``src/bundles/<provider>/``.

The DuckDuckGo bundle is the reference; every section maps to a single
copy-pasteable change and a verification command.

Doc surfaces a forthcoming ``scripts/migrate/port_bundle.py`` automation
helper for the mechanical bits; that script lands on the next branch
together with the second-pilot port that validates the recipe end-to-end.

* fix(extension): accept Output(method=...) in build-method validator

The validator's static AST check looked for a literal ``build`` method
on every Component subclass and emitted ``build-method-missing`` when
absent.  Real Langflow components -- including the production
``DuckDuckGoSearchComponent`` and the in-tree ``ArXivComponent`` -- do
not declare a literal ``build``; they declare entry-points
declaratively via ``outputs = [Output(name=..., method=<name>)]`` and
the named method is what gets called.  Result: every clean port hit a
spurious ``build-method-missing`` error from ``lfx extension validate``.

Fix: extend ``_has_build_method`` to also accept any class that names a
method via ``Output(method="X")`` AND defines that method in the class
body.  The negative case ("Output(method=...) without a matching def")
still fires -- a typo'd method name would crash at runtime, so the
static check should keep flagging it.

Two new tests in ``test_validate.py`` lock the contract: positive case
mirroring duckduckgo / arxiv, negative case for typo'd method names.
All 26 validator tests pass.

PORTING.md updates fall out of running the recipe live against the
duckduckgo bundle on this branch:

  * Validate path is ``src/bundles/<bundle>/src/lfx_<bundle>``, not the
    bundle root (the manifest lives next to ``__init__.py``).
  * Index regen needs ``LFX_DEV=1`` to skip the prebuilt-index fast path
    and uses ``scripts/build_component_index.py``.
  * Drop the migration-table JSON from the ruff invocation (ruff treats
    it as Python and complains about the top-level expression).

* fix: Delete outdated components

* fix(palette): wire reload kebab via DropdownMenu, not Select

The reload kebab on the palette Bundle header was using Radix ``Select``
to back its overflow menu.  ``Select`` is for picking a value, not for
firing an action: ``onValueChange`` is gated by value-equality (so a
re-click of the only item is a no-op), and the popover-portal click
semantics interact poorly with the parent disclosure-trigger button.
The combined effect: clicking ⋮ → Reload opened the popover and closed
it, but never fired the network request, with no console error to point
at.

Switch to ``DropdownMenu`` (purpose-built action menu).
``DropdownMenuItem`` exposes ``onSelect`` which fires on every
activation -- the same callback path keyboard navigation uses -- so
clicking Reload reliably invokes the mutation.

Test mocks updated to drive the DropdownMenu primitives instead of
Select; all 8 bundleHeaderActions tests + the broader 437-test sidebar
suite still pass.

* fix(extension): route ddgs through the lockfile + drop bogus list endpoint hint

Two reviewer findings, both about reproducibility / correctness of
operator-facing surfaces.

[P2] ``docker/build_and_push_base.Dockerfile`` previously installed
``ddgs`` via an unpinned ``uv pip install ddgs`` after the workspace
sync.  That made the base image non-reproducible: a future ``ddgs``
release would silently drift from the tested lock state on every
rebuild.

Fix: route ddgs through the locked sync.

  * Restore the ``duckduckgo`` extra in ``src/backend/base/pyproject.toml``
    (``ddgs>=9.0.0``) as an internal "image-build sidecar".  Public
    consumers still install ``lfx-duckduckgo`` directly; the extra
    exists only to keep ``ddgs`` resolved in the lockfile.
  * The Dockerfile now passes ``--extra duckduckgo`` to ``uv sync
    --frozen``, picking up the locked ``ddgs==9.14.1``.
  * The follow-on bundle install keeps ``--no-deps`` so it does not
    duplicate the now-locked ``ddgs`` / ``lfx`` / ``langchain-community``.

[P3] ``langflow/api/v1/extensions.py`` returned a fix hint that pointed
operators at ``GET /api/v1/extensions`` -- a route that does not exist
in this PR (it lands with the LE-1019 list endpoint).  Replaced with a
reference to ``lfx extension list``, which is shipped here.

* fix(compat): bridge langflow.components.* dynamically via meta path finder

Commit 45552cd1df ("fix: Delete outdated components") removed the
physical shim files under ``src/backend/base/langflow/components/``
that forwarded saved-flow imports like
``from langflow.components.processing.converter import convert_to_dataframe``
or ``import langflow.components.knowledge_bases.retrieval`` to their
new ``lfx.components.*`` homes.  Without those shims, dotted imports
into ``langflow.components.<sub>.<leaf>`` failed at flow-load time --
the existing ``LangflowCompatibilityModule`` registers ``langflow.components``
itself in ``sys.modules`` but does not bridge submodules, so Python's
import machinery falls through to the now-empty langflow.components
directory and raises ``ModuleNotFoundError``.

Replace the deleted physical-shim stack with a single ``MetaPathFinder``
in ``langflow/__init__.py`` that dynamically resolves every
``langflow.components.<rest>`` import to ``lfx.components.<rest>`` and
registers the loaded lfx module in ``sys.modules`` under both names.
The langflow- and lfx-prefixed imports share a single underlying module
object, so class identity is preserved across the bridge -- ``isinstance``
checks against types resolved through either path keep working.

The finder also carries a small first-segment override map for the few
subpackages whose name diverged during the move; the only entry today is
``knowledge_bases`` -> ``files_and_knowledge``, matching the deleted
shim's intent.

Why a meta finder instead of restoring the physical shim files (option
1 from the scope analysis): the meta finder scales without per-bundle
maintenance.  Every future bundle extraction landed under ``lfx.components``
becomes reachable via the legacy ``langflow.components.<bundle>`` path
immediately; nobody has to remember to add a parallel langflow shim.

Six new unit tests in ``test_langflow_components_compat_shim.py`` lock
the contract: dotted submodule resolution, helpers re-export, the
``knowledge_bases`` override, class identity preservation, top-level
aliasing, and the "arbitrary extracted bundle" case that prevents
regression for future ports.

Verified pre-existing tests:
  * 17 dynamic-import integration tests pass.
  * The reload-route-guard suite still passes.
  * The Research Translation Loop starter-project test (which loads
    ArXivComponent) passes.

* Update index.tsx

* fix: Review pass on delivery

* fix: Second review sweep, bare names

* fix: Third review sweep

* Resolve dotted imports and attribute chains

Record import shape and flatten attribute chains so router references can be resolved across dotted imports and re-exports. Added ImportTarget dataclass, _attribute_chain helper, and switched IncludeCall/DecoratorRef to store attribute tuples. parse_file now records ImportTarget entries for imports and captures dotted parent/child chains for include_router and decorators. Replaced _resolve_var with _resolve_chain which handles "from" vs "module" imports, various import shapes (including import x.y and aliased imports), and prevents infinite recursion on re-export cycles.

* One more sweep

* Add seed-directory extension loading and docs

Introduce filesystem "seed directory" extension support and authoring docs. Adds three documentation pages (quickstart, manifest reference, author guide) and wires them into the docs sidebar. Implement load_seed_extensions to discover/load bundles from $LANGFLOW_SEED_DIR (default /opt/langflow/bundles), export it from loader/__init__ and extension package, and integrate it into the components import pipeline. Handle installed-vs-seed shadowing by preferring installed distributions and appending a typed seed-bundle-shadowed ExtensionError; add the corresponding error code and message. Update schema doc link and add unit/integration tests to cover seed loading, determinism, shadowing behavior, and migration-target resolution.

* Fix claims

* Fix docasaurus build

* Clean up the manual checklist

* Update test_pilot_duckduckgo_upgrade.py

* fix: Some wording issues with dogfooding

* fix: Comments addressed

* Update port_bundle.py

* Update component_index.json

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* fix: pytest testpaths glob and accurate scaffold output in port_bundle

testpaths now uses ``src/bundles/*/tests`` so future bundle tests are
picked up automatically without hardcoding each bundle at the root.
pytest expands the glob via ``glob.iglob(..., recursive=True)``.

Rewrote port_bundle.py's ``_render_migration_entries`` and
``_render_test_scaffold`` to match the actual conventions:

* Migration entries now use ``bare_class_name``/``import_path``/
  ``legacy_slot`` + ``target`` + ``added_in`` (the keys
  ``lfx.extension.migration.loader`` actually reads) instead of the
  invented ``from``/``to``/``release`` shape.
* Test scaffold now imports ``load_migration_table`` and
  ``migrate_flow_payload`` -- the APIs the real tests use -- instead of
  a fictitious ``resolve_legacy_id``.  Includes the ``migration_table``
  fixture, ``_saved_flow``/``_saved_flow_node`` helpers, and the
  distribution-importable + manifest-shipped checks that mirror
  ``test_pilot_duckduckgo_upgrade.py``.

Verified by rendering the scaffold for duckduckgo with synthetic plan
data, dropping it into ``src/lfx/tests/integration/extension/``, and
running pytest -- ``3 passed, 2 skipped`` (skips are the wheel-only
checks, by design when run inside the lfx isolation venv).

* [autofix.ci] apply automated fixes

* fix: Review comments

* Update test_discovery.py

* feat: Port Arxiv to the Extension Framework (#13047)

* feat(bundles): port arxiv as the second-pilot Bundle + porting helper

Validates ``src/bundles/PORTING.md`` end-to-end by following its recipe
against a clean candidate (``ArXivComponent``: no third-party runtime
deps, no langflow-base extra, no deactivated duplicate).  Touchpoints
exercised:

  * Bundle skeleton at ``src/bundles/arxiv/`` mirroring duckduckgo.
  * In-tree provider directory removed from ``src/lfx/src/lfx/components/``
    along with its three references in ``components/__init__.py``.
  * Workspace wiring: dep, ``[tool.uv.sources]``, ``[tool.uv.workspace]``
    members, lockfile.
  * Migration table: bare-name + two import-path forms + legacy_slot
    entry.
  * Component index regenerated via ``LFX_DEV=1`` (forces dynamic
    discovery; without it the script reproduces stale entries).
  * Integration test ``test_pilot_arxiv_upgrade.py`` mirroring the
    duckduckgo pilot suite; 5 tests pass against the workspace install.

PORTING.md updates fall out of running the recipe live:
  * Validate path is ``src/bundles/<bundle>/src/lfx_<bundle>``, not the
    bundle root (the manifest lives next to ``__init__.py``).
  * Index regen needs ``LFX_DEV=1`` to skip the prebuilt-index fast path.
  * Drop the migration-table JSON from the ruff invocation (ruff treats
    it as Python and complains about the top-level expression).

``scripts/migrate/port_bundle.py`` is the mechanical helper referenced
from § Automation: stdlib-only, dry-run by default, refuses on invalid
input, and intentionally leaves migration-table edits + integration-test
authoring to a human (release version + bare-name uniqueness require
judgement).  Three guard rails verified: invalid bundle name,
existing-target-bundle, missing in-tree provider.

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* Update Research Translation Loop.json

* Update .secrets.baseline

* fix: Move bundle test for arxiv

* Update PORTING.md

* Update component_index.json

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* Update component_index.json

* [autofix.ci] apply automated fixes

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* [autofix.ci] apply automated fixes

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* Update component_index.json

* Update component_index.json

* Update component_index.json

* fix: empty cache dict on reload

* Lint fixes

* Update test_components_cache_integration.py

* Update test_reload.py

* Hardening pass

* Update test_init_template.py

* fix: resolve cross-source bundle-name shadowing before registry population

The reload pipeline reads ``live.source_path`` from the registry on every
call.  Previously, the registry-population loop in
``import_extension_components`` only special-cased installed-shadows-seed;
for every other pair (seed/dev, seed/inline, dev/inline,
installed/dev, installed/inline) it silently overwrote earlier records
via last-wins iteration order.  A stale dev registration whose
``source_path`` pointed at a different filesystem location could clobber
the seed record's ``source_path``; reload would then walk the dev path
while the operator edited the seed copy on disk -- producing 200 OK with
empty deltas on every reload, including for syntactically broken edits.

Generalize the dedup pass to every (earlier, later) pair using the
explicit precedence ``installed > seed > dev > inline``, applied before
both registry population AND palette template construction so the two
read from the same winning source.  Add a new generic ``bundle-shadowed``
typed error code for the pairs the existing ``seed-bundle-shadowed`` did
not cover; keep ``seed-bundle-shadowed`` for the documented installed-
over-seed pair so existing CLI exit-code logic and snapshot tests stay
intact.  Both codes are warn-only in ``lfx extension list``.

Also add an INFO log line in reload Stage 1 with the resolved
``source_path`` so the next "200 OK with empty deltas" repro can be
triaged from the server log alone -- if the path logged is not the path
the operator was editing, this dedup is what to look at.

Includes a regression test
(``test_seed_bundle_shadows_dev_emits_generic_bundle_shadowed``) that
asserts both halves: seed wins in the BundleRegistry AND the registry's
``source_path`` is the seed path, not the stale dev path.

* Update component_index.json

* fix: widen lfx-* bundles' requires-python to <3.15 to match langflow root

The two pilot bundles (``lfx-arxiv``, ``lfx-duckduckgo``) were scaffolded
by ``scripts/migrate/port_bundle.py`` whose template hard-coded
``requires-python = ">=3.10,<3.14"``.  Because both bundles are uv
workspace members of the langflow root, that cap leaked into every
workspace-level resolve: ``cd src/lfx && uv sync`` (the path
``make lfx_tests`` takes) refuses to pick a Python 3.14 interpreter even
though lfx itself, langflow, and langflow-base all advertise
``>=3.10,<3.15``.  Hosts with 3.14 installed see:

    error: The requested interpreter resolved to Python 3.14.5, which
    is incompatible with the project's Python requirement: `>=3.10, <3.14`.

Fix at the source so future ``port_bundle.py`` runs do not regress this:
template emits ``<3.15``, and the two existing emitted pyprojects are
bumped in lockstep.  ``uv.lock`` regenerates with the wider range.

Verified ``make lfx_tests`` resolves cleanly on a host with both 3.13.12
and 3.14.5 installed; the focused extension slice runs to completion.

* Update __init__.py

* Update src/lfx/tests/unit/extension/migration/test_rewrite.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/extension/loader/_plugins.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/docs/Deployment/deployment-extensions-production.mdx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/lfx/src/lfx/extension/loader/_orchestrator.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/docs/Develop/extensions-author-guide.mdx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update docs/docs/Develop/extensions-manifest.mdx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: Coderabbit review suggestions

* Template updates

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* Tweaks to bundle hot reload

* [autofix.ci] apply automated fixes

* chore: Address review comments by @Cristhianzl

* [autofix.ci] apply automated fixes

* Update BUNDLE_API.md

* Update component_index.json

* Update component_index.json

* Update Structured Data Analysis Agent.json

* fix: Next round of review comments

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-18 17:36:56 +00:00
d017e156cd fix: add stream toggle back to agent (#13155)
* add stream toggle back to agent

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* ci: stop transient Windows npm/cache flakes from failing frontend tests

The nightly frontend job fails almost every day on a single Windows
Playwright shard, never on actual test logic. With fail-fast:false over
~70 Windows shards each making several cache/artifact-service calls, a
transient GitHub Actions cache/artifact hiccup on any one shard flips
needs.setup-and-test.result to failure and reds the whole nightly
(observed steps: Setup Node.js Environment, Cache Playwright Browsers,
Upload Playwright Coverage Artifact).

- Decouple npm caching from actions/setup-node (its internal cache step
  can't be made non-fatal) and replace it with a standalone
  actions/cache@v5 step marked continue-on-error.
- Mark Cache Playwright Browsers and the artifact-upload steps
  continue-on-error so an infra blip can no longer fail a test shard.

Only a real test/browser failure can fail a shard now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

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

* [autofix.ci] apply automated fixes

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-18 13:30:08 +00:00
bd35587957 chore: Add non-blocking test coverage advisor for PRs (#13148)
add checker to verify tests non blocker
2026-05-15 17:36:17 +00:00
66912d4a53 Merge main source updates into release-1.10.0
# Conflicts:
#	docker/build_and_push.Dockerfile
#	docker/build_and_push_backend.Dockerfile
#	docker/build_and_push_base.Dockerfile
#	docker/build_and_push_ep.Dockerfile
#	docker/build_and_push_with_extras.Dockerfile
2026-05-15 09:47:56 -07:00
53c62bceb0 chore: Block hardcoded Tailwind palette via Biome lint (#13125)
* add biome checker for hardcoded pallete

* add biome checker on ci

* [autofix.ci] apply automated fixes

* lint-js fix

* improve CI job

* biome fixes

* unblock CI on biome job

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-15 12:08:51 +00:00
5e9f0c37bc Merge branch 'release-1.9.3' into release-1.10.0
# Conflicts:
#	.secrets.baseline
#	docs/package.json
#	pyproject.toml
#	src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json
#	src/backend/base/pyproject.toml
#	src/backend/tests/unit/components/files_and_knowledge/test_retrieval.py
#	src/frontend/package-lock.json
#	src/frontend/package.json
#	src/frontend/src/components/core/parameterRenderComponent/components/modelInputComponent/__tests__/ModelInputComponent.test.tsx
#	src/frontend/src/components/core/parameterRenderComponent/components/modelInputComponent/index.tsx
#	src/frontend/src/modals/modelProviderModal/hooks/useProviderConfiguration.ts
#	src/lfx/pyproject.toml
#	src/lfx/src/lfx/_assets/component_index.json
#	src/sdk/pyproject.toml
#	uv.lock
2026-05-14 21:20:42 -07:00
b695f97cd3 fix: More python 3.14 compatibility cleanup 2026-05-14 17:49:56 -07:00
883872f95f Update release.yml 2026-05-14 14:39:20 -07:00
a80cf1c649 chore: support Python 3.14 (#13085)
* feat: upgrade Docker images to Python 3.14 (experimental)

- 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

- 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

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

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

- 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>
(cherry picked from commit 1955f8fae5)
2026-05-13 22:23:39 +00:00
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
4f9e0da208 feat: regression bucket (#12948)
* initial-plan

* create-initial-yaml-files

* add-info-to-bug-report

* add-regression-stub-workflow

* use-create-comment-instead-of-github-script
2026-05-12 14:13:46 +00:00
a3723911e6 fix: Restore resolved release-branch ref in nightly build (#13064) 2026-05-11 12:14:54 -03:00
6e4475c139 fix: Unblock nightly install on macOS x86_64 due OpenDsStar (#13061) 2026-05-11 12:11:44 -03:00
db5865d674 fix(tests): Stabilize Windows-only Playwright e2e failures (#13062) 2026-05-11 11:41:20 -03:00
de148c70b7 chore: bump backend unit test timeout from 20 to 30 minutes 2026-05-09 11:15:10 -07:00
a4d875a9a1 chore: bump backend unit test timeout from 20 to 30 minutes 2026-05-09 11:14:03 -07:00
94981c443d fix: update Docker base images to Trixie and force pull latest images in nightly builds (#13015)
- Update builder stage: bookworm-slim → trixie-slim (5 Dockerfiles)
- Update runtime stage: python:3.12.13-slim-trixie → python:3.12-slim-trixie
- Add pull: true to 6 Docker build steps in nightly workflow
- Forces pulling latest base images instead of using cached layers

This resolves CVE vulnerabilities in Docker images by ensuring we use
the latest Debian Trixie base images instead of cached Bookworm layers.
2026-05-06 23:26:11 +00:00
af8871cf61 fix: force pull latest base images in Docker nightly builds (#13014)
- Add pull: true to all docker build steps in nightly workflow
- Forces Docker to pull latest python:3.12-slim-trixie instead of using cached layers
- Ensures Debian Trixie base images are used instead of cached Bookworm layers
- Resolves issue where Docker layer caching prevented CVE fixes from taking effect
2026-05-06 21:26:57 +00:00
f08885f20c ci: block PR titles ending in ellipsis (#13004)
Adds a validate-pr-title job to ci.yml that fails on PRs whose title
ends with '...' or '…'. Wired into the CI Success aggregate so it acts
as a merge gate. Skipped on merge_group, workflow_dispatch, and
workflow_call where no PR title is available.
2026-05-06 18:04:37 +00:00
60627bec3a chore: Add DESIGN.md for Langflow's visual design system (#12830)
* docs: Add DESIGN.md specification for Langflow's visual design system

Machine-readable design tokens (YAML frontmatter) paired with
human-readable design rationale following the open-source DESIGN.md
format from Google Labs. Intended for AI agents generating UI that
must match Langflow's visual identity.

Covers 129 color tokens, 15 typography scales, 50 component
definitions, spacing/rounding/elevation systems, dark mode strategy,
and the 14-hue data type color system used for node port encoding.

* ci: Add DESIGN.md lint workflow

Runs `@google/design.md lint` against DESIGN.md on pull requests that
modify the file. Catches spec violations, broken token references, and
WCAG AA contrast failures before merge. CLI version is pinned via env
var so format spec changes require a deliberate bump.

* ci: Integrate DESIGN.md lint into CI Success pipeline

Replaces the standalone workflow with a job inside ci.yml so it gates
the "CI Success" aggregate check. Path-filter skips the job on PRs
that don't touch DESIGN.md, so cost is near-zero for normal PRs.
2026-05-05 18:51:04 +00:00
9b11f5a383 feat: backend i18n — serve translated component metadata via Accept-Language (#12517)
* feat: add IBM Globalization Pipeline integration and i18n setup

- Add GP REST API client with GP-HMAC authentication (scripts/gp/gp_client.py)
- Add upload/download scripts for syncing strings with GP
- Set up i18next + react-i18next with 7 language support (en, fr, ja, es, de, pt, zh-Hans)
- Add 48 UI strings in flat dot notation to en.json (from alerts_constants)
- Add GitHub Actions for automated upload on en.json changes and daily translation download

* [autofix.ci] apply automated fixes

* fix: resolve ruff linting issues in GP scripts

- Fix quote style, import ordering, docstring format
- Use Path instead of os.makedirs/open/os.path.join
- Add missing timeouts to requests calls
- Move noqa comments to correct lines for S501

* feat: migrate all frontend UI strings to i18n for GP localization

- Replace all hardcoded user-facing strings with t() calls across 80+ components, pages, modals, and utilities
- Add 326 translation keys to en.json covering errors, alerts, dialogs, chat, flow, settings, store, table, output, nav, auth, and more
- Add translated locale files for fr, es, de, pt, ja, zh-Hans (downloaded from Globalization Pipeline)
- Migrate alerts_constants.tsx and constants.ts string usages to react-i18next
- Support non-React files (stores, utils) via i18n.t() direct calls

* feat: migrate sidebar strings to i18n (Phase 4)

- Add 56 new translation keys for sidebar categories, nav items, labels, buttons, and empty states
- Convert SIDEBAR_CATEGORIES display_names to i18n key references in styleUtils.ts
- Update 13 sidebar components to use t() for all user-facing strings
- Refresh all 6 locale files from GP (382 total keys)
- Add BACKEND_STRINGS_STRATEGY.md documenting plan for component display_name i18n

* [autofix.ci] apply automated fixes

* fix: add react-i18next mock and fix sidebar nav tests for i18n migration

- Add global react-i18next mock in jest.setup.js using en.json lookups so
  t(key) returns English strings instead of raw keys in test environment
- Fix sidebarSegmentedNav tests: update NAV_ITEMS expectations to use i18n
  keys, and use en.json lookups for tooltip/accessibility label assertions

* feat: add LanguageSelector dropdown to globalization pipeline

Cherry-picked frontend-only changes from feat/gp-backend-i18n (6ab21559db, 3e274a84e3):
- Add LanguageSelector component with dropdown to switch UI language
- Wire LanguageSelector into AccountMenu header
- Persist language preference to localStorage in i18n.ts
- Send Accept-Language header via useCustomApiHeaders (reactive via useTranslation)

No backend files included.

* [autofix.ci] apply automated fixes

* ci: add GP_TEST environment to gp-download and gp-upload workflows

* temp: add feature branch push trigger for workflow testing

* temp: trigger gp-upload workflow test

* fix: use correct GP_test environment name in workflows

* fix: use vars for GP_INSTANCE and GP_BUNDLE

* [autofix.ci] apply automated fixes

* fix: correct environment name to GP-test (hyphen not underscore)

* chore: update translations from Globalization Pipeline [skip ci]

* [autofix.ci] apply automated fixes

* chore: update translations from Globalization Pipeline [skip ci]

* temp: trigger gp-upload workflow test

* ci: add download-gp-bundle job to nightly build pipeline

Downloads frontend translations from Globalization Pipeline after
create-nightly-tag and before frontend tests run, ensuring tests
always validate the latest translated locale files.

- Frontend tests (Linux/Windows) now depend on download-gp-bundle
- Backend unit tests are unaffected (no frontend locale dependency)
- GP failure blocks release-nightly-build (same severity as linux tests)

* chore: remove temporary dev files from gp scripts directory

Remove BACKEND_STRINGS_STRATEGY.md (internal planning doc), test_auth.py
(GP auth debugging script), and en.json (dev-time string snapshot).
Also add venv/ to .gitignore to prevent accidental commits of the local virtualenv.

* [autofix.ci] apply automated fixes

* chore: update translations from Globalization Pipeline [skip ci]

* [autofix.ci] apply automated fixes

* chore: update translations from Globalization Pipeline [skip ci]

* feat(i18n): move language selector from account menu to Settings > General

- Add LanguageForm card component in GeneralPage following the existing
  Card pattern (ProfilePictureForm), with immediate language switching
- English marked as "(Recommended)" via i18n key
- Remove Language row from AccountMenu dropdown
- Add settings.languageTitle/Description/Recommended keys to en.json
  for upload to Globalization Pipeline

* feat(i18n): refactor language constants and improve GP pipeline

- Extract SUPPORTED_LANGUAGES into src/frontend/src/constants/languages.ts
- Update LanguageForm to import from shared constants, add aria-label
- Add settings.languageSelectAriaLabel key to en.json
- Update LanguageSelector component to use shared SUPPORTED_LANGUAGES
- Fix gp_client and download_translations scripts
- Update GP upload/download/nightly GitHub Actions workflows
- Add GP script tests

* [autofix.ci] apply automated fixes

* ci(gp): trigger upload on release branches, download via dispatch only

- Upload now triggers on push to release-* branches (instead of main)
  so translators get strings once they're finalized on the release branch
- Download removes the daily cron schedule; workflow_dispatch only
  since the nightly build already handles scheduled runs

* chore: update translations from Globalization Pipeline

* fix(i18n): address PR review comments — tests, nightly build decoupling, GP test workflow

- Add pytest tests for gp_client.py (HMAC auth, HTTP errors, timeout)
- Add pytest tests for upload_strings.py and download_translations.py
- Add conftest.py to set sys.path so GP scripts can be imported from repo root
- Add scripts/gp/__init__.py to make gp a proper package for test imports
- Add gp-test.yml workflow to run GP script tests on PRs touching scripts/gp/**
- Decouple download-gp-bundle from nightly build chain:
  - Add continue-on-error: true so GP failure never blocks the build
  - Remove download-gp-bundle from needs/if of frontend-tests-linux,
    frontend-tests-windows, and release-nightly-build
  - Add warning annotation step that fires on GP download failure

* feat(i18n): lazy-load non-English locale files via Vite dynamic imports

- Remove 6 static locale imports from i18n.ts; only en.json is bundled
- Export loadLanguage() which dynamically imports a locale chunk on demand
  and caches it via i18n.hasResourceBundle (no-op on repeat calls)
- Update index.tsx to await loadLanguage(detectedLang) before rendering,
  preventing an English flash for non-English users
- Make handleChange async in LanguageForm and LanguageSelector to await
  loadLanguage before calling i18n.changeLanguage
- Add i18n.test.ts covering loadLanguage: en no-op, new language load,
  cache hit, and multiple independent languages
- Update LanguageForm.test.tsx to mock @/i18n and assert loadLanguage
  is called before changeLanguage on language switch

* [autofix.ci] apply automated fixes

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

* fix: add ruff ignores for scripts/gp/tests

- Add per-file ignores for scripts/gp/tests/* to fix CI failures
- Ignore S101 (assert usage), TRY003, EM101 for test files
- Follows existing pattern for other test directories

* [autofix.ci] apply automated fixes

* fix(ci): run GP translation download before nightly tag creation

Previously download-gp-bundle ran after create-nightly-tag, so fresh
translations were committed to the branch after the tag was already
created. The nightly Docker image builds from the tag, meaning it never
included the day's translations.

Fix: move download-gp-bundle to run before create-nightly-tag so the
tag is created on a commit that already includes the latest translations.

* fix(ui): replace native select with Radix UI Select in LanguageForm

Swaps the native <select> element for the existing Radix UI Select
component to get consistent padding on both sides of the chevron,
removing reliance on browser-rendered chevron positioning.

* feat: backend i18n — serve translated component metadata via Accept-Language

- Add locale middleware (set_locale) in main.py to parse Accept-Language header
- Add translate_component_dict() in utils/i18n.py to substitute display_names
- Add backend locale files (en/fr/es/de/pt/ja/zh-Hans) with 4,748 keys
- Add GP scripts: extract_backend_strings.py, upload_backend_strings.py, download_backend_translations.py
- Add CI workflows: gp-backend-check.yml (PR validation + auto-commit), gp-backend-upload.yml (release branch upload)
- /api/v1/all returns translated component metadata for non-English locales

* chore: auto-regenerate backend locales/en.json [skip ci]

* chore: auto-regenerate backend locales/en.json [skip ci]

* refactor: simplify GP backend workflows to remove duplication

- gp-backend-check: drop redundant --check job, keep only auto-commit
- gp-backend-upload: remove extraction/commit steps, trigger on en.json
  path change, and add latest-release-branch guard (mirrors gp-upload.yml)

* feat: add nightly backend translation download job to gp-download workflow

Mirrors the existing frontend download job — resolves latest release branch,
runs download_backend_translations.py, and opens an auto-merge PR.

* [autofix.ci] apply automated fixes

* fix: skip backend translation download on nightly schedule

Backend download only runs on workflow_dispatch — the nightly cron is
for the frontend build pipeline only.

* fix: remove backend GP download from nightly build workflow

Backend translations are now downloaded via workflow_dispatch in
gp-download.yml, not as part of the nightly build pipeline.

* fix: remove frontend GP download from nightly build workflow

Frontend translations are now downloaded via nightly cron in
gp-download.yml, not as part of the nightly build pipeline.

* fix: remove en.json from check workflow trigger paths

The extraction script only reads from lfx.components — en.json is never
edited directly, so triggering on it was redundant and would silently
overwrite any intentional manual edits.

* fix: mock lfx.components in sys.modules for collect_strings test

The test was failing because collect_strings() imports lfx.components
before reaching the pkgutil/importlib mocks. Injecting fake modules into
sys.modules lets the import succeed so the mocks can take over.

* fix: resolve ruff violations in GP scripts and main.py

- Use .values() instead of .items() when attr_name is unused (B007/PERF102)
- Split long print f-string to stay under 120 chars (E501)
- Add blank line in docstring between summary and description (D205)
- Use ternary operator for locale assignment in main.py (SIM108)

* feat(i18n): sync canvas node translations on language change

Add syncNodeTranslations() to flowStore that patches translatable
metadata (display_name, description, template field display_names,
output display_names) on all canvas nodes when the types catalog is
refreshed with a new language. Field values are never touched. Nodes
whose type is absent from the catalog (custom-code, group nodes) are
skipped.

Called from two places to handle the race between flow loading and
types fetching:
- use-get-types.ts after setTypes(data) — covers the case where nodes
  are already on canvas when translations arrive
- resetFlow — covers the case where translations are already loaded
  when the flow mounts

* feat(i18n): translate Templates modal and starter flow metadata

## Backend

- Extend extraction script (`scripts/gp/extract_backend_strings.py`) to
  auto-discover all `initial_setup/starter_projects/*.json` files and emit
  `starter_flows.{safe_key}.name` / `starter_flows.{safe_key}.description`
  keys — no manual work needed when new starter flows are added.

- Add `translate_starter_flows()` to `utils/i18n.py`, mirroring the
  existing `translate_component_dict()` pattern. Derives a stable
  `name_key` (slug) from the English name and attaches it to each
  `FlowRead` so the frontend can match flows by identity regardless of
  locale.

- Update `FlowRead` model to expose `name_key: str | None`.

- Refactor `read_basic_examples()` endpoint (`api/v1/flows.py`):
  - Accept `Request` parameter to read `request.state.locale` (set by
    existing `set_locale` middleware).
  - Cache the raw `flow_reads` list instead of the compressed response so
    the same DB result can be translated per-request (cheap: ~33 dict
    lookups, no additional DB hit).
  - Call `translate_starter_flows()` on every request so `name_key` is
    always populated.

- Update all 7 backend locale files (en, fr, de, es, ja, pt, zh-Hans)
  with new `starter_flows.*` keys from GP.

## Frontend

- Templates modal (`modals/templatesModal/`):
  - Translate sidebar title, category group labels ("Use Cases",
    "Methodology"), and all nav item labels.
  - Translate "Get started" header and description.
  - Translate card category badges (PROMPTING, RAG, AGENTS).
  - Translate search placeholder and "No templates found" empty state.
  - Translate "Start from scratch" footer and "Blank Flow" button.
  - Use `flow.name_key` (stable slug) for example lookup in
    `GetStartedComponent` so matching is locale-independent.

- Refetch examples on language change: add `i18n.language` to the
  `useGetBasicExamplesQuery` cache key so React Query re-fetches
  translated flow data whenever the UI language switches.

- Add `name_key?: string | null` to `FlowType`.

- Empty state pages (`emptyPage`, `emptyFolder`): translate "Empty
  project", "Start building", description, and "New Flow" button.

- Update all 7 frontend locale files with new `templatesModal.*` and
  `emptyPage.*` keys from GP.

* [autofix.ci] apply automated fixes

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

* feat(i18n): translate MCP Server tab and fix sidebar category truncation

- Translate all strings in McpServerTab, McpFlowsSection, McpAuthSection:
  title, description, guide link, Flows/Tools label + tooltip, Edit Tools
  button, Auth label, None (public), Edit/Add Auth, Auto install tab,
  Loading state, config error messages
- Use stable id/label split for Auto install / JSON tabs so mode
  comparison is locale-independent
- Add truncate to sidebar category label span to prevent long translated
  names (e.g. French "Mannequins et agents") from wrapping to two lines
- Update all 7 frontend locale files with new mcp.* keys from GP

* feat(i18n): translate Knowledge, Files pages and fix sidebar overflow

- Translate Knowledge page (empty state, search, loading, alerts, chunks page)
- Translate My Files page (title, empty state, table columns, upload/delete)
- Fix sidebar category label overflow with min-w-0 truncate on flex spans
- Fix "Discover more components" button text truncation
- Fix empty page CTA button max-width for longer translations
- Remove duplicate language selector from account menu (browser auto-detection already in i18n.ts)
- Upload 471 strings to GP and download translations for fr/ja/es/de/pt/zh-Hans

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

* feat(i18n): translate delete confirmation modal and fix CTA button overflow

- Translate DeleteConfirmationModal (title, body, buttons, "can't be undone")
- Translate flow/folder/component description labels via use-description-modal
- Translate "and its message history" and folder delete note at call sites
- Fix empty page CTA button overflow with whitespace-normal w-auto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): translate Create Knowledge Base modal and flow dropdown menu

* [autofix.ci] apply automated fixes

* fix(i18n): use min-height for Knowledge Base modal to prevent scroll on translated text

* [autofix.ci] apply automated fixes

* feat(i18n): translate StepperModal footer buttons (Next Step, Back, Need Help?)

* feat(i18n): translate model input dropdown (No Models Enabled, Refresh List, Manage Model Providers)

* feat(i18n): translate Model Providers modal (config form, model selection, disconnect warning, multiselect placeholder)

* feat(i18n): translate starter template note/README nodes at serve-time

- Extend extract_backend_strings.py to extract noteNode descriptions as
  template_notes.{flow_key}.{index} keys (64 notes across 32 templates)
- Add translate_flow_notes() to i18n.py to substitute translated markdown
  per-request without mutating the cached template data
- Wire translate_flow_notes() into the /starter-projects/ endpoint using
  the same request.state.locale pattern as the flows endpoint
- Update all 6 backend locale files via GP pipeline (4858 keys total)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): translate canvas note nodes on language change via dedicated endpoint

- Add stamp_note_keys() to i18n.py: stamps i18n_key onto noteNodes of
  starter templates when a user copy is created, enabling stable lookup
- Call stamp_note_keys() in _new_flow() (flows_helpers.py) so keys are
  persisted to DB at copy-creation time
- Add GET /flows/{flow_id}/note_translations endpoint returning
  { node_id: translated_text } for the request locale; falls back to
  positional recompute for flows without stamped keys
- Add useGetNoteTranslationsQuery (React Query) keyed on [flowId, language]
  so it auto-refetches on language change without explicit invalidation
- Apply translations in NoteNode via useEffect on query data
- Rewrite syncNoteTranslations to accept Record<string, string> (node_id map)
- Remove old syncNoteTranslations(FlowType[]) approach and all debug
  console.warn/console.error [i18n-debug] statements
- Suppress i18next Locize promotional console.info by temporarily replacing
  console.info around the synchronous init() call
- Add i18n_key?: string to noteClassType frontend type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): fix Playground/Share button overlap and translate Share button

- Remove fixed w-[7.2rem] from SimpleSidebarTrigger and DisabledButton so
  the Playground button grows to fit translated text in any language
- Add misc.share i18n key and replace hardcoded "Share" string in
  deploy-dropdown with t("misc.share")
- Download GP translations for de/pt/zh-Hans

* feat(i18n): translate input placeholder and info fields for backend components

Extend the backend i18n pipeline to cover placeholder and info (tooltip)
text in addition to display_name and description:

- extract_backend_strings.py: extract placeholder from input fields
- i18n.py (translate_component_dict): translate placeholder and info at
  request time alongside display_name
- Regenerate en.json (6700 keys, +179 placeholders, +info keys)
- Download translated locale files for de, es, fr, ja, pt, zh-Hans

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): translate Share dropdown items (API access, Export, MCP Server, Embed, Shareable Playground)

* fix(i18n): stamp i18n_key in translate_flow_notes and remove dead stamp_note_keys

- Stamp i18n_key onto noteNodes inside translate_flow_notes() using the
  original English name_key so the key travels with the node when a user
  copies a template — fixes translation failure for duplicate copies like
  "Simple Agent (2)" where the frontend sends the translated name
- Remove stamp_note_keys() and its call in _new_flow(): now a no-op since
  i18n_key is always stamped by translate_flow_notes at /basic_examples/
- Remove positional fallback from get_note_translations: only use stamped
  i18n_key; drop unused _safe_flow_key import and note_index counter
- Fix read_basic_examples to pass name_key (original English) instead of
  the already-translated flow.name to translate_flow_notes

* refactor(i18n): bake i18n_key into template noteNodes, remove runtime stamping

Replaces fragile positional indexing with keys baked directly into the source
JSON files, so keys are stable regardless of node order changes.

- scripts/gp/bake_note_keys.py: new script that stamps template_notes.{flow}.{n}
  onto each noteNode missing an i18n_key; idempotent and --dry-run safe
- scripts/gp/extract_backend_strings.py: Tier 4 reads i18n_key from node data
  instead of positional index; warns if any noteNode is missing a key
- utils/i18n.py: translate_flow_notes() simplified — reads baked key, translates,
  no more flow_name param or index counter or stamping logic
- api/v1/flows.py, starter_projects.py: drop flow_name arg from call sites
- test_i18n_note_translation.py: tests updated for key-read behavior
- starter_projects/*.json: 64 noteNodes across 32 templates stamped with i18n_key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci(i18n): auto-bake note keys on template JSON changes in CI

Extends gp-backend-check.yml to trigger on starter_projects/** changes
and runs bake_note_keys.py before extract_backend_strings.py so any new
noteNodes get i18n_key stamped automatically on PR branches.

* refactor(i18n): replace index-based note keys with content-hash keys

bake_note_keys.py now derives i18n_key from sha256[:8] of the
noteNode description instead of a positional counter. Keys are
stable on reorder, auto-rotate when content changes, and never
silently reuse an old index after a note is deleted.

All 64 note keys across 32 starter templates restamped to the
new template_notes.{flow_key}.{hash8} format. Script is idempotent —
running twice on unchanged content produces no changes.

* fix(i18n): thread-safe translation loading and correct falsy key check

- _load_translations(): add double-checked locking with threading.Lock to
  prevent race condition under concurrent requests at cold start
- translate(): use `is not None` instead of truthiness check so empty-string
  translations are not silently dropped in favour of the fallback chain

* feat(i18n): translate Settings page UI strings across multiple sections

- Profile picture chooser: translate People/Space category labels
- MCP Servers page: translate title, description, button, status labels, dropdown items, and all Add MCP Server modal strings
- API Keys page: translate column headers (Name, Key, Created, Last Used, Total Uses) and Create API Key dialog (title, description, labels, buttons, generated key message)
- Shortcuts page: translate all 27 shortcut display names via valueFormatter
- Messages page: translate title and description
- Base modal: translate shared Cancel button

* fix(i18n): replace fixed card heights with min-h to prevent layout issues with longer translations

Cards with h-[84px] would overflow/misalign when translated text wrapped to
multiple lines. Using min-h-[84px] keeps the minimum size while allowing
cards to grow naturally for longer translations.

* feat(i18n): translate SaveChangesModal (unsaved changes popup)

Translates all strings in the unsaved-changes dialog shown when
navigating away from a flow with pending changes:
- title, saving spinner, last saved, exit/save buttons
- unsaved changes warning and auto-saving help link

* chore(i18n): download updated frontend and backend translations from GP

Frontend: 704 strings across fr, ja, es, de, pt, zh-Hans
Backend: updated note keys (hash-based) across fr, ja, es, de, pt, zh-Hans

* feat(i18n): translate ToolsModal (URL/tools selection popup)

Translates all strings in the tools modal including:
- search placeholder, column headers (Name, Description, Slug/Tool)
- sidebar labels, input placeholders, hint text
- Parameters section title and subtitle
- Close button

* chore(i18n): download updated frontend translations from GP

724 strings across fr, ja, es, de, pt, zh-Hans including new
toolsModal and saveChangesModal keys.

* feat(i18n): translate MCP Client settings page, shareable playground label, and Global Variable modal

- McpClientPage: add useTranslation, move all hardcoded strings (title,
  description, steps, config file label, API key notice) to
  settings.mcpClient.* keys
- deploy-dropdown: fix unpublished "Shareable Playground" label that was
  missed when the published branch already used t()
- GlobalVariableModal: add useTranslation, translate all UI strings
  (title, description, type/name/value/fields labels & placeholders,
  save/update button, success and error toasts)
- en.json: add 13 settings.mcpClient.* keys and 23 globalVars.modal.* keys
- All locale files (de, es, fr, ja, pt, zh-Hans) updated from GP

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): translate MCP Server Auth modal (authModal)

- Add useTranslation to authModal/index.tsx
- Translate all hardcoded strings: title, auth type label, radio labels
  (None/API Key/OAuth), no-auth warning, API Key description, OAuth
  field labels/placeholders, Save button, reinstall warning (with and
  without installed clients interpolation)
- Add 30 authModal.* keys to en.json
- Update all locale files (de, es, fr, ja, pt, zh-Hans) from GP (776 strings)

* feat: add translation keys for MCP JSON configuration

* feat(i18n): translate project delete notification, MCP install notification, and McpJsonContent

- main-page.tsx: translate "Project deleted successfully" and "Error
  deleting project" success/error toasts
- useMcpServer.ts: translate "MCP Server installed successfully on
  {{client}}..." success toast using i18n.t()
- McpJsonContent.tsx: translate "Add this config..." hint, Transport
  label, and Generate/API key generated button labels
- Add 3 new keys (mcp.installedSuccessfully, project.deletedSuccessfully,
  project.errorDeleting) + 5 mcpJson.* keys to en.json
- Update locale files from GP

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): translate default flow/project names and delete notifications

- reactflowUtils.ts: use i18n.t("flow.defaultName") for new flow default name
- use-add-flow.ts: use t() for "Starter Project" and "New Project" names at creation time
- sideBarFolderButtons: use t("project.newName") when creating a new project
- main-page.tsx: translate project deleted success/error toasts
- useMcpServer.ts: translate MCP install success toast
- list/index.tsx: translate flow deleted success/error toasts
- Add keys: flow.defaultName, project.newName, project.starterName,
  project.deletedSuccessfully, project.errorDeleting,
  flow.deletedSuccessfully, flow.errorDeleting, flow.errorDeletingRetry,
  mcp.installedSuccessfully
- Update all locale files from GP (790 strings)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(i18n): content-hash component keys and normalize frontend lookup

Backend locale keys for components now use a hybrid format:
  components.{norm_name}.{field_path}.{sha256[:8]}
e.g. components.prompttemplate.display_name.8cd80ebe

The 8-char hash suffix is derived from the English source value, so any
change to a component string produces a new key, forcing GP to issue a
fresh translation. The human-readable prefix keeps keys debuggable.

Component names are normalized (spaces removed, lowercased) in both the
key prefix and the runtime hash computation, so renames like
"PromptTemplate" → "Prompt Template" don't break existing translations.

Frontend syncNodeTranslations() now builds a normalized lookup map so
that nodes stored with old-style type names (e.g. "PromptTemplate") are
correctly resolved against the live registry key ("Prompt Template"),
fixing untranslated Prompt Template nodes in starter template flows.

Also adds chunked uploading to upload_backend_strings.py to avoid
read timeouts when pushing large payloads to GP.

* feat(i18n): fix legacy component alias translation and translate file manager UI

Frontend:
- syncNodeTranslations() now falls back to templates store to resolve legacy
  node.data.type aliases (e.g. "Prompt" → Prompt Template, "parser" →
  ParserComponent), fixing untranslated nodes in starter templates like
  Vector Store RAG
- Translate "Flow saved successfully!" toast in FlowPage
- Translate file manager modal: "Select files" button, error/success toasts,
  drag-and-drop zone labels, folder selection strings (13 new keys)

Backend:
- check_backend_status.py: new script to check GP translation progress
- upload_backend_strings.py: single PUT with 5min timeout, bundle name logged
- download_backend_translations.py: bundle name logged on start
- All locale files updated with latest GP translations (langflow-ui-backend-v2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(i18n): translate canvas controls and Model Providers page strings

- Add useTranslation to MemoizedCanvasControls for Flow Locked, Agent Working, Saving, Lock/Unlock flow labels
- Add useTranslation to CanvasControlsDropdown for Zoom In/Out/To 100%/To Fit and tooltips
- Add useTranslation to ModelProvidersPage for page title and description
- Add canvas.* and modelProviders.page* keys to en.json
- Sync all locale files (fr, ja, es, de, pt, zh-Hans) via GP pipeline

* feat(i18n): translate upload folder button and model trigger strings

- UploadFolderButton: replace hardcoded "Upload a flow" tooltip with t("folder.uploadFlow")
- ModelTrigger: replace hardcoded "No models enabled" and "Select a model" with t() calls

* feat(i18n): cache /basic_examples/ translations per locale

Add _starter_flows_translated_cache keyed by locale so translate_starter_flows
and translate_flow_notes run at most once per locale per 5 minutes instead of
on every request.

* feat(i18n): translate NoteNode empty placeholder text

* feat(i18n): translate slider labels, model trigger, dropdown loading, and loading component

- Translate Precise/Balanced/Creative/Wild slider labels and min/max labels
- Translate "Setup Provider" placeholder in ModelTrigger
- Translate "Loading options" in dropdownComponent (both occurrences)
- Translate "Loading..." in loadingComponent (used by LoadingPage)
- Add syncNodeTranslations() call after model refresh to re-apply translations
- Add 8 new keys to en.json and download translated locale files

* feat(i18n): translate model provider toast notifications

Replace hardcoded English strings in useProviderConfiguration with t()
calls: configuration saved, activated, disconnected success toasts, and
all error titles/messages for save, activate, disconnect, and model
toggle operations.

* fix(i18n): address PR review — DRY key functions, locale allow-list, dead code, silent failures

- Extract safe_flow_key/normalize_component_key/content_hash/component_field_key into
  langflow/utils/i18n_keys.py so runtime translator and extract script share one
  source of truth (C1)
- Validate Accept-Language against supported locales in set_locale middleware;
  unknown values fall back to "en" to prevent cache pollution (C2)
- Delete dead `remaining = ...` block in check_backend_status.py that made a
  redundant network call and never used the result (I2)
- Log logger.warning instead of silently swallowing OSError/JSONDecodeError in
  _load_translations (I3)
- Rename test_strips_dedup_suffix → test_parentheses_in_name_become_underscores
  with corrected comment (I5)
- Remove redundant `import copy as _copy` inside translate_flow_notes (R2)
- Move translate_flow_notes/translate_starter_flows import to flows.py module top (R1)
- Add # Why: comments on max_size=16, uncompressed cache, and verify=False (R5/I8/M3)
- Wrap loadLanguage dynamic import in try/catch so unknown locales don't crash
  React with an unhandled rejection; add i18n.test.ts to cover the fallback

* refactor(gp): consolidate upload/download scripts with --target flag

Replaces four separate scripts (upload_strings, upload_backend_strings,
download_translations, download_backend_translations) with two unified
entry points: upload.py and download.py, each taking --target frontend|backend.

Updates gp-upload.yml to a single workflow with two jobs, updates
gp-download.yml run lines, and removes the now-redundant
gp-backend-upload.yml workflow.

* [autofix.ci] apply automated fixes

* chore(starter-projects): sync starter project snapshots with release-1.10.0

Updates dependency versions (langchain_core 1.2.29 → 1.2.27) and minor
JSON formatting differences across 21 starter project files following
merge from release-1.10.0.

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* feat(i18n): restore LanguageForm component in Settings > General page

* [autofix.ci] apply automated fixes

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

* fix(projects): always use English for new project names

Translated project names (e.g. Japanese '新規プロジェクト') caused MCP
server name collisions because sanitize_mcp_name() strips non-ASCII
chars and all names fell back to 'lf-unnamed'. Hardcode 'New Project'
and 'Starter Project' instead of passing them through t() so the
names sent to the backend are always ASCII.

* chore(i18n): download missing Vector Store RAG note translations

README and Load Data Flow note keys were missing from all non-English
locales. Uploaded to GP and downloaded translations for fr, ja, es,
de, pt, zh-Hans.

* [autofix.ci] apply automated fixes

* fix(canvas): fix zoom dropdown shortcut layout on Windows

The shortcut container had a fixed w-[25px] which fits the single-char
Mac modifier key (⌘) but clips the 4-char Windows modifier key (Ctrl).
Remove the fixed width and use gap-0.5 so the container sizes to its
content on all platforms.

* fix(i18n): sync tooltip info and placeholder fields for existing canvas nodes

syncNodeTranslations() was only updating display_name from fresh component
definitions, leaving baked-in English info (tooltip) and placeholder strings
on nodes loaded from saved flows and starter templates. Extend the merge to
also overlay info and placeholder for template fields, and info for output
fields. Also adds info?: string to OutputFieldType to match what the backend
already sends.

* fix(i18n): persist language selection across page refreshes

loadLanguage() was only adding the resource bundle but never calling
i18n.changeLanguage(), so the active language stayed "en" on every page
load even when a preference was saved in localStorage. Move the
changeLanguage() call into loadLanguage() so both startup (index.tsx)
and runtime language switches activate the language after loading the bundle.

* fix(sidebar): add tooltip to truncated category header labels

Category headers use truncate but had no tooltip, making long translated
names (e.g. French) unreadable. Wrap the label span with ShadTooltip
matching the pattern used by sidebar draggable items.

* fix(sidebar): add tooltip to truncated discover more components button

* fix(sidebar): add tooltips to footer buttons (new custom component, MCP)

* fix(login): center-align title to handle wrapped translations gracefully

* fix(signup): center-align title to handle wrapped translations gracefully

* fix: remove tooltips from form field labels and add tooltip to sign-in button for consistency

* [autofix.ci] apply automated fixes

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* [autofix.ci] apply automated fixes

* fix: resolve ruff violations in gp scripts and i18n util

- FBT001: make dry_run keyword-only in bake_note_keys._bake_file
- S112: add S112 to noqa comments in extract_backend_strings.py
- PLW2901: rename shadowed loop variable in i18n.translate_note_nodes

* [autofix.ci] apply automated fixes

* fix: lazy-import langflow in extract_backend_strings to fix GP test collection

The top-level `from langflow.utils.i18n_keys import ...` caused an
ImportError in CI where the GP test job runs without langflow installed.
Move the imports inside collect_strings() so the module is importable
without langflow — all tests mock collect_strings anyway.

* fix(tests): mock langflow.utils.i18n_keys in test_collect_strings_skips_deactivated_modules

The test calls collect_strings() directly, which now lazy-imports
langflow.utils.i18n_keys. Patch a minimal fake module into sys.modules
so the test runs without langflow installed. Also fix the key assertion
to match the lowercased output of normalize_component_key.

* fix(frontend): restore missing modal height constant imports in KnowledgeBaseUploadModal

The i18n commit (bd070cab63) accidentally dropped MODAL_HEIGHT_DEFAULT,
MODAL_HEIGHT_WITH_ADVANCED, and VALIDATION_ERROR_LINE_HEIGHT from the
constants import when refactoring STEP_TITLES/STEP_DESCRIPTIONS, causing
a ReferenceError at runtime and failing all 39 Jest tests.

* [autofix.ci] apply automated fixes

* fix(tests): add global mocks for ShadTooltip and useGetNoteTranslationsQuery

- Mock shadTooltipComponent to render children only — avoids needing
  TooltipProvider in sidebar unit tests
- Mock useGetNoteTranslationsQuery to return empty data — avoids needing
  QueryClientProvider in NoteNode unit tests
- Fix t() interpolation mock to resolve {{count}} in KnowledgeBasesTab tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tests): add syncNodeTranslations no-op mock to flowStore in affected tests

* [autofix.ci] apply automated fixes

* fix(tests): use stable testId selector instead of node title text in Blog Writer spec

* fix(i18n): preserve instance-level node display_name and description in syncNodeTranslations

Only sync field-level translations (template inputs and outputs).
Node titles and descriptions are instance-level identity — user and
template renames must not be overwritten by canonical registry values.

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 13:43:21 +00:00
4dfe24ab20 ci: unblock community PRs from spurious CI failures (#12916)
Three failures consistently flip community/fork PRs red even when every
substantive check passes (observed on PR #9941):

- Update Component Index: actions/checkout was given only the head ref,
  defaulting the repository to the upstream where the fork branch does
  not exist. Pass head.repo.full_name + head.ref so the head can be
  checked out from the fork. Continue using the pull_request trigger so
  fork code never runs in a privileged context.

- Merge Frontend Jest + Playwright Coverage Reports: codecov-action ran
  with fail_ci_if_error: true and an empty token (forks can't read repo
  secrets), turning a reporting step into a hard merge blocker. Skip the
  upload when CODECOV_TOKEN is unavailable and match the python coverage
  job's fail_ci_if_error: false.

- CI Success: roll-up gate cascading from the two above; clears
  automatically once they pass.
2026-04-28 19:53:42 +00:00
0732423f27 chore: security patch (#12725)
* chore: security patch

security patch

* chore: upgrade package-lock.json

* chore: smolagents and transformer update

* chore: redis upgrade

* chore: litellm upgrade

* fix: Pin click to avoid lower versions

## Root cause

**litellm 1.83.5+** introduced an exact pin `click==8.1.8` in its `requires_dist` (upstream bug [BerriAI/litellm#26154](https://github.com/BerriAI/litellm/issues/26154) — "Pinning exact dependency versions breaks downstream consumers"). When this branch bumped litellm to 1.83.11, uv was forced to downgrade click from 8.3.2 → 8.1.8. Click 8.2+ captures stderr separately by default; 8.1 merges it — hence `ValueError: stderr not separately captured`.

## Fix

- [pyproject.toml:155](pyproject.toml:155) — added `"click>=8.3.0"` to `[tool.uv] override-dependencies` with comment linking the upstream issue.
- [src/backend/base/pyproject.toml:112](src/backend/base/pyproject.toml:112) — same override so `langflow-base` Docker builds pick it up too.
- Regenerated `uv.lock` (click: 8.1.8 → 8.3.3).
- Patched the `click` entry in `src/backend/base/uv.lock` to 8.3.3 (that lockfile is used by `docker/build_and_push_base.Dockerfile` via `uv sync --frozen`; uv treats backend/base as a workspace member so I updated its click block directly — no other references to 8.1.8 remain).
- Reverted [src/lfx/tests/unit/cli/test_run_real_flows.py](src/lfx/tests/unit/cli/test_run_real_flows.py) back to its original `result.stderr` usage — now passes cleanly with click 8.3.3.

All 19 tests in the file pass.

* Update build_and_push_backend.Dockerfile

The `uv pip install` command doesn't read `[tool.uv] override-dependencies` from the target packages' pyproject.toml — those overrides only apply during `uv lock`/`uv sync`. The fix is to pass the overrides explicitly via `--overrides`.

Now let me verify it works by running the same install locally.

Resolution succeeded — click==8.3.3, aiohttp==3.13.5, litellm==1.83.11 all coexist. Let me also test without `--overrides` to confirm I reproduce the original failure.

Reproduced the Docker failure locally. Now test with the overrides file:

Fix verified — resolution succeeds with the overrides file, failing without. Let me clean up and show the final Dockerfile change.

## Summary

**Root cause:** The Dockerfile uses `uv pip install ./src/...` which **does not read** `[tool.uv] override-dependencies` from the target packages' `pyproject.toml` — those overrides only apply during `uv lock`/`uv sync`. On Linux, `cuga>=0.2.20` requires `aiohttp>=3.13.5`, but `litellm==1.83.11` exact-pins `aiohttp==3.13.3` (same upstream bug as the click pin, [BerriAI/litellm#26154](https://github.com/BerriAI/litellm/issues/26154)). Without an active override, resolution fails.

**Fix:** Write a `/tmp/uv-overrides.txt` file mirroring the workspace's `override-dependencies` (litellm, python-dotenv, openai, aiohttp, click) and pass `--overrides /tmp/uv-overrides.txt` to `uv pip install`.

Verified locally by reproducing the exact failure in an isolated tmp directory (to escape the workspace's pyproject.toml auto-apply), then confirming the overrides file resolves it:
- aiohttp==3.13.5 ✓
- click==8.3.3 ✓
- litellm==1.83.11 ✓
- cuga==0.2.22 ✓

* Templates version update

* Update .secrets.baseline

* chore: update overrides

* chore: bump version

* chore: bump main version

* chore: bump SDK version to 0.1.1

* chore: run uv lock and uv sync after SDK version bump

* chore: read the overrides from a single source

security patch

* chore: add pip check toggle

add pip check toggle

* chore: litellm base uv.lock update

* fix(chore): Pin litellm back to last working release

* fix(chore): Pin to 1.83 and higher litellm

* Update build_and_push_backend.Dockerfile

* chore: update base uv.lock

* fix: lazyload toolguard since its an optional extra

* Update .secrets.baseline

* Update component_index.json

* Rebuild component index

* Update component_index.json

---------

Co-authored-by: Eric Hare <ericrhare@gmail.com>
Co-authored-by: vijay kumar katuri <vijay.katuri@ibm.com>
(cherry picked from commit 60a8f76c3fde17124057626c671ea8162872837a)
2026-04-23 17:49:53 -07:00
0659105762 fix: allow backend/frontend Docker builds when main version exists (#12854)
Backend and frontend images (langflowai/langflow-backend and
langflowai/langflow-frontend) are separate from the main image
(langflowai/langflow), so they should not be skipped when the
main version already exists on Docker Hub.

This fixes the issue where backend/frontend builds for 1.9.0
were skipped because the main 1.9.0 image was already published,
preventing these critical images from being available on Docker Hub.

Fixes: Backend and frontend Docker images not published in 1.9.0 release
(cherry picked from commit 595587027d)
2026-04-23 17:49:53 -07:00
b2d4539590 fix(ci): prevent duplicate tags and validate tag format in release workflow (#12847)
- Add tag format validation (must start with 'v')
- Check for duplicate tags without 'v' prefix
- Prevent release notes from using wrong base comparison
- Add validate-tag-format job dependency to create_release

Fixes tag duplication issue that caused v1.9.0 release notes to miss 58 commits.

Root cause: Duplicate tags (1.8.3 vs v1.8.3) caused GitHub's generateReleaseNotes
to pick the wrong base tag due to alphabetical sorting.

This ensures future releases will:
1. Only accept tags with 'v' prefix (v1.2.3 format)
2. Detect and reject releases if duplicate tags exist
3. Generate correct release notes with proper commit history

(cherry picked from commit a754961428)
2026-04-23 17:49:53 -07:00
5d6a7a0f31 chore: address eric's comment 1
address eric's comment 1
2026-04-21 16:18:16 -04:00
88d95758b0 chore: add whitesource to ignore and scan all src
add whitesource to .gitignore and scan all of src
2026-04-21 16:18:16 -04:00
ce6507bee1 ci: add mend integration
add mend integration to OSS
2026-04-21 16:18:16 -04:00
9aae6de814 ci: increase backend test timeout 2026-04-15 08:41:09 -07:00
38d142a723 fix: Upgrade cuga to 0.2.20 to resolve playwright dependency conflict (#12703)
* fix: upgrade playwright to 1.58.0 to address Chromium CVEs

- Add playwright>=1.58.0 to override-dependencies in pyproject.toml
- Update uv.lock: playwright 1.49.0 -> 1.58.0, pyee 12.0.0 -> 13.0.1
- Fixes CVE-2026-2313, CVE-2026-2314, CVE-2026-2315, CVE-2026-2319,
  CVE-2026-2321, CVE-2026-2441, CVE-2026-2648, CVE-2026-2649
- Ensures Docker builds download updated Chromium with security patches

* fix: update npm to latest version to address brace-expansion CVE-2026-33750

- Add npm update after Node.js installation in Dockerfile
- Fixes CVE-2026-33750 in system npm's brace-expansion dependency
- System npm had brace-expansion 2.0.2, update gets 5.0.5+
- Low risk change: npm is backward compatible, only affects CLI tool

* revert: remove npm update from Dockerfile

- npm update attempts were causing CI build failures
- Bundled npm has issues but updating it is proving problematic
- Focus on playwright CVE fix which is the primary concern
- brace-expansion CVE-2026-33750 is lower priority (DoS only)

* chore: sync uv.lock files

sync uv.lock files

* fix(mcp): dedupe edges in connect_components (#12701)

* fix(mcp): make add_connection idempotent to avoid duplicate edges

connect_components used to append a new edge unconditionally. Because
the edge id is deterministic from source/target/handles, calling it for
a pair the flow already had wired up (UI-then-MCP, batch retry, or just
a repeat call) produced a second edge with the same id, double-wiring
the flow at runtime.

Before appending, scan the existing edges for one with the same id and
return that instead. Different outputs/inputs between the same pair
still produce distinct ids and remain supported.

* test(mcp): cover dedupe against UI-saved edges, broaden match key

Older Langflow UIs saved edges with an `xy-edge__` id prefix instead of
the current `reactflow__edge-`, so an id-based dedup would miss the
UI-then-MCP case for any flow that came from an older version. Switch
the existence check to a structural one (source, target, sourceHandle
name, targetHandle fieldName) so the same logical connection dedupes
regardless of id format.

Add a fixture-driven test that loads MemoryChatbotNoLLM.json (an
xy-edge-prefixed flow) and replays each connection through
add_connection, asserting the edge count does not grow.

* fix(mcp): validate_flow fast-fails and reports partial errors (#12697)

* fix(mcp): validate_flow fast-fails and reports partial errors

validate_flow polled /monitor/builds for up to 30 seconds waiting for
every component to finish before reporting errors. When a component
fails early (for example a missing required field), downstream
components never run, so the loop waited out the full window and
returned just "Build timed out: N/M components completed" with no
actionable context.

- Short-circuit as soon as any completed build reports valid: false;
  return those errors immediately instead of polling on.
- On timeout, include the errors from the builds that did complete
  plus a component_count so the caller can see progress.
- Extract _collect_build_errors so the poll loop and timeout branch
  share the same error shape.

* fix(mcp): stream validate_flow build inline instead of polling

The previous implementation triggered an async build and polled
/monitor/builds, which depended on FastAPI BackgroundTasks firing the
log_vertex_build calls after the trigger request had returned. Under
ASGI test transport these tasks never run, so /monitor/builds stayed
empty and validate_flow timed out with component_count=0.

Switch to event_delivery=direct so the build streams its events back
inside the same request:

- Drive the build via client.stream_post and aggregate per-vertex
  results from end_vertex events.
- Fast-fail on the first vertex with valid=false, since downstream
  vertices depend on it and would not produce useful information.
- Surface top-level error events as a single flow-level error.
- Replace _collect_build_errors with _extract_vertex_error, which
  reads the structured error payload from the end_vertex outputs.

Update the lfx unit tests to use the streaming shape and tighten the
backend integration test to assert real success now that the build
actually runs end to end under ASGI.

* fix(graph): make end_all_traces_in_context Python 3.10 compatible

The implementation called asyncio.create_task(coro, context=context),
but the context= keyword was added to create_task in Python 3.11. On
3.10 it raised TypeError. The bug was latent because nothing in the
test suite previously consumed a streaming build response far enough
for Starlette to dispatch the post-response BackgroundTasks where this
code lives. The validate_flow streaming change exposes it.

On 3.10, route the create_task call through context.run so the new
Task copies the captured context as its current context, matching the
isolation the 3.11 path provides via the context= kwarg.

Add a regression test that asserts end_all_traces sees the value of a
contextvar set before the context was captured, even after the caller
mutates that var.

* fix: failing wxo list llm test (#12700)

patch service layer and update failing test

* [autofix.ci] apply automated fixes

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

* Try to fix the missing typer import

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
Co-authored-by: Adam Aghili <Adam.Aghili@ibm.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-14 21:23:12 +00:00
b65515f351 fix: add uv sync step to SDK version determination job (#12695)
The determine-sdk-version job was missing the 'uv sync' step that creates
the cache, causing the 'Post Setup Environment' step to fail with:
'cache path does not exist on disk'

This matches the pattern used in determine-lfx-version job (line 247) and
the nightly build workflow (line 104-105).

Fixes the SDK build failure in release workflow dry run.
2026-04-14 18:14:51 +00:00
9029c4b61e fix: Updates the CI workflow to handle known dependency conflicts. (#12691)
* fix: upgrade playwright to 1.58.0 to address Chromium CVEs

- Add playwright>=1.58.0 to override-dependencies in pyproject.toml
- Update uv.lock: playwright 1.49.0 -> 1.58.0, pyee 12.0.0 -> 13.0.1
- Fixes CVE-2026-2313, CVE-2026-2314, CVE-2026-2315, CVE-2026-2319,
  CVE-2026-2321, CVE-2026-2441, CVE-2026-2648, CVE-2026-2649
- Ensures Docker builds download updated Chromium with security patches

* fix: update npm to latest version to address brace-expansion CVE-2026-33750

- Add npm update after Node.js installation in Dockerfile
- Fixes CVE-2026-33750 in system npm's brace-expansion dependency
- System npm had brace-expansion 2.0.2, update gets 5.0.5+
- Low risk change: npm is backward compatible, only affects CLI tool

* revert: remove npm update from Dockerfile

- npm update attempts were causing CI build failures
- Bundled npm has issues but updating it is proving problematic
- Focus on playwright CVE fix which is the primary concern
- brace-expansion CVE-2026-33750 is lower priority (DoS only)

* CI: Filter cuga/playwright dependency conflict in release workflow

- Filter cuga/playwright conflict (we override playwright>=1.58.0 for CVE fixes)
- Still fails CI if other genuine dependency issues are detected
- Applied to both base and main package build steps

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
2026-04-14 15:29:28 +00:00
2fa3d1c759 feat: add langflow-sdk support to release workflow (#12679)
* feat: add langflow-sdk build and publish support to release workflow

Add support for building and publishing the langflow-sdk package in the
release workflow, mirroring the nightly build support added in #12491.

Changes:
- Add `release_sdk` workflow input for controlling SDK release
- Add `determine-sdk-version` job with first-release PyPI handling
- Add `build-sdk` job with version verification and import testing
- Add `publish-sdk` job that publishes SDK to PyPI before LFX
- Update `build-lfx` to download SDK artifact and test both wheels together
- Update `build-base` and `build-main` to use SDK wheel as find-links
- Pass SDK artifact to cross-platform tests
- Add validation: releasing LFX requires releasing SDK
- Add pre-release version handling for SDK and LFX's SDK dependency

* Update release.yml

* Update release.yml

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-14 01:43:25 +00:00
9dad1965c9 ci: add component index sync on label addition (#12590)
Add component index sync on label addition

Adds a job that attempts to sync comp index with
manual addition of label on PR
2026-04-10 17:54:08 +00:00
30f351766b feat: IBM Globalization Pipeline integration and i18n setup (#12226)
* feat: add IBM Globalization Pipeline integration and i18n setup

- Add GP REST API client with GP-HMAC authentication (scripts/gp/gp_client.py)
- Add upload/download scripts for syncing strings with GP
- Set up i18next + react-i18next with 7 language support (en, fr, ja, es, de, pt, zh-Hans)
- Add 48 UI strings in flat dot notation to en.json (from alerts_constants)
- Add GitHub Actions for automated upload on en.json changes and daily translation download

* [autofix.ci] apply automated fixes

* fix: resolve ruff linting issues in GP scripts

- Fix quote style, import ordering, docstring format
- Use Path instead of os.makedirs/open/os.path.join
- Add missing timeouts to requests calls
- Move noqa comments to correct lines for S501

* feat: migrate all frontend UI strings to i18n for GP localization

- Replace all hardcoded user-facing strings with t() calls across 80+ components, pages, modals, and utilities
- Add 326 translation keys to en.json covering errors, alerts, dialogs, chat, flow, settings, store, table, output, nav, auth, and more
- Add translated locale files for fr, es, de, pt, ja, zh-Hans (downloaded from Globalization Pipeline)
- Migrate alerts_constants.tsx and constants.ts string usages to react-i18next
- Support non-React files (stores, utils) via i18n.t() direct calls

* feat: migrate sidebar strings to i18n (Phase 4)

- Add 56 new translation keys for sidebar categories, nav items, labels, buttons, and empty states
- Convert SIDEBAR_CATEGORIES display_names to i18n key references in styleUtils.ts
- Update 13 sidebar components to use t() for all user-facing strings
- Refresh all 6 locale files from GP (382 total keys)
- Add BACKEND_STRINGS_STRATEGY.md documenting plan for component display_name i18n

* [autofix.ci] apply automated fixes

* fix: add react-i18next mock and fix sidebar nav tests for i18n migration

- Add global react-i18next mock in jest.setup.js using en.json lookups so
  t(key) returns English strings instead of raw keys in test environment
- Fix sidebarSegmentedNav tests: update NAV_ITEMS expectations to use i18n
  keys, and use en.json lookups for tooltip/accessibility label assertions

* feat: add LanguageSelector dropdown to globalization pipeline

Cherry-picked frontend-only changes from feat/gp-backend-i18n (6ab21559db, 3e274a84e3):
- Add LanguageSelector component with dropdown to switch UI language
- Wire LanguageSelector into AccountMenu header
- Persist language preference to localStorage in i18n.ts
- Send Accept-Language header via useCustomApiHeaders (reactive via useTranslation)

No backend files included.

* [autofix.ci] apply automated fixes

* ci: add GP_TEST environment to gp-download and gp-upload workflows

* temp: add feature branch push trigger for workflow testing

* temp: trigger gp-upload workflow test

* fix: use correct GP_test environment name in workflows

* fix: use vars for GP_INSTANCE and GP_BUNDLE

* [autofix.ci] apply automated fixes

* fix: correct environment name to GP-test (hyphen not underscore)

* chore: update translations from Globalization Pipeline [skip ci]

* [autofix.ci] apply automated fixes

* chore: update translations from Globalization Pipeline [skip ci]

* temp: trigger gp-upload workflow test

* ci: add download-gp-bundle job to nightly build pipeline

Downloads frontend translations from Globalization Pipeline after
create-nightly-tag and before frontend tests run, ensuring tests
always validate the latest translated locale files.

- Frontend tests (Linux/Windows) now depend on download-gp-bundle
- Backend unit tests are unaffected (no frontend locale dependency)
- GP failure blocks release-nightly-build (same severity as linux tests)

* chore: remove temporary dev files from gp scripts directory

Remove BACKEND_STRINGS_STRATEGY.md (internal planning doc), test_auth.py
(GP auth debugging script), and en.json (dev-time string snapshot).
Also add venv/ to .gitignore to prevent accidental commits of the local virtualenv.

* [autofix.ci] apply automated fixes

* chore: update translations from Globalization Pipeline [skip ci]

* [autofix.ci] apply automated fixes

* chore: update translations from Globalization Pipeline [skip ci]

* feat(i18n): move language selector from account menu to Settings > General

- Add LanguageForm card component in GeneralPage following the existing
  Card pattern (ProfilePictureForm), with immediate language switching
- English marked as "(Recommended)" via i18n key
- Remove Language row from AccountMenu dropdown
- Add settings.languageTitle/Description/Recommended keys to en.json
  for upload to Globalization Pipeline

* feat(i18n): refactor language constants and improve GP pipeline

- Extract SUPPORTED_LANGUAGES into src/frontend/src/constants/languages.ts
- Update LanguageForm to import from shared constants, add aria-label
- Add settings.languageSelectAriaLabel key to en.json
- Update LanguageSelector component to use shared SUPPORTED_LANGUAGES
- Fix gp_client and download_translations scripts
- Update GP upload/download/nightly GitHub Actions workflows
- Add GP script tests

* [autofix.ci] apply automated fixes

* ci(gp): trigger upload on release branches, download via dispatch only

- Upload now triggers on push to release-* branches (instead of main)
  so translators get strings once they're finalized on the release branch
- Download removes the daily cron schedule; workflow_dispatch only
  since the nightly build already handles scheduled runs

* chore: update translations from Globalization Pipeline

* fix(i18n): address PR review comments — tests, nightly build decoupling, GP test workflow

- Add pytest tests for gp_client.py (HMAC auth, HTTP errors, timeout)
- Add pytest tests for upload_strings.py and download_translations.py
- Add conftest.py to set sys.path so GP scripts can be imported from repo root
- Add scripts/gp/__init__.py to make gp a proper package for test imports
- Add gp-test.yml workflow to run GP script tests on PRs touching scripts/gp/**
- Decouple download-gp-bundle from nightly build chain:
  - Add continue-on-error: true so GP failure never blocks the build
  - Remove download-gp-bundle from needs/if of frontend-tests-linux,
    frontend-tests-windows, and release-nightly-build
  - Add warning annotation step that fires on GP download failure

* feat(i18n): lazy-load non-English locale files via Vite dynamic imports

- Remove 6 static locale imports from i18n.ts; only en.json is bundled
- Export loadLanguage() which dynamically imports a locale chunk on demand
  and caches it via i18n.hasResourceBundle (no-op on repeat calls)
- Update index.tsx to await loadLanguage(detectedLang) before rendering,
  preventing an English flash for non-English users
- Make handleChange async in LanguageForm and LanguageSelector to await
  loadLanguage before calling i18n.changeLanguage
- Add i18n.test.ts covering loadLanguage: en no-op, new language load,
  cache hit, and multiple independent languages
- Update LanguageForm.test.tsx to mock @/i18n and assert loadLanguage
  is called before changeLanguage on language switch

* [autofix.ci] apply automated fixes

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

* fix: add ruff ignores for scripts/gp/tests

- Add per-file ignores for scripts/gp/tests/* to fix CI failures
- Ignore S101 (assert usage), TRY003, EM101 for test files
- Follows existing pattern for other test directories

* [autofix.ci] apply automated fixes

* fix(ci): run GP translation download before nightly tag creation

Previously download-gp-bundle ran after create-nightly-tag, so fresh
translations were committed to the branch after the tag was already
created. The nightly Docker image builds from the tag, meaning it never
included the day's translations.

Fix: move download-gp-bundle to run before create-nightly-tag so the
tag is created on a commit that already includes the latest translations.

* fix(ui): replace native select with Radix UI Select in LanguageForm

Swaps the native <select> element for the existing Radix UI Select
component to get consistent padding on both sides of the chevron,
removing reliance on browser-rendered chevron positioning.

* fix(ci): rearchitect GP translation download to use PRs instead of direct push

Replace direct branch commits in gp-download.yml and nightly_build.yml with
a PR-based flow using peter-evans/create-pull-request, respecting release
branch protection policies.

- gp-download.yml: dynamically resolves latest release-* branch, closes stale
  translation PRs, opens a new PR via peter-evans/create-pull-request@v8, and
  enables auto-merge (squash); scheduled at 23:00 UTC (1h before nightly)
- nightly_build.yml: remove download-gp-bundle job and its dependency from
  create-nightly-tag — nightly build now uses translations already in the branch

* fix(ci): skip GP upload when triggered from an outdated release branch

Add a branch guard step that resolves the latest release-* branch at
runtime and skips the upload (with a notice annotation) if the triggering
branch is not the latest. Prevents stale en.json from an old release branch
overwriting current source strings in the Globalization Pipeline.

* fix(tests): fix LanguageForm test suite failures

- Add `...jest.requireActual` to @tanstack/react-query mock so QueryClient
  constructor remains available (used in contexts/index.tsx at module load)
- Mock @/components/ui/select with a native <select> shim so test assertions
  using selectOptions() and querySelectorAll("option") work correctly

* fix: add trailing newline to component_index.json

The build_component_index.py script was generating component_index.json
without a trailing newline, causing CI failures when autofix.ci tried to
add the missing newline. This fix ensures the generated JSON file always
ends with a newline character, following POSIX text file standards.

Fixes the 'No newline at end of file' error in the update-component-index
workflow job.

* [autofix.ci] apply automated fixes

* fix: add trailing newline to component_index.json

The build_component_index.py script was generating component_index.json
without a trailing newline, causing CI failures when autofix.ci tried to
add the missing newline. This fix ensures the generated JSON file always
ends with a newline character, following POSIX text file standards.

Fixes the 'No newline at end of file' error in the update-component-index
workflow job.

* [autofix.ci] apply automated fixes

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

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: vijay kumar katuri <vijay.katuri@ibm.com>
2026-04-08 17:51:18 +00:00
cde9f23001 feat: deployment page and stepper UI with watsonx Orchestrate integration (#12303)
* feat: deployment page list

* feat: add deployment stepper modal with context-based state management

Implement a multi-step deployment creation flow with 4 steps (Provider,
Type, Attach Flows, Review). State is centralized in a scoped
DeploymentStepperContext to avoid prop-drilling across step components.
Includes bug fixes for version re-selection and connection pre-selection.

* feat: replace mock flows and versions with real API data in deployment stepper

Fetch real flows from the API (scoped to current folder) and versions
per-flow lazily when selected. Enrich selectedVersionByFlow context to
store versionTag alongside versionId so the review step can display it
without re-fetching. Remove MOCK_FLOWS_WITH_VERSIONS from mock-data.

* refactor: improve deployment stepper code quality and conventions

Rename all new deployment files to kebab-case per project conventions,
fix context re-render issues with useMemo/useCallback, replace raw
Tailwind colors with design tokens, add missing data-testid and ARIA
attributes, fix Deploy button disabled on review step, and move shared
FlowTabType to a dedicated types file.

* refactor: align deployment provider types and UI to backend contracts

- Rename ProviderInstance -> ProviderAccount to match backend naming
- Update ProviderAccount fields to match DeploymentProviderAccountGetResponse (provider_key, provider_url, provider_tenant_id, created_at, updated_at)
- Update ProviderCredentials fields to match DeploymentProviderAccountCreateRequest (provider_key, provider_url, api_key)
- Rename all "instance" UI terminology to "environment" (tabs, labels, components)
- Auto-select watsonx Orchestrate on mount as the only supported provider
- Remove Kubernetes from mock providers; update mock data to use watsonx_orchestrate only

* feat: add react-query hooks for deployments and provider accounts with mock data

- Add useGetProviderAccounts hook (GET /deployments/providers) replacing direct MOCK_PROVIDER_INSTANCES import in step-provider.tsx
- Add useGetDeployments hook (GET /deployments) replacing direct MOCK_DEPLOYMENTS import in deployments-page.tsx
- Replace fake setTimeout loading state with hook isLoading state
- Register DEPLOYMENTS and DEPLOYMENT_PROVIDER_ACCOUNTS URL constants
- Real API calls are commented out with TODO markers for easy swap when backend is ready

* chore: remove stale biome-ignore suppression in step-attach-flows

* fix: replace button role=radio with semantic input type=radio in ProviderCard

* fix: replace button role=radio with semantic radio inputs across deployment stepper

- Extract RadioSelectItem component (label + sr-only input + checkbox indicator)
  used by version, connection, and environment selectors
- Fix step-type.tsx type cards with label/input pattern (matching ProviderCard)
- Add role="radiogroup" wrapper to EnvironmentList
- Fix envVars key: use stable crypto.randomUUID() id instead of array index

* feat: add name field to provider accounts, multi-select connections, and real API call

- Add `name` field to ProviderAccount, ProviderCredentials, and mock data
- Switch connection selection from single to multi-select (CheckboxSelectItem)
- Allow creating new connections inline from the attach flows step
- Lift connections state up to DeploymentStepperContext
- Move ConnectionItem type from step-attach-flows to shared types.ts
- Wire up real API call in useGetProviderAccounts (remove mock)

* feat: wire deploy button and populate deployments page with real API data

- Add usePostDeployment and usePostProviderAccount mutation hooks
- Wire Deploy button in stepper modal: creates provider account if needed,
  then POSTs to /api/v1/deployments with WXO provider_data shape
- Fix environment_variables payload: wrap values as { value, source: "raw" }
  to satisfy EnvVarValueSpec schema
- Fix connection app_id: prefix with conn_ so WXO name validation passes
  (names must start with a letter)
- Multi-select connections with checkbox UI; persist connections in context
  so they survive back/forward navigation
- Update Deployment type to match API response shape; remove mock fields
  (url, status, health, lastModifiedBy)
- Wire useGetDeployments to real API; load provider ID from useGetProviderAccounts
- Update deployments table to display real fields: name, type, attached_count,
  provider name, updated_at

* refactor: extract DeploymentsContent component to eliminate nested ternary

* feat: add Test Deployment chat modal for deployed agents

Implements a chat interface to test deployments directly from the UI.
Wires up two entry points: the stepper modal "Test" button (inline
transition) and the deployments table play button (standalone dialog).

- Add usePostDeploymentExecution and useGetDeploymentExecution hooks
  hitting POST/GET /api/v1/deployments/executions
- Build test-deployment-modal: ChatHeader, ChatMessages, ChatInput,
  ChatMessageBubble with user/bot avatars, loading dots, tool traces
- useDeploymentChat hook: recursive setTimeout polling (max 30 × 1.5s),
  thread_id persistence for multi-turn, watsonx response parsing
  (response_type/type text, wxo_thread_id extraction)
- Stepper modal transitions inline to TestDeploymentContent on Test click
- Deployments table play button opens standalone TestDeploymentModal

* feat: add syntax highlighting to chat code blocks using SimplifiedCodeTabComponent

* feat: add action menu and icon-based type badge to deployments table

- Add dropdown action menu (Duplicate, Update, Delete) to each row
- Replace left-border type badge with icon-based badge (Bot for Agent, Plug for MCP)

* refactor: remove connected status from provider card in stepper

* feat: auto-detect flow env vars in deployment connection form

- Add POST /deployments/variables/detections backend endpoint that scans
  flow version data for credential fields (load_from_db=True globals and
  password=True fallbacks) and returns detected variable names
- Derive meaningful env var names from the model field's category when no
  global variable is linked (e.g. OPENAI_API_KEY from category "OpenAI")
- Add DetectEnvVarsRequest/DetectedEnvVar/DetectEnvVarsResponse schemas
- Add usePostDetectDeploymentEnvVars frontend mutation hook
- Pre-populate Create Connection env var rows with detected keys/values
  when attaching a flow version; global variable selections render as tags
  via InputComponent with global variable picker support

* feat: add empty state and smart default tab for available connections

- Default to "Create Connection" tab when no connections exist
- Show empty state with icon, description, and shortcut link when
  the Available Connections tab has no items

* feat: redesign review step with two-column layout and env vars section

Match new design reference with Deployment/Attached Flows columns
and a masked Configuration section showing env variable keys.

* feat: integrate delete deployment with loading state and fix test modal flow

- Add useDeleteDeployment hook (DELETE /deployments/{id}, refetches list)
- Show spinner + faded row while deletion is in progress; fix race where
  deleteTarget was cleared before isPending resolved by using separate deletingId state
- Redesign StepDeployStatus with animated spinner, ping ring, and success checkmark
- After deploy, "Test" closes the stepper and opens the standalone TestDeploymentModal
  (consistent UI, correct providerId, chat reset on close)
- Prevent closing the stepper modal while deployment is in progress

* refactor: address PR review concerns for deployment UI

- Split step-attach-flows.tsx (656→274 lines) into FlowListPanel,
  VersionPanel, and ConnectionPanel sub-components
- Extract Watsonx parser utilities into watsonx-result-parsers.ts,
  reducing use-deployment-chat.ts from 384 to 277 lines
- Surface detectEnvVars errors via setErrorData instead of silently
  resetting state
- Add EnvVarEntry named type to types.ts, replacing inline shape
  repeated across two files
- Move import json to module level in deployments.py (PEP 8)
- Fix URL construction in useGetDeploymentExecution to use axios
  params option, consistent with other hooks
- Remove commented-out MOCK_CONNECTIONS dead code
- Add TODO comment to hardcoded "watsonx-orchestrate" provider key

* refactor: apply React best practices and remove mock data from deployment UI

- Fix barrel imports: import directly from source files in deployments-page,
  step-provider, and step-attach-flows
- Replace useEffect auto-select with derived effectiveFlowId in step-attach-flows
- Fix async state init bug for environmentTab using useRef guard pattern
- Fix stale closure in handleAddEnvVar with functional setState
- Wrap useState initial value in lazy initializer for envVars
- Wrap all 8 handlers in useCallback; wrap panel components in memo()
- Remove mock-data.ts; move PROVIDERS constant inline to step-provider
- Drop MOCK_CONNECTIONS (was empty array) from context
- Simplify step-provider UI: remove provider selection radio group since
  only one provider exists; show watsonx Orchestrate as a static display

* feat: add Deploy button to canvas toolbar

Adds a primary-colored Deploy button at the far right of the canvas toolbar.
Clicking it saves the flow, creates a version snapshot, and opens the
deployment stepper modal with the current flow and version pre-selected in
the Attach Flows step, including auto-detection of environment variable keys.

* chore: disable ENABLE_DEPLOYMENTS feature flag

* fix: forward LANGFLOW_FEATURE_WXO_DEPLOYMENTS env var to frontend

The feature flag was reading from import.meta.env but the variable
was never injected by Vite's define config, so the deployments
feature was always disabled regardless of the .env value.

* feat: implement providers tab with environment list

Replace the "coming soon" placeholder in the Providers sub-tab with a
real table showing existing provider accounts (name, URL, provider key,
created date) fetched from the API, including loading skeleton and
empty state.

* feat: add provider creation modal with tab-aware action button

Create AddProviderModal with name, API key, and URL fields matching
the deploy modal. The top-right button now switches between
"New Deployment" and "New Environment" based on the active sub-tab.

* feat: implement provider account deletion with confirmation modal

Add useDeleteProviderAccount hook, wire it through ProvidersContent
and ProvidersTable with loading/disabled row state on delete, and
reuse DeleteConfirmationModal for the confirmation flow.

* fix: correct provider_key to watsonx-orchestrate and add API key visibility toggle

Fix the provider_key value sent to the API. Add eye/eye-off toggle
to the API Key input in both the add provider modal and the deploy
stepper's provider step.

* feat: navigate to deployments tab and open test modal after canvas deploy

After a successful deployment from the canvas deploy button, clicking
"Test" navigates to the deployments tab and auto-opens the test modal.
Also adds eye toggle to the deploy stepper's API Key field.

* Add llm config to deployment creation workflow

* [autofix.ci] apply automated fixes

* feat: add useGetDeploymentLlms query hook

Add missing query hook for the GET /deployments/llms endpoint,
resolving the broken import in step-type.tsx.

* Create provider env inline of step

Ensures the list llm call has an authed provider account to use

* feat: add extensibility for wxo tools in deployments api (#12425)

* feat(deployments): surface tool_ids in WXO API for explicit tool control
- Fix bind to reuse existing WXO tools via attachment lookup instead of
  always creating new ones
- Add tool_id-based operations (bind_tool, unbind_tool, remove_tool_by_id)
  alongside existing flow_version_id operations
- Add PATCH /deployments/snapshots/{provider_snapshot_id} endpoint to
  update snapshot content with a new flow version (blast-radius bounded
  to Langflow-tracked tools only)
- Add update_snapshot method to WXO adapter
- Enrich flow version list with deployment info (tool_id, tool_name,
  app_ids) via FlowVersionReadWithDeployments API schema
- Surface tool_id in WatsonxApiToolAppBinding responses; flow_version_id
  becomes optional for tool-id-based operations

* Revert "feat: Add Langflow Assistant chat panel for component generation (#11636)"

This reverts commit 61fac94139.

* Add some comments

* Reapply "feat: Add Langflow Assistant chat panel for component generation (#11636)"

This reverts commit d7f08791f0.

* fix(deployments): review fixes for wxo tools extensibility PR

- Add best-effort compensating rollback to update_snapshot endpoint
- Add N+1 TODO for provider account batch-fetch in build_deployment_info_map
- Remove unpopulated app_ids field from FlowVersionDeploymentInfo
- Use elif chains in validate_operation_references for tool-id ops
- Strip provider_snapshot_id once at function entry
- Add note about WXO-only update_snapshot adapter method
- Replace call-count-based _MultiQueryFakeDb with table-name dispatch
- Fix SnapshotUpdateRequest docstring route path

* fix doc

---------

Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>

* feat: allow attaching flows without connections in deployment stepper

Connections are now optional when attaching flows in step 3. Users can
skip the connection panel and proceed with just a version selected.
The deploy payload iterates selectedVersionByFlow so flows without
connections are included with an empty app_ids array.

* feat: show attached connections per flow in deployment stepper

Display connection names under each flow in the flow list panel so
users can see what's attached at a glance. Refactor the review step
to show configuration scoped per flow instead of a flat list.

* remove unused enriched flow version data. to be replaced by a new endpoint in /deployments

* Allow nonraw env vars wxo (#12435)

Allow vars to be interpreted through global vars

* feat: add update existing deployment from canvas deploy button (#12440)

* feat: add update existing deployment from canvas deploy button

When clicking Deploy on the canvas, if the flow already has existing
deployments, a choice dialog is shown allowing the user to either
update an existing deployment or create a new one. Updating calls
PATCH /deployments/snapshots/{provider_snapshot_id} with the new
flow version.

Backend changes:
- New GET /deployments/flow-attachments/{flow_id} endpoint to discover
  existing deployments for a flow
- New CRUD function list_attachments_for_flow_with_deployment_info
- New schemas FlowDeploymentAttachmentItem/FlowDeploymentAttachmentsResponse
- Fix update_snapshot to properly construct flow_definition with nested
  data structure, name, description, and last_tested_version
- Fix rollback path to cache ORM values before session.rollback()

Frontend changes:
- New useGetFlowDeploymentAttachments and usePatchSnapshot hooks
- New DeployChoiceDialog component with radio selection
- Modified deploy button to check for existing deployments on click

* feat: add flow_ids filter to deployments endpoint and fix snapshot update
Add a `flow_ids` query parameter to GET /deployments so the frontend can
discover which deployments a flow is part of. The response includes a new
`matched_attachments` field with per-attachment `flow_version_id` and
`provider_snapshot_id`, replacing the non-existent flow-attachments endpoint.
Refactor PATCH /snapshots to accept BaseFlowArtifact (via the mapper's new
resolve_snapshot_update_artifact method) instead of a raw dict, fixing the
misleading "Deployment name must include at least one alphanumeric character"
error that occurred because flow_version.data lacks a name field.
Frontend: rewrite useGetFlowDeploymentAttachments to query the real
GET /deployments endpoint per provider account.

* feat: refactor deploy dialog with update-snapshot flow and phased UI

Restructure deploy-choice-dialog into modular phases (provider, review,
deployment, update) to support both new deployments and snapshot updates.
Add use-get-deployments-by-providers query replacing the removed
flow-attachments endpoint, extract provider-credentials-form component,
and add error/navigation helpers. Backend: validate flow_id as string
in WatsonX Orchestrate service before deployment.

* [autofix.ci] apply automated fixes

* ruff

---------

Co-authored-by: Hamza Rashid <hzarashid@gmail.com>
Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* feat: reorder model field and add scrollable dropdown in deploy wizard type step

Move Model select to appear after Agent Name (before Description) and
constrain the dropdown with max-height + scroll. A bouncing chevron
indicator fades in/out to signal more items below.

* feat(deployments): add paginated deployment flow-version listing (#12453)

* feat(deployments): add paginated deployment flow-version listing with snapshot sync
Add a new read path for listing flow versions attached to a deployment:
- introduces GET /deployments/{deployment_id}/flows with page/size pagination
- returns attachment-scoped metadata (flow_version id, flow_id, version_number, attached_at, provider_snapshot_id)
- keeps provider-owned fields under provider_data on each item
Implement snapshot-aware synchronization behavior for this list endpoint:
- loads deployment attachments from DB, extracts provider_snapshot_id values, and verifies them via adapter list_snapshots(snapshot_ids=...)
- removes stale attachment rows when snapshot IDs no longer exist on the provider
- falls back to DB-only response without enrichment on any sync failure (including partial failures after list_snapshots)
Refactor mapper boundaries to follow shape_* contract conventions:
- helper now passes through SnapshotListResult | None instead of pre-building enrichment maps
- route delegates full response shaping to mapper.shape_flow_version_list_result(...)
- base mapper performs direct DB->API mapping for flow-version list items
- WXO mapper overrides list shaping to enrich provider_data with connection_app_ids from snapshot binding.langflow.connections
Normalize WXO connection extraction into a shared read-path helper:
- add extract_langflow_connections_binding(...) in core/tools.py
- reuse it in verify_tools_by_ids and service config listing to avoid duplicated nested payload parsing
Add/adjust persistence and tests:
- add CRUD helpers for paginated attachment+flow_version join and attachment counting
- extend schema and route tests for new response models/endpoint behavior
- add mapper tests for base direct mapping and WXO-specific enrichment
- add sync tests for snapshot-id verification, stale-row cleanup, and fallback semantics

* fix(deployments): normalize wxo snapshot provider_data contracts
Validate and normalize snapshot connection payloads across mapper/service flows, and align flow-version item provider_data to use app_ids.
Add typed snapshot-item schema support in lfx and expand tests for missing/malformed snapshot provider_data behavior.

* add flow name

* return empty app_ids list instead of null

---------

Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>

* ref: remove resource name prefix (#12459)

* refactor: remove resource_name_prefix from all naming paths

Remove the resource_name_prefix concept entirely. Tool names now use the
exact flow name (or user-provided name in the future). Agent names use the
deployment name directly with no prefix.

BE:
- Delete resource_name_prefix.py
- Remove field + validators from API schemas (create + update)
- Remove field + validators from adapter schemas (create + update)
- Remove from mapper _build_provider_payload_body
- Remove resource_prefix from ProviderCreatePlan and ProviderUpdatePlan
- Remove tool_name_prefix from all tool creation functions
- Remove resolve_resource_name_prefix from utils
- Remove prefix constants from constants.py
- Remove prefixed_deployment_name from config.py
- Clean 5 test files (remove prefix-only tests, update payloads)

FE:
- Remove toResourceNamePrefix function from types.ts
- Remove resource_name_prefix from DeploymentCreateRequest type
- Remove usage from deployment-stepper-context.tsx payload builder

* [autofix.ci] apply automated fixes

* refactor: remove resource_name_prefix from all naming paths

Remove the resource_name_prefix concept entirely. Tool names now use the
exact flow name (or user-provided name in the future). Agent names use the
deployment name directly with no prefix.

BE:
- Delete resource_name_prefix.py
- Remove field + validators from API schemas (create + update)
- Remove field + validators from adapter schemas (create + update)
- Remove from mapper _build_provider_payload_body
- Remove resource_prefix from ProviderCreatePlan and ProviderUpdatePlan
- Remove tool_name_prefix from all tool creation functions
- Remove resolve_resource_name_prefix from utils
- Remove prefix constants from constants.py
- Remove prefixed_deployment_name from config.py
- Clean 5 test files (remove prefix-only tests, update payloads)

FE:
- Remove toResourceNamePrefix function from types.ts
- Remove resource_name_prefix from DeploymentCreateRequest type
- Remove usage from deployment-stepper-context.tsx payload builder

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* feat: wxo custom tool naming (#12460)

feat: custom tool naming when attaching flows to deployments

Users can now name tools when attaching flows in the deployment stepper.
If left blank, the flow name is used as the tool name.

BE:
- Add optional tool_name field to WatsonxApiBindOperation
- Mapper overrides raw_name_by_flow_version_id with user-provided tool_name
- Apply custom name to both raw payloads and provider bind operations

FE:
- Add toolNameByFlow state + setToolNameByFlow to stepper context
- Include tool_name in bind operations when set (trimmed, omitted if empty)
- Tool Name input in version panel (shown when a version is selected)
- Review step shows tool name (custom or flow name) in config cards
  with flow name + version underneath

* feat(deployments): derive connection ID from user-provided name

Replace random UUID generation with a sanitized version of the
connection name. Input is restricted to alphanumeric, underscore,
and space characters; spaces are converted to underscores in the ID.

* feat(deployments): prevent duplicate connection names

Disable the Create Connection button and show a validation error
when a connection with the same name (case-insensitive) already exists.

* refactor(deployments): extract page logic into custom hooks and self-contained tab components

Extract state management from the monolithic DeploymentsPage into focused
custom hooks (useDeleteWithConfirmation, useProviderFilter, useTestDeploymentModal)
and consolidate each tab's modals into its own content component, reducing the
page from 270 to 62 lines.

* fix(deployments): forward thread_id through WxO execution lifecycle

* fix(deployments): extract WxO tool call traces from actual step_history format

The parser expected tool_use/tool_result fields but WxO returns
type: "tool_calls" with tool_calls[] and type: "tool_response" —
traces were never being extracted. Rewrites extractToolTraces to
correlate calls with responses via tool_call_id, captures
agent_display_name, and makes each trace individually expandable.

* feat(deployments): refactor WXO deployment schemas and create response mapping (#12454)

* feat(deployments): add paginated deployment flow-version listing with snapshot sync
Add a new read path for listing flow versions attached to a deployment:
- introduces GET /deployments/{deployment_id}/flows with page/size pagination
- returns attachment-scoped metadata (flow_version id, flow_id, version_number, attached_at, provider_snapshot_id)
- keeps provider-owned fields under provider_data on each item
Implement snapshot-aware synchronization behavior for this list endpoint:
- loads deployment attachments from DB, extracts provider_snapshot_id values, and verifies them via adapter list_snapshots(snapshot_ids=...)
- removes stale attachment rows when snapshot IDs no longer exist on the provider
- falls back to DB-only response without enrichment on any sync failure (including partial failures after list_snapshots)
Refactor mapper boundaries to follow shape_* contract conventions:
- helper now passes through SnapshotListResult | None instead of pre-building enrichment maps
- route delegates full response shaping to mapper.shape_flow_version_list_result(...)
- base mapper performs direct DB->API mapping for flow-version list items
- WXO mapper overrides list shaping to enrich provider_data with connection_app_ids from snapshot binding.langflow.connections
Normalize WXO connection extraction into a shared read-path helper:
- add extract_langflow_connections_binding(...) in core/tools.py
- reuse it in verify_tools_by_ids and service config listing to avoid duplicated nested payload parsing
Add/adjust persistence and tests:
- add CRUD helpers for paginated attachment+flow_version join and attachment counting
- extend schema and route tests for new response models/endpoint behavior
- add mapper tests for base direct mapping and WXO-specific enrichment
- add sync tests for snapshot-id verification, stale-row cleanup, and fallback semantics

* fix(deployments): normalize wxo snapshot provider_data contracts
Validate and normalize snapshot connection payloads across mapper/service flows, and align flow-version item provider_data to use app_ids.
Add typed snapshot-item schema support in lfx and expand tests for missing/malformed snapshot provider_data behavior.

* add flow name

* return empty app_ids list instead of null

* feat(deployments): refactor WXO deployment schemas and create response mapping
Restructure the Watsonx Orchestrate deployment contracts to remove redundant connection declaration and make create responses explicit and typed.
- Remove `existing_app_ids` from API and adapter `connections` payload schemas.
- Replace shared API payload base with separate create/update models and focused validators.
- Keep validation strict (no legacy/backward-compat handling for removed fields).
- Derive provider-side `existing_app_ids` in plan builders from:
  operation app_ids - connections.raw_payloads[*].app_id
- Stop passing `existing_app_ids` through mapper payload translation.
- Add explicit create response shaping via mapper (`shape_deployment_create_result`) and route integration.
- Introduce typed create provider_data structure with `created_app_ids` and `tool_app_bindings`.
- Document and enforce `source_ref` normalization semantics:
  UUID refs map to `flow_version_id`; non-UUID refs map to `None`; empty refs error.
- Remove obsolete create-response helper path and align mapper interface signatures.
- Update unit tests and assertions to reflect the new contract and validation wording.

* improve api error logs (remove internal details and surface more informative message when an invalid field is provided

* feat(deployments): API contract updates, bug fixes, and observability improvements
BREAKING CHANGES (API):
- Move variable detection endpoint from POST /deployments/variables/detections
  to POST /variables/detections (variables router)
- Rename `reference_ids` to `flow_version_ids` in DetectVarsRequest schema
- Remove `existing_app_ids` from WXO create/update connection payloads
- Remove `resource_name_prefix` from WXO create/update payloads
- Add optional `tool_name` field to WXO bind operations
Backend:
- Rename DetectEnvVarsRequest/Response to DetectVarsRequest/Response (internal)
- Fix tool name mismatch (`name_of_raw not found in tools.raw_payloads`) during
  deployment updates by applying tool_name override and aligning
  filtered_raw_payloads with name-updated artifacts in the WXO mapper
- Fix "Missing snapshot bindings for added flow versions on update" by filtering
  out already-attached flow version IDs in the route handler before calling
  resolve_added_snapshot_bindings_for_update (stateless, DB-backed filtering)
- Switch WXO adapter core modules (retry, update, shared, create, tools) from
  stdlib logging to lfx.log.logger (structlog) for consistent log output
- Upgrade rollback log calls from info to warning level for visibility
- Add logger.exception() calls in handle_adapter_errors() for
  DeploymentServiceError, NotImplementedError, and ValueError to surface
  provider errors (e.g. validation failures) in backend logs with tracebacks
Frontend:
- Remove existingAppIds from deployment stepper context and create payload
- Move detect-env-vars hook from deployments/ to variables/ query directory
- Update step-attach-flows import path and payload field (flow_version_ids)
Tests:
- Add 15 unit tests for detect_env_vars endpoint and _derive_env_var_name helper
- Add 3 route-handler tests for already-attached flow version filtering on update
- Update WXO adapter tests: drop existing_app_ids, fix agent name assertions,
  fix mock signatures, adapt logging test for structlog

* fix: harden detect_env_vars endpoint against abuse
- Add max_length=50 on flow_version_ids to prevent resource exhaustion
- Hide /detections from OpenAPI schema (consistent with other variable routes)
- Return unresolved_ids in response so callers know which version IDs were skipped

* feat: add update agent impl (#12475)

* Allow updating an agent

ONLY allows attaching and detaching Flows.
Does NOT allow attaching / detaching connections, or editing attached
tool names.

* [autofix.ci] apply automated fixes

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

* remove print

* add missing files

* mypy

* Add edit flow tests

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* feat: add list all conns impl for wxo create deploy workflow (#12476)

* feat(deployments): list all WXO tenant connections in create workflow

- Fix backend list_configs to handle SDK Pydantic model objects (not just dicts)
- Add useGetDeploymentConfigs hook to fetch tenant connections by provider
- Seed existing connections into the attach-flows step on mount
- Add search/filter input for the available connections list
- Raise configs endpoint page size cap to 10k for large tenants

* test(deployments): add list_configs tests for Pydantic model handling

Cover the fix where SDK ConnectionsClient.list() returns Pydantic
model objects instead of dicts: pure models, mixed dicts+models,
deduplication, and non-dict/non-model skip behavior.

* add todo

* [autofix.ci] apply automated fixes

* fix(tests): update imports for removed to_deployment_create_response helper

The helper was replaced by BaseDeploymentMapper.shape_deployment_create_result
in the schema refactor (#12454). Update both test files to use the new method.

* fix(tests): remove shape_deployment_create_result from passthrough test

Method signature changed from single-arg passthrough to (result, deployment_row)
in the schema refactor (#12454). It's now tested in test_deployment_description_and_type.py.

* tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* feat(deployments): improve attach flow step UX in deploy modal (#12482)

- Add detach flow button in both create and edit modes
- Defer version attachment until connection step is completed (skip/attach)
- Replace radio indicators with clickable version items that auto-advance to connections
- Move tool name editing to review page with inline edit/confirm pattern
- Sort newly created connections to top of list and remove variable count display
- Add visual distinction (blue border/bg) for attached versions
- Split review page connections into "Existing" and "New" sections

* feat(deployments): add expandable rows to show attached flows (#12483)

Allow users to click the "Attached" count in the deployments table to
expand a row showing the flow names and versions. Flows are fetched
lazily via GET /deployments/{id}/flows only when the row is expanded.

* feat(deployments): replace Duplicate with Details modal (#12492)

feat(deployments): replace Duplicate action with Details modal

Replace the unused "Duplicate" action menu item with a "Details" option
that opens a read-only modal showing deployment info, attached flows,
and their connections. Data is fetched via useGetDeployment,
useGetDeploymentAttachments, and useGetDeploymentConfigs.

* Add wxo lfx req override

* feat(wxo): tool name handling, rename support, and ownership safety (#12502)

* feat(wxo): tool name handling, rename support, and ownership safety

- Fix update_snapshot to preserve existing wxO tool name instead of
  deriving from flow name (prevents overwriting custom tool names)
- Add _validate_tool_name at API boundary with deferred validation so
  user-provided tool_name overrides aren't blocked by invalid flow names
- Add rename_tool operation (API + provider + plan + apply) with safety
  checks: tool must be on agent, must exist, must have binding.langflow
- Add verify_langflow_owned guard to all tool mutation paths (create,
  update, rename) to prevent modifying non-Langflow-managed tools
- Add WXO_LFX_REQUIREMENT_OVERRIDE env var for lfx version pinning
- Pre-seed resolved_connections from existing agent tools during update
- Surface tool_name in /flows endpoint response from wxO snapshot data
- Pre-populate tool names and connections in edit mode stepper (FE)
- Emit rename_tool operations from FE when pre-existing tool name changes
- Sort attached flows to top of flow list in edit mode (FE)
- Fix FE sending unsupported existing_app_ids field in update payload
- Add 24 backend + 6 frontend tests covering ownership checks, rename
  safety, name validation, plan building, env var override, and payloads

* [autofix.ci] apply automated fixes

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

* chore(wxo): trim verbose pre-seeding logs, add plan entry/exit logging

- Condense per-tool pre-seeding debug logs into a single summary line
- Add plan summary log at entry of apply_provider_create_plan_with_rollback
- Add plan summary log at entry of apply_provider_update_plan_with_rollback
- Add agent creation result log in create path

* fix(wxo): resolve mypy, ruff, and lint errors

- Fix dict[str, str] → dict[UUID, str] type annotation for
  raw_name_by_flow_version_id in mapper create/update paths (mypy)
- Move logger assignment after imports in config.py (ruff E402)
- Remove unnecessary result variable in shared.py (ruff TRY300/RET504)
- Add raise-from chain in tools.py _resolve_lfx_requirement (ruff B904)
- Hoist class aliases to module level in tests (ruff N806)

* fix(wxo): resolve mypy, ruff, and lint errors

- Fix dict[str, str] → dict[UUID, str] type annotation for
  raw_name_by_flow_version_id in mapper create/update paths (mypy)
- Use .items() for dict iteration instead of key-only loop (ruff PLC0206)
- Move logger assignment after imports in config.py (ruff E402)
- Remove unnecessary result variable in shared.py (ruff TRY300/RET504)
- Add raise-from chain in tools.py _resolve_lfx_requirement (ruff B904)
- Split long log format string in create.py (ruff E501)
- Hoist class aliases to module level in tests (ruff N806)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* refactor(deployments): Revise v1 deployments API (#12478)

* refactor(deployments): trim top-level deployment API surface for WXO-first flow
- Remove unused deployment stub routes and schemas:
  - drop POST /deployments/{deployment_id}/redeploy and /duplicate handlers
  - remove DeploymentRedeployResponse and DeploymentDuplicateResponse usage
  - remove corresponding route-handler unit tests
- Simplify deployment list response contract:
  - remove deployment_type from DeploymentListResponse
  - update deployments route and base/WXO mapper list shapers to stop passing deployment_type
- Make deployment request schemas API-owned and stricter:
  - replace shared-kernel strict wrappers with DeploymentSpec / DeploymentSpecUpdate
  - remove create/update top-level config and flow-version mutation fields
    (flow_version_ids, add_flow_version_ids, remove_flow_version_ids, config)
  - tighten update validation to only accept spec and/or provider_data
- Align mapper behavior with trimmed API contracts:
  - base mapper create/update now maps spec explicitly to BaseDeploymentData/BaseDeploymentDataUpdate
  - remove base create/update handling for top-level snapshot/config/flow-version passthrough
  - make base util_create_flow_version_ids and util_flow_version_patch return empty results by default
  - update WXO mapper to use explicit spec mapping and provider_data-only operation reconciliation
  - remove WXO 422 guards tied to removed top-level config/flow-version request fields
- Remove obsolete provider_spec plumbing in adapter schema/utilities:
  - drop ProviderSpecModel and T_DeploymentSpec usage from lfx deployment schema
  - make BaseDeploymentData inherit directly from BaseModel
  - remove unused build_agent_payload helper that depended on provider_spec from WXO utils
- Update tests to reflect the trimmed contracts and current behavior:
  - remove schema tests for removed config/flow-version helper models
  - update schema compatibility tests to reject provider_spec in API deployment spec
  - update base mapper tests for new create-result shaping and provider payload validation expectations
  - remove WXO mapper tests asserting rejected top-level config/flow-version inputs
  - adjust WXO mapper naming assertions to current flow/tool naming behavior
  - update sync/service/payload formalization tests for provider_data-driven operations and provider_spec removal

* refactor(deployments): normalize deployment metadata and list payload contracts
- Source `get_deployment` name/description/type/timestamps from DB deployment rows and stop injecting `resource_key` into provider payloads.
- Clarify schema docs that `resource_key` is provider-originated but Langflow-owned once persisted.
- Replace `DeploymentConfigListItem`/`DeploymentSnapshotListItem` response envelopes with paginated `provider_data` payloads in API schemas.
- Update base deployment mapper to serialize config/snapshot items directly into paginated `provider_data.configs` and `provider_data.snapshots`.
- Add WXO API payload models and validation slots for config-list and snapshot-list provider result metadata.
- Add WXO adapter payload result contracts (`WatsonxConfigListResultData`, `WatsonxSnapshotListResultData`) and register them in deployment payload schemas.
- Update WXO service list-config/list-snapshot flows to parse and emit normalized provider result metadata (tenant scope now `{}`, deployment scope includes `deployment_id` and optional `tool_ids`).
- Remove `provider_data` from `lfx` `ConfigListItem` to align with the new list response contract.
- Update mapper registration tests to assert config/snapshot payload slots are wired.
- Update route-handler tests for new provider-result expectations and add regression coverage that `resource_key` is not injected into `provider_data`.
- Rewrite deployment schema tests around provider-data-only config/snapshot list responses and field-surface checks.
- Update WXO service tests for tenant-scope provider-result normalization.
- Update `lfx` schema tests to remove obsolete config-item `provider_data` assertions.

* feat(deployments): add WXO mapper shaping for config-list and snapshot-list responses
- Add `shape_config_list_result` and `shape_snapshot_list_result` to WXO
  deployment mapper with pagination, slot validation, and HTTP 500 on
  malformed payloads.
- Add `WatsonxApiConfigListItem` and `WatsonxApiSnapshotListItem` strict
  payload models with string normalization validators.
- Wire `configs` and `snapshots` item lists into the existing
  `WatsonxApiConfigListProviderData` and `WatsonxApiSnapshotListProviderData`
  provider-data envelopes.
- Add mapper unit tests for config-list slot validation, snapshot-list
  connections extraction, and malformed-payload rejection.
- Remove obsolete `resource_name_prefix` and `connections` from
  deployment-sync update test fixture.

* feat(deployments): add provider identity to responses, nest execution endpoints under deployments
Surface provider_id and provider_key on all deployment responses so
clients can identify which provider owns a deployment without a side
lookup. Nest execution endpoints under their parent deployment path
and drop redundant deployment_id / provider_id parameters that clients
previously had to supply.
Deployment responses:
- Add provider_id (UUID) and provider_key (str) to _DeploymentResponseBase,
  propagating to DeploymentGetResponse, DeploymentCreateResponse,
  DeploymentUpdateResponse, DeploymentStatusResponse, and DeploymentListItem.
- Add provider_key parameter to BaseDeploymentMapper.shape_deployment_create_result,
  shape_deployment_update_result, and shape_deployment_list_items.
- Update WatsonxOrchestrateDeploymentMapper overrides to match.
- Update resolve_adapter_from_deployment to return (row, adapter, provider_key)
  and resolve_adapter_mapper_from_deployment to return
  (row, adapter, mapper, provider_key).
- All route handlers pass provider_key through to shape methods and
  response constructors.
Execution endpoints:
- POST /executions → POST /deployments/{deployment_id}/executions.
  deployment_id moves from the request body to the URL path.
- GET /executions/{execution_id}?deployment_id=...
  → GET /deployments/{deployment_id}/executions/{execution_id}.
  deployment_id moves from a query param to the URL path.
- Remove deployment_id from ExecutionCreateRequest schema.
- Remove unused resolve_adapter_mapper_from_provider_id import.
Tests:
- Update all mapper tests to pass provider_key and assert provider_id /
  provider_key on shaped responses.
- Update route handler mocks for new helper return tuple sizes.
- Remove deployment_id from ExecutionCreateRequest test constructions.
- Fix pre-existing broken test_deployments_response_mapping.py (was
  importing removed to_deployment_create_response helper; now uses
  BaseDeploymentMapper.shape_deployment_create_result).

* refactor(deployments): delegate conflict error messaging to provider mappers
Move provider-specific conflict formatting out of shared helpers by adding a base mapper hook and a Watsonx override, thread mapper-aware conflict formatting through adapter error handling, and add tests for mapper delegation and fallback behavior.

* refactor(deployments): rename provider-account API fields and make update identifiers immutable
- rename provider-account API fields for cleaner contracts:
  - create/get: provider_tenant_id -> tenant_id
  - create/get: provider_url -> url
  - keep provider_key and provider_data unchanged
- enforce immutable provider-account identifiers on PATCH:
  - remove tenant_id and url from DeploymentProviderAccountUpdateRequest
  - remove update-path URL allowlist validation
  - trigger update credential verification only when provider_data changes
  - limit provider-account updates to name and provider_data
- simplify mapper update behavior:
  - resolve_provider_tenant_id now uses tenant_id parameter naming
  - remove no-op WXO resolve_provider_account_update override
  - remove dead auth_utils/decrypt path in WXO update verification
- align frontend deployment-provider-account usage with new API fields:
  - POST payload now sends url
  - ProviderAccount/ProviderCredentials now use tenant_id/url
  - deployment provider UI components now read/write url consistently
- update deployment mapper rules and backend tests for new contract
- verify with targeted backend tests: 245 passed

* fix(deployments): flatten API payloads, normalize execution routes, and trim null response fields
- flatten v1 deployment API request contracts by removing nested `spec` from create/update payloads
  - create now uses top-level `name`, `description`, `type`
  - update now uses top-level `name`, `description`, `provider_data`
- update backend handlers and mapper wiring to consume top-level deployment fields end-to-end
- fix execution route paths to avoid duplicated segment:
  - `/api/v1/deployments/{deployment_id}/executions`
  - `/api/v1/deployments/{deployment_id}/executions/{execution_id}`
- align frontend execution query hooks/chat flow with deployment-id path params and remove provider-id execution params
- add response null-trimming tweaks in deployment read/list endpoints:
  - set `response_model_exclude_none=True` on deployment list route
  - set `response_model_exclude_none=True` on deployment get route
  - normalize empty/non-dict `provider_data` to `None` before shaping detail response
- tighten watsonx API mapper payload contract:
  - type `connections.raw_payloads[*].environment_variables` as `dict[EnvVarKey, EnvVarValueSpec]`
  - remove unused API-level `provider_config` field from update raw connection payload
- refresh backend/frontend tests and deployment endpoint docs to match current API shapes and routes

* feat(deployments): redesign list contracts and normalize WXO payloads
Rework deployment list and flow attachment contracts to support flow-filtered list responses and lazy flow-version lookup in the frontend deploy dialog. Normalize WXO provider payload shapes by flattening provider entries, renaming identifier fields, and tightening adapter/API validation.
- replace deployment list-item matched_attachments with conditional flow_version_ids (omitted when no flow filter is provided)
- add flow_ids support to GET /api/v1/deployments/{id}/flows and propagate it through deployment sync helpers and flow_version_deployment_attachment CRUD filters
- flatten load_from_provider deployment entries, rename provider resource_key to id, and validate WXO entries with explicit fields
- rename WXO provider_data snapshot_ids to tool_ids for provider list/config metadata payloads
- inline WXO provider list entry model_validate payload construction for cleaner mapper logic
- add deployment description max-length contract in adapter/API schemas and enforce it in deployment CRUD create/update paths
- update frontend deploy-choice dialog to a two-request flow (list deployments first, fetch /flows on selection) and align FE deployment types/query hook params
- refresh mapper/route/schema/sync/frontend tests and add dedicated CRUD tests for deployment description length validation

* feat(deployments): align WXO provider_data list and connection payload contracts
- make provider-only deployment list responses use provider_data.deployments and omit top-level deployments/pagination fields
- rename config/snapshot provider_data list keys to connections and tools, and move page/size/total into provider_data
- remove provider_data.deployment_id from config/snapshot list payload shaping
- rename API connection input fields from raw_payloads to key_value and environment_variables to credentials
- add explicit credential item model (key/value/source) with duplicate-key validation
- map API credentials list back to adapter environment_variables dict in mapper for adapter compatibility
- make shared pagination fields optional in deployment response schemas and update config-list provider_data description to "connections"
- set response_model_exclude_none on /configs and /snapshots list routes to suppress unused top-level null pagination fields
- apply formatter quote normalization in WXO config validation debug log

* feat(deployments): add connection type to config listings, enforce security_scheme, and trim leaked adapter fields
- Config list items now expose a `type` field derived from the provider's
  `security_scheme`, enabling callers to distinguish connection types
  (e.g. `key_value_creds`) without a separate lookup
- Requests that return provider_data without a valid `security_scheme`
  now fail fast with HTTP 500 instead of silently omitting the type
- Config list responses no longer leak `tool_ids` or `deployment_id` —
  these were adapter-internal fields with no frontend or API consumers
- Snapshot list responses no longer leak `deployment_id`

* feat(deployments): flatten WXO API operations into per-entity keyed fields
Replace the discriminated `operations` array with explicit per-entity
fields for better type safety and developer experience:
- Create: `add_flows` + `upsert_tools` (create-only item, no remove_app_ids)
- Update: `upsert_flows` + `upsert_tools` + `remove_flows` + `remove_tools`
Each item carries its own add/remove app-id deltas instead of relying on
op-tag dispatch. The mapper consumes the new shapes directly and produces
unchanged adapter-layer operations, eliminating all isinstance dispatch.
Also corrects FlowVersionPatch semantics: upsert_flows.remove_app_ids
now only unbinds connections without detaching the flow from the agent;
detachment is exclusively driven by remove_flows.

* feat(deployments): align watsonx create/update payloads and created-tools responses
- Add `resource_key` to deployment create/update API responses and mapper output.
- Replace API-facing `tool_app_bindings` with `created_tools` in watsonx mapper responses.
- Shape `created_tools` from created snapshot/tool refs only (not all referenced tools).
- Flatten request `provider_data.connections` from nested `connections.key_value` to a direct list.
- Add duplicate `connections.app_id` validation using Counter and update related validation messages.
- Document connection typing strategy: implicit key_value today, future type field with default.
- Add strict `flow_version_id` validation on `WatsonxApiCreatedTool`
  (accept UUID or UUID string; reject other types/invalid UUIDs).
- Update mapper/unit tests for new response contract, flattened connections, and non-UUID rejection.

* fix(deployments): allow watsonx updates without llm
Make PATCH provider_data.llm optional in the Watsonx deployment flow while preserving create-time llm requirements.
- allow missing llm in the API update payload model
- only include llm in mapper-built provider payloads when explicitly provided
- remove adapter update-schema validation that required llm for update operations
- clarify validator docs for empty/no-op provider_data handling
- update mapper/service/schema tests to accept update payloads without llm

* fix(deployments): normalize wxo config list to connection_id/app_id and fix provider_accounts naming
Align the config-list contract end-to-end so that items are keyed by
connection_id + app_id (matching the upstream SDK model) instead of
opaque id/name pairs derived from dict introspection.
Backend:
- Reshape mapper/payloads to emit connection_id/app_id/type.
- Replace loose dict/model_dump parsing with strict ListConfigsResponse
  type checks; add _build_config_list_item factory and
  _normalize_optional_text with documented SDK quirk guards.
- Enrich deployment-scope configs with security_scheme type via
  get_drafts_by_ids.
- Rename DeploymentProviderAccountListResponse.providers to
  provider_accounts for consistency with the entity name.
- Remove leftover debug print/hardcoded requirements in core/tools.
Frontend:
- Migrate DeploymentConfigItem to connection_id/app_id/type and
  unwrap provider_data.connections in the query hook.
- Access provider_accounts instead of providers on the list response.
- Add onBlur confirm for editable tool names; fix overflow/truncation
  in the connection panel.
Tests:
- Use real ListConfigsResponse SDK model in service tests.
- Add scope-shape consistency, type preservation, dict-filtering, and
  mapper contract tests.

* fix(deployments): remove stale-tool fallback in list_snapshots and log unresolved IDs
Replace the phantom-stub fallback that synthesized SnapshotItems for
agent-referenced tool IDs when get_drafts_by_ids returned no results.
Those tools were likely deleted on the provider, and returning stubs
with the ID as the name and empty connections masked stale references
and risked corrupting downstream attachment sync.
Now returns only snapshots that actually resolve from the provider and
logs a warning with the stale tool IDs for observability. Also handles
partial resolution (some tools found, some not).
Update existing test to provide real tool data and add coverage for
all-stale and partial-resolution scenarios.

* fix(deployments): fail fast on invalid wxo config entries and trust provider identifiers
Fail fast when wxO returns unexpected config-list entry types, and preserve provider-provided connection/app identifiers without local normalization or deduplication. Also narrow conflict-detail mapping for connection errors to real conflict messages and update tests to match the new behavior.

* refactor: move tenant_id from top-level into provider_data for provider accounts
tenant_id is a provider-specific concept (e.g., WXO tenant vs Azure AD
tenant vs AWS account_id) and does not have universal semantics across
deployment providers. Moving it into provider_data keeps the top-level
API surface limited to Langflow-universal fields (id, name, provider_key,
url, timestamps) and lets each provider define its own metadata shape.
API schema changes:
- DeploymentProviderAccountCreateRequest: remove top-level tenant_id
  field; tenant_id now arrives inside provider_data alongside api_key
- DeploymentProviderAccountGetResponse: remove top-level tenant_id;
  add provider_data field for non-sensitive provider metadata (e.g.
  {"tenant_id": "..."} for WXO); credentials are excluded
- DeploymentProviderAccountUpdateRequest: unchanged (already uses
  provider_data for credential rotation)
Base mapper (base.py):
- resolve_provider_tenant_id: signature changed from (provider_url,
  tenant_id) to (provider_url, provider_data); delegates to new
  resolve_provider_tenant_id_from_data() for extraction/validation
- shape_provider_account_response: now includes provider_data via new
  shape_provider_account_provider_data() method that returns non-sensitive
  metadata (tenant_id when present)
WXO mapper (watsonx_orchestrate/mapper.py):
- resolve_provider_tenant_id: updated signature; extracts tenant_id
  from provider_data first, falls back to URL extraction
- _validate_provider_data: strips mapper-owned metadata keys (tenant_id)
  before passing to WatsonxVerifyCredentialsPayload slot (extra=forbid)
- New _credential_provider_data() helper for metadata/credential separation
Route + helpers:
- deployments.py create_provider_account: passes payload.provider_data
  (not payload.tenant_id) to resolve_provider_tenant_id
- helpers.py resolve_provider_tenant_id: updated parameter from
  tenant_id to provider_data
Frontend:
- ProviderAccount type: replaced tenant_id with optional provider_data
RULES.md: updated credential flow and defense-in-depth sections to
reflect provider_data as the source for provider metadata.
Tests: updated schema, base mapper, WXO mapper, and route handler tests
to use provider_data for tenant_id. Added tests for tenant metadata
passthrough, credential field filtering, and top-level tenant_id
rejection. Fixed two pre-existing test stubs missing resource_key.
No DB model or migration changes -- provider_tenant_id column remains;
the mapper extracts it from provider_data and stores it as before.

* fix: follow deployment boundary rules and fail fast (wxo) (#12539)

* feat(wxo): filter configs to key_value_creds, surface type and environment, harden mapper contract
Service layer (service.py):
- Filter list_configs to only return connections with security_scheme == "key_value_creds" in both tenant and deployment scopes
- Surface `environment` field alongside `type` in provider_data for config list items
- Normalize security_scheme via _normalize_optional_text to handle SDK enum/tuple quirks
- Extract _warn_if_expected_ids_missing helper to deduplicate staleness warnings
- Remove defensive isinstance checks on provider responses (trust the provider)
- Replace conflicting-binding error with last-write-wins (app_to_connection_id.update)
- Deduplicate connection IDs before calling get_drafts_by_ids
Mapper layer (mapper.py):
- Fail fast with HTTP 500 if config list item is missing a truthy `type`
- Conditionally include `environment` in shaped config list payload
Payloads (payloads.py):
- Add `environment: str | None` field to WatsonxApiConfigListItem with normalizing validator
Tests:
- Add test for deployment-scope key_value_creds filtering (mixed security schemes)
- Add test for tenant-scope key_value_creds filtering (oauth2 excluded)
- Add test for environment metadata passthrough in mapper and service
- Add tests for provider failure paths (tenant list failure, None response, tool fetch failure)
- Add tests for edge cases (latest-binding-wins, skip enrichment with no connections, malformed detailed connection)
- Update mapper tests to assert fail-fast on missing type
- Update existing fixtures to include security_scheme where required by new filtering

* refactor(deployments): move flow-version tool_name into provider_data
Move provider tool_name from a top-level flow-version response field into
provider_data, aligning API ownership boundaries for provider-originated
non-persisted fields.
- Remove top-level tool_name from DeploymentFlowVersionListItem
- Add tool_name to WatsonxApiDeploymentFlowVersionItemData with normalization
- Update WXO mapper to shape tool_name under provider_data
- Update frontend attachments type and consumer to read provider_data.tool_name
- Update backend tests for new response contract location
- Add explicit RULES.md requirement for non-persisted provider data placement

* make environment required

* refactor list configs method and fix broken tests

* feat(deployments): type-to-confirm dialog for deployment deletion (#12546)

* feat(deployments): replace simple delete confirm with type-to-confirm dialog

Deleting a deployment is irreversible — it removes the agent from both
Langflow and Watsonx Orchestrate permanently. Require the user to type
the deployment name before the Delete button activates, matching
industry-standard patterns (GitHub, AWS).

- Add `TypeToConfirmDeleteDialog` component (deployment-specific, not shared)
- Input resets on close; label + placeholder for accessibility
- Replace `DeleteConfirmationModal` in `deployments-content.tsx`
- 6 unit tests covering all confirmation behaviours

* fix(deployments): address PR review findings on type-to-confirm dialog

- Fix icon spacing: replace pr-1 with mr-2 on AlertTriangle (padding
  was compressing the SVG viewport instead of creating sibling spacing)
- Fix label copy: "agent name" → "deployment name" to cover both agent
  and MCP deployment types
- Remove unused cancelDelete from useDeleteWithConfirmation — callers
  close the dialog via setModalOpen; the export was dead code
- Add case-sensitivity test to type-to-confirm-delete-dialog tests
- Add unit tests for useDeleteWithConfirmation hook (8 tests covering
  requestDelete, confirmDelete, onSettled, onError, and setModalOpen)

* test(deployments): add comprehensive frontend test suite for WXO deployments (#12535)

* test(deployments): add unit tests for all deployment API query and mutation hooks

16 test files covering deployment queries, mutations, provider accounts,
execution hooks, and env var detection (59 tests total).

* test(deployments): add stepper context create-mode tests and expand edit-mode/tool-naming coverage

Step 2 of the frontend deployment test plan — 63 new tests (93 total)
covering create-mode payload builders, step validation, provider
selection, multi-flow scenarios, partial update payloads, detach/re-attach
flows, and tool naming edge cases.

* test(deployments): add component rendering tests for Step 3 (163 tests, 10 files)

Covers tables, stepper steps, connection panel, and modals with data-testid additions to source components for reliable targeting.

* test(deployments): add custom hook unit tests for Step 4 (121 tests, 7 files)

Covers useErrorAlert, useProviderFilter, useNavigateToTest, useDeleteWithConfirmation,
useTestDeploymentModal, useDeploymentChat (polling, thread_id, timeouts, tool traces),
and watsonx-result-parsers (extractText, extractToolTraces, extractThreadId).

* test(deployments): add deploy button and choice dialog unit tests for Step 5 (76 tests, 3 files)

- deploy-button.test.tsx (12 tests): feature-flag rendering, disabled states
  (no flow/preparing/dialog open), handleDeploy click, animate-pulse on icon
- deploy-choice-dialog/index.test.tsx (30 tests): phase transitions
  (provider → deployments → review → update), auto-select single attachment,
  provider key mapping, patchSnapshot args, update/error flows, onUpdateComplete
- deploy-choice-dialog/hooks/use-prepare-deploy.test.ts (34 tests): handleDeploy
  save/snapshot/provider fetch, no-flow bail-out, deployModal vs choiceDialog
  branching, error handling, handleChooseNew, handleUpdateComplete, resetChoiceState

* test(deployments): add E2E Playwright tests for Step 6 (28 tests, 5 files)

- deployments-page.spec.ts: page nav, empty/loaded states (5 tests)
- deployment-create.spec.ts: full create wizard, POST, deploy status (6 tests)
- deployment-edit.spec.ts: edit mode, PATCH, cancel (5 tests)
- deployment-providers.spec.ts: add/delete providers, confirmation (6 tests)
- deployment-test-modal.spec.ts: chat, polling, multi-turn, reset (6 tests)

Add shared deployment-mocks.ts for mock data reuse across all specs.

Add data-testid to stepper modal title, add-provider modal title, and
test-deployment modal title to avoid strict-mode selector violations.

Add data-testid to provider radio items in step-provider.tsx.

Set LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true in CI workflow env.

* fix(tests): update deployment E2E mocks for wxo-fe API changes

- provider_accounts field rename: { providers } → { provider_accounts }
- ProviderAccount.provider_url → ProviderAccount.url
- Execution endpoints moved to deployment-scoped URLs:
  POST /deployments/executions → /deployments/{id}/executions
  GET /deployments/executions/{exec_id} → /deployments/{id}/executions/{exec_id}
- Remove deployment_id from POST execution request body (now in URL path)

* fix(tests): update unit tests for wxo-fe API shape changes

- ProviderAccount: provider_url → url, removed provider_tenant_id
- Provider list response: { providers } → { provider_accounts }
- ProviderCredentials: provider_url → url
- Deployment payload: spec.{name,description,type} → top-level fields
- Provider data: operations → add_flows/upsert_flows/remove_flows
- Connections: raw_payloads[].environment_variables → connections[].credentials
- DeploymentConfigItem: { id, name } → { app_id, connection_id }

* fix(tests): align frontend tests with revised deployments API shape

Update unit tests, E2E mocks, and the ProviderAccountListResponse type
to match the API changes from the v1 deployments revision (#12478):
- execution endpoints now use deployment_id in URL path
- getExecution uses deployment_id + execution_id (no provider_id)
- provider accounts response key changed to provider_accounts
- deployment configs response wrapped in provider_data
- deploy-choice-dialog now uses useGetDeploymentAttachments

* fix(ci): read LANGFLOW_FEATURE_WXO_DEPLOYMENTS from process.env fallback

Vite config only read feature flags from the .env file via dotenv,
ignoring CI workflow environment variables. This caused all 28
deployment E2E tests to fail because the flag was never enabled.

* fix(tests): add missing resource_key to deployment mapper test mocks

The SimpleNamespace mocks in TestCreateResponse and TestMapperUpdateResult
were missing the resource_key attribute now required by the mapper.

* test(deployments): add E2E tests for type-to-confirm deployment deletion

Adds testid to the delete dropdown item and four Playwright tests covering:
- dialog opens on delete action
- confirm button disabled until name matches exactly
- confirming with correct name calls DELETE /api/v1/deployments/{id}
- cancel dismisses without calling DELETE

* chore: trigger CI

* fix(deployments): remove unused description field from connection panel (#12549)

The Description input in the Create Connection tab was never persisted —
ConnectionItem has no description field and the value was not sent to any API.

* fix(deployments): enforce single-environment filter on deployments page (#12551)

fix(deployments): enforce single-environment filter and reorder provider form fields

Remove the "All environments" global listing mode from the deployments
page. Deployments are now always fetched for a single selected
environment, avoiding N parallel API calls that each trigger a backend
sync. Also differentiates empty states (no providers vs no deployments)
and reorders the provider credentials form to show URL before API key.

* be test

* be test

---------

Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
Co-authored-by: Hamza Rashid <hzarashid@gmail.com>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
2026-04-08 10:00:41 +00:00
f8aa12d404 fix(ci): add missing SDK build step to cross-platform workflow_dispatch (#12536)
The build-if-needed job (used for workflow_dispatch) was not building
the langflow-sdk package. Since lfx depends on langflow-sdk>=0.1.0
and langflow-sdk is not yet published to PyPI, all test jobs failed
during lfx installation with 'No solution found'.

Changes:
- Build langflow-sdk wheel in build-if-needed job
- Upload SDK artifact and output sdk-artifact-name
- Update SDK download conditions with build-if-needed fallback
- Update SDK+LFX combined/individual install conditions to properly
  route through the combined installer when both are available
2026-04-06 17:29:14 -04:00
4ee94e6ce6 fix(ci): add missing lfx build step to cross-platform workflow_dispatch (#12524)
The build-if-needed job (used by workflow_dispatch) was missing the
lfx package build, causing all cross-platform tests to fail with:

  'No solution found: lfx>=0.4.0 required but only <=0.3.4 available'

Changes:
- Build lfx wheel in build-if-needed job
- Upload lfx artifact (adhoc-dist-lfx)
- Add lfx-artifact-name to job outputs
- Update all test jobs to fallback to build-if-needed outputs
  for lfx artifact (matching existing base/main pattern)
2026-04-06 15:58:54 -04:00
68642a8a86 fix: Properly grep for the langflow version (#12486)
* fix: Properly grep for the langflow version

* Mount the sdk where needed

* Skip the sdk
2026-04-03 15:02:11 +00:00
abd772f780 fix: Build and install the langflow-sdk for lfx (fixes nightly) (#12481)
* fix: Build and install the langflow-sdk for lfx

* Publish sdk as a nightly

* Update ci.yml

* Update python_test.yml

* Update ci.yml
2026-04-03 05:59:09 +00:00
9f255dc851 chore: remove mypy from CI (#12448)
chore: remove mypy from CI and dev dependencies

mypy hasn't caught issues in a long time due to its lenient config
(follow_imports=skip, ignore_missing_imports=true). We evaluated ty as
a replacement but it lacks Pydantic and SQLModel support, producing too
many false positives. Removing the type checker until a viable
alternative matures.
2026-04-01 20:35:44 +00:00
4e449da9da fix: update npm dependencies (#12412)
* fix(docs): update fast-xml-parser to 4.5.4 to address security vulnerability

- Add override for fast-xml-parser to force version 4.5.4
- Fixes entity encoding bypass via regex injection in DOCTYPE entity names
- Addresses CVE in transitive dependency from redocusaurus

* ci: update merge-frontend-coverage if

update merge-frontend-coverage if to not run during doc changes

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
Co-authored-by: Adam Aghili <Adam.Aghili@ibm.com>
2026-03-31 16:43:54 +00:00
ba3472959f docs: CSS redesign (#12306)
* align-copy-page-to-version

* css-changes

* add-tsx-components

* fix-giant-icons

* improve-download-cta-to-align-with-font

* peer-review

* accessibility-scan

* aria-labels-and-roles-for-svgs

* add continue on error on JEST download step

---------

Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
2026-03-31 16:43:32 +00:00
51fd75bb9f perf(test): Optimize CI tests causing timeout on Windows and Python 3.12 (#12405)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-03-31 12:01:29 -03:00
5f2f287bf1 test: add PostgreSQL to migration CI tests (#12257)
* test: add PostgreSQL to migration CI tests

* [autofix.ci] apply automated fixes

* use random pg name

* [autofix.ci] apply automated fixes

* fix: rewrite _get_main_branch_head to use git grep instead of filename parsing

The old approach used --diff-filter=A and extracted revision IDs from
filenames, which broke in two ways: filenames don't always match actual
revision IDs, and modified-only migrations were silently treated as a
no-op. Now uses git grep to read revision/down_revision directly from
origin/main's migration files and computes the head from the chain.

Also skips test_upgrade_from_main_branch with a clear message when main
and branch share the same head, instead of silently doing nothing.

* [autofix.ci] apply automated fixes

* ruff

* Add postgres to install

* [autofix.ci] apply automated fixes

* comp index

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-03-25 13:26:56 +00:00
f625ef29e7 test: add playwright code coverage (#12294)
add playwright code coverage
merge frontend code coverage
use merged frontend for codeCov

add make command
update CI
2026-03-23 20:50:01 +00:00
198fab1dc7 feat: Add Windows Playwright test fixes to RC (#12265)
* feat: Add Windows Playwright tests to nightly builds

- Add windows-latest to typescript_test.yml runner options
- Add shell: bash to all script steps for cross-platform compatibility
- Split Playwright installation into OS-aware steps (Linux uses --with-deps, Windows/macOS/self-hosted don't)
- Fix artifact naming with OS prefix to prevent conflicts: blob-report-${{ runner.os }}-${{ matrix.shardIndex }}
- Split frontend-tests into separate Linux and Windows jobs in nightly_build.yml
- Add ref parameter to all test jobs to checkout code from release branch
- Add resolve-release-branch to needs dependencies
- Update Slack notifications to handle both Linux and Windows test results
- Windows tests are non-blocking (not checked in release-nightly-build condition)
- Update .secrets.baseline with new line number (263 -> 347) for LANGFLOW_ENG_SLACK_WEBHOOK_URL

Fixes LE-566

* fix: Use contains() for self-hosted runner detection

- Replace exact string equality (==, !=) with contains() for substring matching
- Fixes issue when inputs.runs-on is array format: '["self-hosted", "linux", "ARM64", ...]'
- Ensures self-hosted Linux runners correctly skip --with-deps flag

Addresses CodeRabbit feedback on PR #12264
2026-03-20 19:22:09 +00:00
5c9bbe580f fix: prevent CI injection via unsanitized GitHub context interpolation (#12224)
Pass github.event.pull_request.head.ref through env: instead of
interpolating it directly into run: shell steps. This prevents bash
from evaluating command substitutions embedded in malicious branch names
before input validation runs.

Co-authored-by: Janardan Singh Kavia <janardankavia@ibm.com>
2026-03-19 19:40:26 +00:00