mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 00:03:32 +08:00
v1.11.0.dev15
18149 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 0b8da894fd | Update version for nightly v1.11.0.dev15 | |||
| 2b7b113049 |
fix: warn on empty value and decrypt failure in get_all variable listing (#13741)
Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local> |
|||
| 2ff192129a | docs: add lfx-nextplaid bundle (#13679) | |||
| 59e03266c7 |
feat(lfx): verified-JWT identity forwarding for lfx serve (#13632)
* feat(lfx): verified-JWT identity forwarding for lfx serve
Add an optional per-user identity layer to `lfx serve` so the edge gateway's
authenticated caller is threaded into flow execution as `user_id`. Identity
must arrive as a cryptographically verifiable JWT by default; plain gateway
headers are accepted only under an explicit, weaker trust mode.
- serve_identity.py: IdentityConfig (off / jwt / header modes, env round-trip)
and IdentityVerifier (pyjwt) with algorithms pinned to RS256/ES256 (rejects
alg:none and HMAC alg-confusion before any crypto), exact iss, aud
containment, exp/nbf with skew, and a per-process TTL'd JWKS cache with
startup prefetch, rate-limited refresh, and keep-cache-on-fetch-failure. The
raw token never reaches a log line or error body.
- Wiring: /flows/{id}/run and /stream gain an identity dependency that
sub-depends on verify_api_key, so identity only annotates requests that
already cleared the serve-key floor. execute_graph_with_capture threads the
resolved user_id through run + stream paths and graph.user_id.
- CLI options propagated to uvicorn workers via LFX_SERVE_IDENTITY_* (each
worker keeps its own JWKS cache).
- Startup notices (failed JWKS prefetch, header-mode trust warning) are emitted
on a guaranteed stderr console as well as the logger, since the structlog
logger is not reliably surfaced on the serve stdout path under uvicorn.
Tests use a locally generated RSA keypair and an in-memory fake JWKS (no
network) covering off-mode regression, identity threading, the reject matrix,
rate-limited refresh, warm-cache resilience, prefetch recovery, header mode,
the serve-key floor, multi-worker env round-trip, and token-never-logged.
* [autofix.ci] apply automated fixes
* fix(lfx): require HTTPS for JWKS/issuer and fail closed on malformed JWKS
Security: plaintext http:// JWKS/issuer URLs are MITM-able — an attacker who
tampers with the response serves their own JWKS and forges tokens with the
expected iss/aud. Require https:// by default for the JWKS URL and the
issuer-derived OIDC discovery URL (and the discovered jwks_uri), with an
explicit --identity-jwt-allow-insecure-http / LFX_SERVE_IDENTITY_ALLOW_INSECURE_HTTP
escape hatch for local/dev. Non-http(s) schemes remain refused.
Availability: malformed external JSON (a JWKS or OIDC discovery doc that is a
JSON array rather than an object, or a non-string jwks_uri) raised an uncaught
AttributeError out of prefetch/request handling — aborting startup or 500ing a
request instead of failing closed. Guard the shapes and raise IdentityConfigError
so they funnel into the existing fail-closed (401) path.
Also adds ES256 unit coverage (the second accepted algorithm was only exercised
manually before).
Tests: +14 (HTTPS policy + escape hatch, malformed-shape fail-closed, ES256).
* fix(lfx): resolve identity OptionInfo leak and ANSI-fragile help assertion
serve_command declared the identity params as typer.Option(...). Direct
(non-CLI) callers that omit them got the raw OptionInfo as the value, so
IdentityConfig(mode=<OptionInfo>) raised on every serve. Convert them to
plain defaults (the CLI options live on serve_command_wrapper), completing
the pattern already applied to identity_jwt_allow_insecure_http.
Also strip ANSI before the serve --help substring assertion: rich colorizes
each hyphen-segment of an option name, so '--identity-mode' is never a literal
substring when color is forced (as in CI).
Additionally reject whitespace-only identity claims, not just empty/None, and
cover it with a parametrized test.
* Fix jwks fetch to use httpx client, avoiding redirects
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
|
|||
| 2b8f7f90a0 |
fix: Raise on an undecryptable secret at the variable-resolution (#13681)
fix: Raise on an undecryptable secret at the variable-resolution boundary Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local> |
|||
| b033bfa22b |
feat(auth): external trusted JWT auth + JIT user mapping + access ceiling (#13293)
* feat(auth): external trusted JWT auth + JIT user mapping Adds the OSS half of trusted external identity support: - New EXTERNAL_AUTH_* AuthSettings (off by default) covering provider key, token transport (header/cookie), JWKS or trusted-decode, claim mapping, and a pluggable EXTERNAL_AUTH_IDENTITY_RESOLVER import path. - New services/auth/external.py with JWT/JWKS validation, identity resolver protocol, and token extraction helpers. - AuthService.get_or_create_user_from_claims + extract_user_info_from_claims implement the existing BaseAuthService JIT hook through SSOUserProfile - no new tables. - _authenticate_with_token falls back to external resolution when the native JWT path fails, so Authorization-header callers transparently upgrade to external auth. - Token extractors in services/auth/utils.py consult the configured external header/cookie after the native JWT path on session, WebSocket, SSE, and optional-user dependencies. - /api/v1/session catches AuthenticationError so external-credential failures resolve to authenticated=False rather than 500. Tests: 14 unit tests for external.py (JWT decode, claim mapping, custom resolver) plus 3 integration tests in test_login.py exercising the session endpoint JIT path (header + cookie + expired-token). Co-Authored-By: phact <estevezsebastian@gmail.com> Co-Authored-By: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Based-On: langflow-ai/langflow#13280 * Update src/backend/base/langflow/services/auth/external.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(auth): refetch JWKS once on unknown kid to survive IdP key rotation Without this, a token signed by a newly rotated IdP key is rejected for up to JWKS_CACHE_TTL_SECONDS (5 min) because the cached JWKS predates the key. On a kid miss we now refetch the JWKS once, rate-limited to one forced refresh per 30s per URL so attacker-supplied kids cannot hammer the IdP's JWKS endpoint. Adds JWKS-path tests (signature verification, rotation refetch, rate-limited refresh) that were previously uncovered. * feat(auth): add external access ceiling for trusted auth (#13637) * feat(auth): add external access ceiling * fix(auth): tolerate minimal external access settings * fix(auth): address external-auth review (require JWKS audience, untangle authz/auth) Resolves Gabriel's PR review feedback on #13293: - Require EXTERNAL_AUTH_AUDIENCE on the JWKS verification path. Previously a JWKS-only config verified signature + exp but left aud/iss unbound, so a token the same IdP minted for a *different* relying party was accepted. decode now fails closed (before any network fetch) with an actionable message and binds aud; iss stays verified-when-set. Adds wrong-aud / missing-aud tests. - Move the request-scoped action ceiling out of auth/external.py into a new authorization/access_ceiling.py so the authorization package no longer imports the auth layer (the only such import). Guards consult the authz-owned primitive; the auth layer only derives the ceiling and installs it. external.py re-exports the names for callers that derive/inspect the ceiling. - _unique_external_username reuses _external_username_fallback instead of re-implementing the provider-digest formula. - Read the non-optional EXTERNAL_AUTH_* settings as plain attributes (drop getattr(..., default), which re-hardcoded Field defaults) in service.py and api_key/crud.py. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(authz): pass API key context to authorization (#13639) * chore: bump version to 1.10.1 * fix(lfx): restamp component index version after 1.10.1 fork bump (#13575) Companion to the release-1.11.0 fix (#13574). The 1.10.1 fork bump left component_index.json's version at 1.10.0; _read_component_index fails closed on the exact-version mismatch, so the bundled registry loads as None and the upgrade-gate LFX tests fail on this branch's CI. Surgical restamp: version -> 1.10.1, sha256 recomputed with the build script's exact hashing/serialization. Entries unchanged; 2-line diff. The three affected tests pass locally. * fix(ci): rebalance backend test splits with measured durations + raise unit-test step timeout (1.10.1) (#13585) fix(ci): rebalance backend test splits with measured durations + raise unit-test step timeout Unit Tests - Python 3.12 - Group 3 has been failing at 98-99% on the nightly: the job hits the nick-fields/retry 40-minute per-attempt timeout and pytest is SIGTERM'd mid-test, so it looks like one flaky last test when it is actually a deterministic timeout (and the internal retry plus run-level retries can never succeed). Root cause is twofold: 1. .test_durations was last regenerated ~May 2025 and covered only 2,219 of ~9,500 current unit tests, so pytest-split weighted 77% of the suite at the 0.73s average. The expensive client-fixture tests (test_webhook.py, test_login.py - each pays a full create_app + lifespan boot per test, 60-120s late in a CI run) clustered into group 3's tail, making it ~8-10 minutes slower than its siblings (33:15 on 3.13, >40min on 3.12 which is additionally slowed by astrapy disabling SSL connection reuse on Python 3.12.0-11). The weekly store_pytest_durations workflow that should refresh the file has been dying at the 6-hour GitHub job limit every week (serial full-suite run no longer fits), so the file silently froze. This regenerates the file from real per-test wall-clock measured in the passing Python 3.13 nightly jobs (run 27246228762 attempt 1, all 5 groups, parsed from the -vv xdist logs: per-worker start-to-start deltas). 9,508 tests now have measured durations; old entries are kept where no new measurement exists. Simulated least_duration split goes from one outlier group to 5 even groups (~24.6 min each) with the >50s tests spread 1-2 per group instead of 7 in one. 2. timeout_minutes 40 -> 50 gives headroom for matrix-cell variance (3.12 runs ~20% slower than 3.13) so a slightly slow cell degrades gracefully instead of burning 2x40min and failing the whole run. Follow-ups (not in this PR): fix store_pytest_durations to run with xdist or split groups so it fits the 6h limit; investigate the in-worker degradation that makes client-fixture boots cost 8-12s early in a run but 60-120s after ~30 minutes. * fix(test): raise spawn-child join timeout in test_multi_process_visibility (1.10.1) (#13590) fix(test): raise spawn-child join timeout in test_multi_process_visibility The test spawns a child via multiprocessing spawn context, which cold-imports the full langflow package (plus coverage's multiprocessing hooks in CI) before appending a single event. The 10s join timeout is routinely exceeded on a loaded CI runner sharing 4 vCPUs with a second xdist worker: in nightly run 27253229568 (Unit Tests - Python 3.12 - Group 5) the test failed all 12 executions (5 reruns x 2 step attempts), each rerun exactly 10s apart - the join deadline, not a product bug. Raise the liveness bound to 60s (join returns immediately when the child exits, so the passing case is unaffected) and kill the child on timeout so a hung spawn can't leak into later tests. * fix(ci): anchor langflow-base version extraction in nightly docker build (1.10.1) (#13593) fix(ci): anchor langflow-base version extraction in nightly docker build The first nightly on the 1.11 workspace (tag v1.11.0.dev0, run 27253229568) failed in Build Nightly Base Package within seconds: 'Base version format is incorrect'. The extraction (uv tree, grep langflow-base unanchored, awk field 3, first line) broke because uv tree now prints the langflow-base workspace-root line ('langflow-base v1.11.0.dev0', two fields) before any dependency line ('langflow-base[complete] v1.11.0.dev0' under the langflow root, three fields), so the first match yields an empty field 3 and the format check exits 1. uv tree's stderr is discarded, which made the real cause invisible in CI. Anchor to the root line and take field 2 instead - the exact pattern the langflow (main package) extraction in this same file already uses, which is why the main-package builds passed on the same tag. Verified both extractions print 1.11.0.dev0 against a local checkout of v1.11.0.dev0. * fix(test): gate models.dev background refresh out of tests (1.10.1) (#13598) fix(test): gate models.dev background refresh out of tests Integration tests failed twice in nightly run 27260425158 with pyleak EventLoopBlockError - first Integration Tests 3.14, then 3.12 on the rerun, each time in a different test. The blocking stack points at refresh_models_dev_periodically: every app boot unconditionally starts a lifespan task that immediately fetches https://models.dev/api.json, so the request lands mid-test in whatever test happens to be running. Under pyleak's asyncio debug instrumentation the fetch blocked the loop 0.797s against a 0.2s threshold. Whichever test draws the short straw flakes - which is why it looked transient and moved between versions. Add a LANGFLOW_MODELS_DEV_REFRESH env gate (default unchanged: enabled) and disable it session-wide in the backend test conftest. Tests fall back to the bundled static model lists, which is also deterministic. Verified: with the gate set, app boot makes zero models.dev requests; the previously failing integration test passes. * fix(ci): allow pre-releases when pinning the nightly in migration validation (1.10.1) (#13601) fix(ci): allow pre-releases when pinning the nightly in migration validation Migration Test: pip/venv (stable -> nightly) failed deterministically on nightly run 27260425158 (twice, including a rerun): hint: langflow-base was requested with a pre-release marker (e.g., langflow-base==1.11.0.dev1), but pre-releases weren't enabled (try: --prerelease=allow) This is the first nightly publishing as a canonical .devN pre-release of the langflow distribution (nightly -> stable bundle cutover). The 'latest' branch of the upgrade step already passes --prerelease=allow, but the pinned-version branches do not. uv implicitly allows the pre-release for the directly requested ==X.Y.Z.devN pin, yet langflow's metadata pins langflow-base==X.Y.Z.devN transitively, and uv rejects transitive pre-releases unless they are enabled - so the install fails after the stable uninstall, sinking the migration test. Add --prerelease=allow to both pinned-version install lines. * fix(ci): scope nightly migration-test pre-releases to the langflow stack (1.10.1) (#13605) fix(ci): scope nightly migration-test pre-releases to the langflow stack The previous fix (#13599) added a global --prerelease=allow, which let UNRELATED dependencies resolve to alphas: on nightly run 27274206250 the stable->nightly upgrade pulled pydantic 2.14.0a1 + pydantic-yaml 1.6.1a1, and the pydantic alpha breaks langchain-core at import time (RunnablePassthrough pydantic ValidationError), failing the nightly boot right after a successful install. Clean installs were fine - only this upgrade path resolved the alpha combo. Scope pre-release eligibility to the langflow lockstep stack instead: uv accepts a pre-release when the package's own requirement carries a pre-release marker, but not via transitive pins, and the nightly chain is langflow -> langflow-base -> lfx -> langflow-sdk with exact ==devN pins. Request each directly; langflow-sdk versions independently (0.2.0.devN), so an explicit .dev0 floor marks it eligible while lfx's exact pin selects the version. The 'latest' branch gets the same treatment via .dev0 floors on all four. Verified by dry-run against PyPI: langflow/langflow-base/lfx at 1.11.0.dev2 + langflow-sdk 0.2.0.dev2 resolve with pydantic staying at stable 2.13.4. * fix(ci): create GitHub releases on the dispatched v-prefixed tag (1.10.1) (#13609) fix(ci): create GitHub releases on the dispatched v-prefixed tag The create_release job passed the bare version (v stripped) as the release tag with no commit target, so when that tag did not exist GitHub minted a new lightweight tag at the default-branch HEAD -- the wrong commit, still carrying the previous version (main only adopts a release's version via the post-release back-merge). Every release since 1.8.2 shipped a stray bare tag (1.8.2, 1.8.3, 1.9.0-1.9.6, 1.10.0) pointing at a previous-version commit, and the GitHub release had to be manually re-pointed to the real vX.Y.Z tag after each release. - create_release now attaches the release to inputs.release_tag for stable releases; pre-releases keep their computed tag (e.g. 1.10.0rc1) but it is minted at the release commit via 'commit:'. - release-lfx.yml pins the minted lfx-v* tag to github.sha instead of the default branch. The validate-tag-format guard (#12847) only blocks at dispatch time; create_release was re-creating the very duplicates it guards against. * fix(ci): resolve validate-version output in release-lfx changelog link (1.10.1) (#13611) fix(ci): resolve validate-version output in release-lfx changelog link The create-release job references needs.validate-version.outputs.current_version in its generated release notes (the Full Changelog compare link), but validate-version was not in the job's needs array, so the expression evaluated empty and the link rendered as compare/v...lfx-vX.Y.Z (broken base). Add validate-version to the create-release needs. This adds no real serialization: create-release already waits on release-lfx, which transitively requires validate-version via run-tests, and the job's always() if-condition is unaffected. Flagged by actionlint: property "validate-version" is not defined in object type {build-docker, release-lfx}. * feat(auth): external trusted JWT auth + JIT user mapping Adds the OSS half of trusted external identity support: - New EXTERNAL_AUTH_* AuthSettings (off by default) covering provider key, token transport (header/cookie), JWKS or trusted-decode, claim mapping, and a pluggable EXTERNAL_AUTH_IDENTITY_RESOLVER import path. - New services/auth/external.py with JWT/JWKS validation, identity resolver protocol, and token extraction helpers. - AuthService.get_or_create_user_from_claims + extract_user_info_from_claims implement the existing BaseAuthService JIT hook through SSOUserProfile - no new tables. - _authenticate_with_token falls back to external resolution when the native JWT path fails, so Authorization-header callers transparently upgrade to external auth. - Token extractors in services/auth/utils.py consult the configured external header/cookie after the native JWT path on session, WebSocket, SSE, and optional-user dependencies. - /api/v1/session catches AuthenticationError so external-credential failures resolve to authenticated=False rather than 500. Tests: 14 unit tests for external.py (JWT decode, claim mapping, custom resolver) plus 3 integration tests in test_login.py exercising the session endpoint JIT path (header + cookie + expired-token). Co-Authored-By: phact <estevezsebastian@gmail.com> Co-Authored-By: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Based-On: langflow-ai/langflow#13280 * Update src/backend/base/langflow/services/auth/external.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(auth): refetch JWKS once on unknown kid to survive IdP key rotation Without this, a token signed by a newly rotated IdP key is rejected for up to JWKS_CACHE_TTL_SECONDS (5 min) because the cached JWKS predates the key. On a kid miss we now refetch the JWKS once, rate-limited to one forced refresh per 30s per URL so attacker-supplied kids cannot hammer the IdP's JWKS endpoint. Adds JWKS-path tests (signature verification, rotation refetch, rate-limited refresh) that were previously uncovered. * fix(components): follow HTTP redirects in URL component with per-hop SSRF revalidation (#13572) * fix(components): follow HTTP redirects in URL component with per-hop SSRF revalidation The URL component sent every request with follow_redirects=False, so any site whose entered URL 301s to its canonical address (http->https or www/non-www normalization) returned the redirect stub - e.g. a bare "301 Moved Permanently / nginx" page - as the scraped content, or failed outright when the redirect response had an empty body. Redirects are now followed via a new advanced "Follow Redirects" input (default on). When SSRF protection is enabled, hops are followed manually and each Location target is re-validated with the same blocked-IP denylist and DNS pinning as the initial request before any connection is made, mirroring the API Request component; cross-host hops drop sensitive headers and chains are capped at 20 redirects. * fix(components): compare full origin when keeping credentials across redirects, crawl from post-redirect base Addresses CodeRabbit review: - _headers_for_redirect now keeps Authorization/Proxy-Authorization/Cookie only for same-origin hops (scheme, host, port) or a direct http->https upgrade on default ports, exactly the cases where httpx keeps the Authorization header. Applied to both the URL and API Request components (the helper was copied from the latter). - _crawl_recursive resolves relative links and the prevent_outside check against the final post-redirect URL, and marks it visited, so depth>1 crawls work on sites that 301 to their canonical address. * chore: regenerate component index and starter projects for release-1.10.1 base The autofix.ci jobs uploaded but never pushed the regeneration after the rebase, leaving the 4 URL-component templates with a stale field_order (missing follow_redirects) and the index without the new code hashes. Generated with the same commands CI uses: LFX_DEV=1 make build_component_index + scripts/ci/update_starter_projects.py. Pokedex Agent / Structured Data Analysis Agent pick up the API Request component's new code_hash from the origin-comparison fix. * [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> * fix(ci): make release Biome lint green — NUL-delimit file list + clear pre-existing lint errors (#13550) The Lint Frontend job runs against `main` as the base. In a release (workflow_dispatch) run it diffs the entire release branch (~915 files) and re-lints nearly the whole frontend, exposing two issues that per-PR linting never hits together: 1. xargs split starter-project spec paths containing spaces (e.g. "News Aggregator.spec.ts" -> "News" + "Aggregator.spec.ts"), producing `internalError/io: No such file or directory`. NUL-delimit the file list so spaces are preserved. (supersedes #13381) 2. The whole-branch diff surfaced 30 pre-existing Biome errors: 22 noExplicitAny + 8 organizeImports. Resolved with real types where safe (freezeObject generic, ColDef defaults, messagesSorter field shape, VertexBuildTypeAPI, Record<string,string>, DragEvent<HTMLElement>, unknown for narrowed values) and justified biome-ignore for genuinely loose cases (polymorphic display values, test global stubs, captured unexported StreamCallbacks). Imports auto-sorted via biome. Verified locally: biome check on the full release-vs-main file set is now 0 errors (was 30); tsc unchanged at its 303-error baseline (no new type errors). * fix: limit public flow endpoint from displaying private flow streams (#13602) * fix: limit public flow endpoint from displaying private flow streams * fix: Address coderabbit reviews --------- Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local> * fix: enforce the FileSystemTool credential deny-list (#13625) * enforce the FileSystemTool credential deny-list * security checks gh review * [autofix.ci] apply automated fixes * feat(authz): pass API key context to authorization * refactor(authz): address review feedback on API-key auth context - Add AuthCredentialContext.from_api_key_result() and use it at all six API-key projection sites (service.py x5, mcp_projects.py) so the caveat fields stay in sync and no site can silently drop one. - authz_me builds the enforce context from the public current_auth_context_for_authz() helper instead of reaching into the private guards._auth_context. - Clear request-local credential context at the top of verify_project_auth to match the service.py entrypoints, so the composer-token fast path can never inherit stale context. --------- Co-authored-by: phact <estevezsebastian@gmail.com> Co-authored-by: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Janardan Singh Kavia <janardankavia@ibm.com> Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local> Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com> * [autofix.ci] apply automated fixes * fix(auth): close access-ceiling bypasses and harden external JWT auth Address review findings on the external trusted-auth + access-ceiling work: - Enforce the access ceiling on execution/mutation paths that bypassed the ensure_*_permission chokepoint: MCP project tool-call (flow execute), flow version snapshot/activate/delete, v1 file upload/delete, and memory base CRUD/ingest. - Move the external-user API-key block into the shared authenticate_api_key chokepoint so /run, v2 workflow, OpenAI-compat, WebSocket, webhook and MCP key auth all enforce it (was only on the JWT-fallback path). - Require exp on both the JWKS and trusted-decode paths; reject non-https JWKS URLs (loopback http allowed for dev). - Normalize EXTERNAL_AUTH_PROVIDER at the config boundary so the API-key floor cannot be silently disabled by an empty/whitespace value. - Make a configured EXTERNAL_AUTH_ACCESS_CLAIM_MAPPING authoritative: an unmapped claim value falls to the default level instead of self-elevating via the built-in alias table. - Stop nulling a stored SSOUserProfile.email when a later token omits email. - Keep the external credential usable as a fallback when a stale/invalid native token is present (WebSocket, SSE, optional-user paths). - Add delete to the editor access level (deploy stays admin-only). Adds regression tests across auth, authz guards, api-key crud, and the newly guarded routes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(mcp): give handle_call_tool flow stub the attrs ensure_flow_permission reads The access-ceiling hardening added an ensure_flow_permission(EXECUTE) guard to handle_call_tool that reads flow.user_id and flow.workspace_id, but the _invoke_handle_call_tool flow stub only carried id/name/folder_id, raising AttributeError. Set user_id to match the current user so the owner-override path is exercised, and add workspace_id. * fix(auth): close remaining ceiling gaps + external-cookie/filesystem/job-queue hardening Synthesizes the external review (P1-P3) with the multi-agent re-review findings. External auth: - P1: regular HTTP (get_current_user) and /api/v1/session now extract the external credential separately and pass it as a fallback, so a stale/invalid native cookie can no longer shadow a valid external credential (previously only WS/SSE/optional paths did this). - P2: clear the request-local external access ceiling at every auth entrypoint (api-key, raw access-token, webhook, ws api-key, MCP), not just authenticate_with_credentials, so a stale ceiling can never carry into a non-external path. - Make a configured access-claim mapping authoritative even when it parses empty (all-invalid entries no longer re-enable alias self-elevation). - A blocked external user's API key no longer increments usage counters. Access-ceiling coverage (routes that previously escaped the deny-only cap): - custom_component / custom_component_update (code instantiation) now enforce the ceiling directly (viewer denied; editor/admin/native users unchanged). - Deprecated /upload/{flow_id}, update_project_mcp_settings, and the models.py default/enabled-model variable routes now call the appropriate guard. - Memory-base guards resolve the base first and pass kb_id + real owner so plugin enforce runs for non-owners and audit rows carry the kb id. Bundled hardening: - P2: filesystem deny-list now denies a protected credential directory requested by basename (.ssh/.aws/.git) and as a glob entry, not just as a parent dir. - P3: public-job marker write failures on the Redis backend now fail the build (503) instead of returning an un-shareable job id that 404s on other workers. Adds regression tests across auth, authz route guards, api-key crud, external auth, filesystem deny-list, memory bases, and the redis job queue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [autofix.ci] apply automated fixes --------- Co-authored-by: phact <estevezsebastian@gmail.com> Co-authored-by: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Janardan Singh Kavia <janardankavia@ibm.com> Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local> Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com> |
|||
| e5d42e54fc |
feat: upgrade Firecrawl to v2 SDK and extract into the lfx-firecrawl extension bundle (#13495)
* Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Upgrade Firecrawl components to firecrawl-py v2 + add Search component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add Firecrawl Search API to the bundle page Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [autofix.ci] apply automated fixes * fix(firecrawl): D205 docstring + defensive .get('data') in crawl Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(firecrawl): D205 docstring in scrape Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(firecrawl): use list.extend in map (PERF401) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: add unit tests for Firecrawl v2 components Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [autofix.ci] apply automated fixes * test: align Firecrawl component tests with lfx _attributes pattern Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [autofix.ci] apply automated fixes * refactor(firecrawl): remove deprecated extract endpoint component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(firecrawl): remove deprecated extract endpoint component Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: drop extract component test (extract endpoint removed) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: remove Firecrawl Extract API section (deprecated) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [autofix.ci] apply automated fixes * fix(firecrawl): map Search component to Firecrawl icon, drop Extract The frontend icon map in styleUtils.ts still referenced the removed FirecrawlExtractApi and lacked the new FirecrawlSearchApi, so the Search node would not render the Firecrawl logo. * chore: regenerate component index for Firecrawl v2 (Search added, Extract removed) * refactor(firecrawl): extract components into the lfx-firecrawl extension bundle Port the firecrawl provider out of lfx.components into a standalone Extension Bundle at src/bundles/firecrawl (distribution: lfx-firecrawl), following src/bundles/PORTING.md: - Move the v2-SDK components (Scrape, Crawl, Map, Search) to src/bundles/firecrawl/src/lfx_firecrawl/components/firecrawl/ and remove the in-tree provider + its lfx.components registration. - Own the firecrawl-py>=4,<5 pin in the bundle; drop it from langflow-base. - Wire the workspace (root pyproject deps/sources/members) and uv.lock. - Append migration-table entries (bare name, both import paths, pre-a slot) for the four classes; FirecrawlExtractApi gets no entries since the v2 SDK removed the extract endpoint and the component was dropped. - Regenerate the component index (firecrawl category removed) and locales/en.json (54 firecrawl keys move out of core). - Bundle-local unit tests + test_pilot_firecrawl_upgrade integration test; update Dockerfile bundle enumeration comments. * fix(lfx): repair migration table entry fused during release-1.11.0 merge The release-1.11.0 back-merge collided the FirecrawlSearchApi legacy_slot entry with the NextPlaidVectorStoreComponent bare_class_name entry, mashing both into a single object with duplicate 'target' keys, populating two of {bare_class_name, import_path, legacy_slot}. That fails the MigrationTable validator (entries.51) and broke the lfx loader tests. Split it into the two intended entries so each populates exactly one key-field. Restores the FirecrawlSearchApi and NextPlaidVectorStoreComponent quads (60 entries total, +16 vs base). Append-only and bare-name guards pass. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Eric Hare <ericrhare@gmail.com> |
|||
| 97c5598ee3 |
fix(authz): schedule periodic audit-log retention cleanup (#13546)
* fix(authz): schedule periodic audit-log retention cleanup clean_authz_audit_log() was only invoked once, at startup inside initialize_services(). A long-running instance never pruned authz_audit_log again after boot, so the table grew unbounded between restarts even though the retention helper was already implemented and unit-tested. Wire the same helper to a recurring background worker: - New AuditLogCleanupWorker (services/task/audit_cleanup.py), modelled on the sibling temp_flow_cleanup.CleanupWorker: a stop-event-driven asyncio task that prunes on a fixed interval in its own session_scope(). Best-effort -- the helper logs-and-swallows DB errors and an outer guard keeps the loop alive, so it never blocks the event loop or request path. - Gated: the worker only schedules when AUTHZ_AUDIT_ENABLED is True and AUTHZ_AUDIT_RETENTION_DAYS > 0; otherwise start() is a no-op. Retention=0 stays a no-op end to end. - Sleep-first loop: the unconditional startup sweep still prunes at boot, so the first scheduled pass waits one interval to avoid a redundant immediate delete. The startup sweep is left unchanged. - New AUTHZ_AUDIT_CLEANUP_INTERVAL setting (default 86400 = daily, floor 300s). - Started/stopped from the application lifespan, both best-effort. Tests (test_audit_cleanup_worker.py) show cleanup runs repeatedly on the recurring schedule (not just at startup), is a no-op when retention or auditing is disabled, survives sweep failures, and -- end to end against a real SQLite engine -- prunes a row inserted after startup while leaving in-window rows. Closes the audit-retention-scheduling gap (C) for OSS RBAC on release-1.10.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(authz): read AUTHZ_AUDIT_CLEANUP_INTERVAL directly The cleanup worker resolved the sweep interval via getattr-with-default plus a TypeError/ValueError guard, mirroring the field default in a module constant. Pydantic already guarantees the field is present and >= 300, so those fallbacks were unreachable and the constant was a second source of truth that could drift. Read the setting directly; keep DEFAULT_CLEANUP_INTERVAL_SECONDS only as the pre-start() placeholder for unstarted workers. Drop the matching missing/garbage-fallback test assertion. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
|||
| 5d1c36c8b1 |
test(authz): RBAC enforcement + share-lifecycle integration tests via a test-double enforcer (#13549)
* test(authz): RBAC enforcement + share-lifecycle integration tests via a test-double enforcer
No automated test exercised end-to-end authorization enforcement. The OSS
authorization service is a pass-through (enforce() always returns True,
supports_cross_user_fetch() is False), so allow/deny semantics could not be
asserted against it — only guard-wiring unit tests existed.
Add a lightweight, OSS-only test-double enforcer plus integration tests that
drive the real flow routes over HTTP under a genuine allow/deny signal — no EE
Casbin package required.
- _policy_double.py: PolicyTestAuthorizationService derives allow/deny from the
seeded authz_role / authz_role_assignment / authz_share rows and sets
SUPPORTS_CROSS_USER_FETCH=True. install_policy_authz() swaps it onto the
service manager (str-enum keys make ServiceType interchangeable) and flips
AUTHZ_ENABLED=true / AUTHZ_SUPERUSER_BYPASS=false, restoring both on exit, so
every get_authorization_service() call site (guards, fetch, listing, helpers)
sees it for the duration of a test. Ships seed helpers (roles/assignments/
shares) so tests stay declarative.
- test_rbac_enforcement_integration.py:
* Role matrix (Phase 1.11) on flow routes, with flows owned by a separate user
so the guards' owner-override does not mask the role decision:
- viewer: read + execute (build) allowed; write/delete -> 404; create -> 403
- developer: write + create allowed; delete -> 404
- admin: full, including delete
* Share lifecycle (Phase 3.13): Alice shares a flow with Bob ->
Bob GET/PATCH/build succeed; without the share every fetch route -> 404
(UUID-privacy mask). A read-level share grants GET but 404s PATCH, proving
permission_level (not mere share presence) is enforced.
* Domain resolution: a workspace-scoped grant authorizes a flow in that
workspace but 404s a flow in another — trips if _resolve_authz_domain
regresses.
In each test a cross-user GET -> 200 (only possible with enforcement on +
share-aware fetch) is the linchpin proving the paired deny is real enforcement,
not an owner-scoped fetch miss; removing a guard flips a 404 to 200/200.
Closes the enforcement integration-test gap (D) for OSS RBAC on release-1.10.0.
Per the ticket's triage note, the reusable test-double also stands alone if the
team prefers the deny-path matrix to live in EE.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(authz): harden policy double per review — strict scoped grants, deterministic seeding, read-share build denial
- _assignment_covers: a scoped assignment missing domain_id no longer
widens to global coverage; only domain_type='global' is global
- assign_role: fail fast (ValueError) on non-global assignments without
a domain_id so malformed grants can't hide domain-resolution bugs
- seed_system_roles: overwrite preexisting viewer/developer/admin rows
so the asserted policy matrix is always SYSTEM_ROLE_PERMISSIONS
- read-only share test: also assert build/execute is denied (404), not
just PATCH
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|||
| 40d7814739 |
feat: New Bundle support for Multi-vector NextPLAID vector store (#13553)
* nextplaid integrate * doc ids fix * multivec fixes * type check * refactor: ship NextPlaid as the lfx-nextplaid extension bundle Convert the in-tree NextPlaid vector store and its companion vLLM multivector embeddings into a standalone `lfx-nextplaid` Extension Bundle, following src/bundles/PORTING.md (cf. lfx-arxiv, lfx-ibm). The whole feature now ships as an additive bundle with no core modifications. - Move both components into src/bundles/nextplaid (components/nextplaid), dropping src/lfx/.../components/nextplaid and reverting the core vllm __init__ multivector additions. - Bundle declares its own runtime deps (langchain-plaid, pillow) and ships extension.json + the langflow.extensions entry point. - Add migration_table entries mapping the legacy class names / import paths to ext:nextplaid:*@official; wire the workspace (root pyproject, uv.lock). - Add the pilot upgrade integration test and bundle-local unit tests. - Declare explicit outputs on NextPlaidVectorStoreComponent so the extension validator can resolve its output methods. * fix: address CodeRabbit review on NextPlaid bundle - nextplaid: derive stable text IDs from source/page+content (not content alone) and fingerprint raw PIL images by bytes (not batch position) so ingests upsert instead of colliding/overwriting unrelated vectors - vllm impl: validate the /pooling response envelope before nested indexing, raising a clear RuntimeError on malformed text/image responses - icon: use the isDark boolean prop contract instead of the isdark string - test: gate the distribution check on package presence (PackageNotFoundError) so genuine import failures surface instead of being skipped --------- Co-authored-by: Meet <meet@dhcp-9-127-22-227.c4p-in.ibm.com> Co-authored-by: Eric Hare <ericrhare@gmail.com> |
|||
| 72f86c96eb |
ci: gate backend tests by Python changes (#13614)
* ci: gate backend tests by Python changes * fix(ci): update test conditions to include docs-only path filter * Update .github/workflows/ci.yml Co-authored-by: Adam-Aghili <149833988+Adam-Aghili@users.noreply.github.com> --------- Co-authored-by: Adam-Aghili <149833988+Adam-Aghili@users.noreply.github.com> |
|||
| bb930e2ffe |
docs: build docker from source (#13041)
* docs-add-build-from-source-and-refresh * peer review * peer-review * peer-review * peer-review |
|||
| ca29afab5c |
fix(frontend): restore Inspection Panel access to hidden advanced fields (1.11.0) (#13626)
fix(frontend): restore Inspection Panel access to hidden advanced fields Fields that were both advanced=True and listed in HIDDEN_FIELDS became unreachable from the Inspection Panel: hidden from the node body (advanced) and removed from the panel (HIDDEN_FIELDS), leaving no edit path. This affected every HIDDEN_FIELDS entry, originally reported for Chat Input/Output Store Messages (should_store_message). Keep HIDDEN_FIELDS out of the default advanced view (the #12473 declutter intent) but stop filtering them out of edit-fields mode, so users can surface and edit them via the visibility toggle. Also drop the stale Agent verbose entry (removed from the component via drop = {verbose}) while keeping the still-live format_instructions and output_schema hidden. i18n the two remaining hardcoded panel strings. Fixes #13595 |
|||
| de06b9176a |
fix(frontend): duplicate tooltip + raw i18n key on collapsed sidebar nav (1.11.0) (#13652)
fix(frontend): remove duplicate tooltip and raw i18n key on collapsed sidebar nav The floating canvas control nav (shown when the sidebar is collapsed) rendered two tooltips per icon: a native `title` attribute on ControlButton plus the styled ShadTooltip. On top of that, MemoizedComponents passed the raw i18n key (`item.tooltip`) as the tooltip text instead of running it through `t()`, so the ShadTooltip exposed strings like `sidebar.nav.bundles` while the native title showed the bare id (`bundles`). - CanvasControlButton: drop the native `title` (the duplicate tooltip); keep accessibility via `aria-label`. The ShadTooltip is the single visual tooltip. This also removes the redundant native tooltip from the zoom/fit/lock control buttons. - MemoizedComponents: translate the nav tooltip with `t(item.tooltip)`. Adds an isolated CanvasControlButton regression test asserting no native `title` and that the label is surfaced via aria-label + ShadTooltip. |
|||
| 02319696dc |
feat: add docs feedback components (#13627)
* add-docs-page-feedback-component * make-buttons-same-size |
|||
| 3e3a24e353 |
fix: fail fast when Redis job queue backend is unreachable (#13456)
* fix: fail fast when Redis job queue backend is unreachable When LANGFLOW_JOB_QUEUE_TYPE=redis is set but Redis is not reachable, Langflow booted normally and then raised a raw redis ConnectionError as a 500 on the first flow execution, with no clear cause. Probe Redis at startup (bounded retry) and abort boot with an actionable error when it stays unreachable, mirroring the existing external-cache connectivity check. Translate a later Redis outage on the build and ownership paths into a clean HTTP 503 via a typed JobQueueBackendUnavailableError instead of a raw stack trace. * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * [autofix.ci] apply automated fixes * fix: address review - probe redis pre-start, redact credentials, cancel orphaned build - is_connected() now probes with a temporary client when the service has not started yet: the startup fail-fast in initialize_services() runs before the per-worker start() creates the client, so it previously rejected every redis boot, healthy or not - connection_target redacts URL userinfo so credentials never reach server logs or the HTTP 503 detail - build_flow cancels the just-started build (best-effort) when owner registration fails, instead of leaving an unreachable build running - register_job_owner only records the local owner after the Redis write succeeds, so a failed write cannot leave same-worker ownership checks passing while other workers see the job as unowned --------- 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> |
|||
| 538bcf3845 |
feat: docs page copy-as-markdown keyboard shortcut (#13628)
* add-copy-page-keyboard-shortcut * peer-review |
|||
| 415323ec6d |
docs: migrate code blocks to native Prism and restyle API reference pages (#13299)
* docs: migrate code blocks from CodeSnippet to native Prism Replace the custom CodeSnippet component (@code-hike/lighter) with Docusaurus's native @theme/CodeBlock across all MDX files (current and versioned docs). Add bash to additionalLanguages and swizzle prism-include-languages.js to add custom token highlighting for shell commands and flags. Remove @code-hike/mdx dependency. * docs: improve inline code styling Darken inline code background, increase horizontal padding to 0.4em, fix vertical alignment, and remove border in light mode. * docs: address PR review — fix code slice regression and CSS/regex polish - Inline RecursiveCharacterTextSplitter inputs and methods as literal code blocks in concepts-components.mdx (current + 1.8.0 + 1.9.0), restoring the focused slices lost when migrating from CodeSnippet - Scope bash-plain Prism regex to unambiguous CLI subcommands only, removing generic bash builtins (run, add, get, set, start, stop, etc.) - Merge duplicate .theme-code-block CSS rules into a single declaration * fix(docs): prevent horizontal scroll on API docs pages The Redoc two-column layout (sidebar 300px + api-content 1300px) totals 1600px, expanding .main-wrapper beyond narrower viewports because it has overflow:visible. Clips at .main-wrapper using the html.plugin-redoc class that Docusaurus adds on API pages only. * fix(docs): API docs sidebar and layout fixes - Disable Redoc built-in search (disableSearch: true) - Pin sidebar top to navbar height (top: 60px) so it never hides behind navbar - Remove hardcoded #111 background from dark mode sidebar * fix(docs): render markdown correctly in API docs descriptions The _clean_descriptions function was converting newlines to <br> tags, mixing HTML with Markdown. CommonMark stops parsing Markdown headings (###) inside HTML blocks, causing them to appear as literal text in Redoc. Replace the <br> conversion with a simple strip() so descriptions remain pure Markdown and Redoc renders headings, lists, and code blocks correctly. * feat(docs): align API docs colors with Langflow brand - Set primaryColor to #F471B5 (Langflow pink) - Add HTTP method badge colors matching Langflow palette - Set schema.linesColor and requireLabelColor to brand pink - Set inline code color to pink, headers to #e3e3e3 - Set sidebar background to #18181b (matches frontend dark bg) - Set rightPanel background to #0d0d0f, codeBlock to #161618 - Refactor: move color config from CSS to theme.theme where safe - Remove dead search input CSS (search disabled via disableSearch:true) - Consolidate duplicate .menu-content rules * wip(docs): API docs styling — colors, components, light/dark themes * fix(docs): fix Response samples h3 title padding and refactor HTTP method button CSS * feat(docs): add sidebar dark background, active item styles, and right panel color adjustments * fix(docs): add sidebar borders and remove operation divider border * fix(docs): fix expanded response background and align dark/light theme colors * refactor(docs): standardize selectors, comments and remove duplicate rules in redocusaurus.css * refactor(docs): apply PR review — CSS custom properties, version pin, dom version comments, fix overflow * fix(docs): fix Redocly badge visibility covered by sidebar background * fix(docs): extend sidebar border-right to Redocly badge area * refactor(docs): replace hardcoded #ffffff label color with --redoc-text-label variable * fix(docs): lighten inline code background in API docs dark theme Redoc's default typography.code.backgroundColor (rgba(38, 50, 56, 0.05)) is nearly invisible over the dark background. Override it with rgba(255, 255, 255, 0.05) in dark theme only, excluding pre > code so code sample blocks stay unaffected. * feat(docs): align docs primary pink with API spec brand color Use #f471b5 (API spec primaryColor) as --ifm-color-primary in dark theme and #e44fa0 (slightly darkened for contrast on white) in light theme. Remove dark-theme pink overrides in sidebar.css (#ff6ad0 CTA and hsla(329, 55%, 68%) active TOC link) — they compensated for the old muted pink and are redundant now that the primary itself is bright. * feat(docs): lighten dark theme text colors for better readability Bump body text (#a8a8b0 -> #bcbcc4), headings (#cdcdd4 -> #dcdce2) and sidebar menu (#8a8a92 -> #9c9ca4) one step brighter. * chore(docs): add IBM Equal Access accessibility-checker setup Add accessibility-checker as devDependency with aceconfig.js (policy IBM_Accessibility, JSON reports in docs/a11y-results/, gitignored). Scan with: npx achecker <url> against a built docs site. * fix(docs): WCAG AA contrast and ARIA fixes across docs and API reference Validated with IBM Equal Access scans (light theme): home, quickstart and component pages at 0 violations; /api from 3382 down to 167 (all remaining are Redoc-internal DOM: schema table headers, svg/select labels). Docs site: - Light primary pink #d11074 — passes 4.5:1 on white and inline-code bg - Light Prism palette darkened per-token to pass 4.5:1 on #f9f9fd - TabItem swizzled to give tabpanels an accessible name (aria-label) - codeBlockA11y client module: scrollable code blocks get role=region + unique label; non-scrollable ones lose the needless tabindex API reference (Redocusaurus): - redocA11y client module: role=main on api-content, role=navigation on sidebar — fixes 1924 aria_content_in_landmark violations - HTTP method badges and response chips darkened to pass with white text - Light-theme overrides: accessible pink #cd1072 for links, required markers, constraint chips, schema tree lines; darker grays for utility buttons and type labels (incl. 0.7-opacity wrapper fix) - Sidebar active/hover items use regular text color, method badges keep their own colors; expandable property names match non-expandable ones * fix(docs): WCAG AA contrast fixes for dark theme Validated with IBM Equal Access scans in dark mode (temporary colorMode.defaultMode flip during scanning): home, quickstart and component pages at 0 violations; /api matches light at 167 remaining (all Redoc-internal DOM: table headers, svg/select labels). - Code block comments/line numbers #4a5060 -> #798197 (4.56:1 on #18181a) - Redoc dark sample tokens: boolean/null #e95c59, number #5392b8 - Status-code tabs: lift docusaurus-theme-redoc's #303846 !important selected-tab rule with a higher-specificity override - oneOf variant buttons: dark text in both themes (their white/pink backgrounds are theme-independent) - redocA11y client module: patch response chip colors (success green / error red) to dark-accessible variants when data-theme=dark — a single Redoc theme color cannot pass on both light and dark derived backgrounds, and status is only distinguishable by computed color * fix(docs): resolve remaining Redoc-internal accessibility violations Extend the redocA11y client module with semantic patches for Redoc DOM the theme cannot reach (validated: 0 IBM Equal Access violations on all scanned pages in both light and dark themes): - Decorative svg chevrons/arrows: aria-hidden=true (svg_graphics_labelled) - Content-type dropdowns: aria-label (input_label_exists) - Schema field tables (2-col name|description layout, no <th> anywhere): role=presentation — content reads in DOM order; role=rowheader on <td> is invalid ARIA inside a native table (table_headers_exists/related) - Semantic patches run on the next animation frame after DOM changes so Redoc's lazy-rendered operations are covered immediately; the heavier color patch stays debounced * ci(docs): gate docs accessibility with IBM Equal Access scans Add test-docs-accessibility job to docs_test.yml (rides the existing docs/** path filter from ci.yml): build + scan 4 representative pages (home, quickstart, component page, /api) in light theme, then flip colorMode.defaultMode to dark, rebuild and scan again. - scripts/a11y-ci.sh: serves the build and runs npx achecker per page with one retry to absorb Redoc lazy-render timing flakes; any real violation fails the job (failLevels: violation) - aceconfig.js: pin ruleArchive to 19May2026 so IBM rule updates don't break CI without a deliberate bump * ci(docs): fix Chrome sandbox launch on Ubuntu 24.04 runners Ubuntu 24.04 restricts unprivileged user namespaces via AppArmor, which prevents puppeteer's Chrome (used by the IBM checker) from starting its sandbox. Re-enable them with the documented sysctl workaround instead of weakening the browser with --no-sandbox. * fix(docs): align response status code with description text Redoc sets vertical-align: top and a smaller line-height on the status code <strong> inside response buttons, leaving "200" visually higher than "Successful Response". Align both to the shared text baseline. * refactor(docs): apply PR review feedback - redocA11y: match only real Redoc routes (/api, /api/workflow) — a bare startsWith("/api") also matched docs pages like /api-request and leaked one body MutationObserver per navigation - codeBlockA11y: also observe the hidden attribute — Docusaurus tabs toggle panels via hidden (no childList mutation), so scrollable blocks inside an initially hidden tab were never re-evaluated for tabindex - Extract Prism themes to src/prismThemes.js (docusaurus.config.js was past the 600-line red flag) - concepts-components.mdx (current + 1.9.0 + 1.8.0): comment pointing hardcoded snippets to recursive_character.py to mitigate drift Validated: clean build + IBM Equal Access scans 4/4 passing. * replace-openapi-file-with-1.10 * migrate-prism-changes-to-1.10-version * a11y-script-dont-block --------- Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com> |
|||
| 29140c06f0 |
fix(ci): resolve validate-version output in release-lfx changelog link (1.11.0) (#13612)
fix(ci): resolve validate-version output in release-lfx changelog link
The create-release job references
needs.validate-version.outputs.current_version in its generated release
notes (the Full Changelog compare link), but validate-version was not in
the job's needs array, so the expression evaluated empty and the link
rendered as compare/v...lfx-vX.Y.Z (broken base).
Add validate-version to the create-release needs. This adds no real
serialization: create-release already waits on release-lfx, which
transitively requires validate-version via run-tests, and the job's
always() if-condition is unaffected.
Flagged by actionlint: property "validate-version" is not defined in
object type {build-docker, release-lfx}.
|
|||
| dea7b09257 |
fix(ci): create GitHub releases on the dispatched v-prefixed tag (1.11.0) (#13607)
fix(ci): create GitHub releases on the dispatched v-prefixed tag The create_release job passed the bare version (v stripped) as the release tag with no commit target, so when that tag did not exist GitHub minted a new lightweight tag at the default-branch HEAD -- the wrong commit, still carrying the previous version (main only adopts a release's version via the post-release back-merge). Every release since 1.8.2 shipped a stray bare tag (1.8.2, 1.8.3, 1.9.0-1.9.6, 1.10.0) pointing at a previous-version commit, and the GitHub release had to be manually re-pointed to the real vX.Y.Z tag after each release. - create_release now attaches the release to inputs.release_tag for stable releases; pre-releases keep their computed tag (e.g. 1.10.0rc1) but it is minted at the release commit via 'commit:'. - release-lfx.yml pins the minted lfx-v* tag to github.sha instead of the default branch. The validate-tag-format guard (#12847) only blocks at dispatch time; create_release was re-creating the very duplicates it guards against. |
|||
| 84c3bbebcd |
fix(ci): scope nightly migration-test pre-releases to the langflow stack (1.11.0) (#13604)
fix(ci): scope nightly migration-test pre-releases to the langflow stack The previous fix (#13599) added a global --prerelease=allow, which let UNRELATED dependencies resolve to alphas: on nightly run 27274206250 the stable->nightly upgrade pulled pydantic 2.14.0a1 + pydantic-yaml 1.6.1a1, and the pydantic alpha breaks langchain-core at import time (RunnablePassthrough pydantic ValidationError), failing the nightly boot right after a successful install. Clean installs were fine - only this upgrade path resolved the alpha combo. Scope pre-release eligibility to the langflow lockstep stack instead: uv accepts a pre-release when the package's own requirement carries a pre-release marker, but not via transitive pins, and the nightly chain is langflow -> langflow-base -> lfx -> langflow-sdk with exact ==devN pins. Request each directly; langflow-sdk versions independently (0.2.0.devN), so an explicit .dev0 floor marks it eligible while lfx's exact pin selects the version. The 'latest' branch gets the same treatment via .dev0 floors on all four. Verified by dry-run against PyPI: langflow/langflow-base/lfx at 1.11.0.dev2 + langflow-sdk 0.2.0.dev2 resolve with pydantic staying at stable 2.13.4. |
|||
| 75e01157fe |
fix(ci): allow pre-releases when pinning the nightly in migration validation (1.11.0) (#13600)
fix(ci): allow pre-releases when pinning the nightly in migration validation Migration Test: pip/venv (stable -> nightly) failed deterministically on nightly run 27260425158 (twice, including a rerun): hint: langflow-base was requested with a pre-release marker (e.g., langflow-base==1.11.0.dev1), but pre-releases weren't enabled (try: --prerelease=allow) This is the first nightly publishing as a canonical .devN pre-release of the langflow distribution (nightly -> stable bundle cutover). The 'latest' branch of the upgrade step already passes --prerelease=allow, but the pinned-version branches do not. uv implicitly allows the pre-release for the directly requested ==X.Y.Z.devN pin, yet langflow's metadata pins langflow-base==X.Y.Z.devN transitively, and uv rejects transitive pre-releases unless they are enabled - so the install fails after the stable uninstall, sinking the migration test. Add --prerelease=allow to both pinned-version install lines. |
|||
| 0555eeced1 |
fix(test): gate models.dev background refresh out of tests (#13596)
Integration tests failed twice in nightly run 27260425158 with pyleak EventLoopBlockError - first Integration Tests 3.14, then 3.12 on the rerun, each time in a different test. The blocking stack points at refresh_models_dev_periodically: every app boot unconditionally starts a lifespan task that immediately fetches https://models.dev/api.json, so the request lands mid-test in whatever test happens to be running. Under pyleak's asyncio debug instrumentation the fetch blocked the loop 0.797s against a 0.2s threshold. Whichever test draws the short straw flakes - which is why it looked transient and moved between versions. Add a LANGFLOW_MODELS_DEV_REFRESH env gate (default unchanged: enabled) and disable it session-wide in the backend test conftest. Tests fall back to the bundled static model lists, which is also deterministic. Verified: with the gate set, app boot makes zero models.dev requests; the previously failing integration test passes. |
|||
| 086e38898d |
fix(ci): anchor langflow-base version extraction in nightly docker build (1.11.0) (#13592)
fix(ci): anchor langflow-base version extraction in nightly docker build
The first nightly on the 1.11 workspace (tag v1.11.0.dev0, run
27253229568) failed in Build Nightly Base Package within seconds:
'Base version format is incorrect'. The extraction (uv tree, grep
langflow-base unanchored, awk field 3, first line) broke because uv
tree now prints the langflow-base workspace-root line
('langflow-base v1.11.0.dev0', two fields) before any dependency line
('langflow-base[complete] v1.11.0.dev0' under the langflow root, three
fields), so the first match yields an empty field 3 and the format
check exits 1. uv tree's stderr is discarded, which made the real
cause invisible in CI.
Anchor to the root line and take field 2 instead - the exact pattern
the langflow (main package) extraction in this same file already uses,
which is why the main-package builds passed on the same tag. Verified
both extractions print 1.11.0.dev0 against a local checkout of
v1.11.0.dev0.
|
|||
| d065523493 |
fix(test): raise spawn-child join timeout in test_multi_process_visibility (#13588)
The test spawns a child via multiprocessing spawn context, which cold-imports the full langflow package (plus coverage's multiprocessing hooks in CI) before appending a single event. The 10s join timeout is routinely exceeded on a loaded CI runner sharing 4 vCPUs with a second xdist worker: in nightly run 27253229568 (Unit Tests - Python 3.12 - Group 5) the test failed all 12 executions (5 reruns x 2 step attempts), each rerun exactly 10s apart - the join deadline, not a product bug. Raise the liveness bound to 60s (join returns immediately when the child exits, so the passing case is unaffected) and kill the child on timeout so a hung spawn can't leak into later tests. |
|||
| 17447920f9 |
fix(ci): rebalance backend test splits with measured durations + raise unit-test step timeout (#13583)
Unit Tests - Python 3.12 - Group 3 has been failing at 98-99% on the nightly: the job hits the nick-fields/retry 40-minute per-attempt timeout and pytest is SIGTERM'd mid-test, so it looks like one flaky last test when it is actually a deterministic timeout (and the internal retry plus run-level retries can never succeed). Root cause is twofold: 1. .test_durations was last regenerated ~May 2025 and covered only 2,219 of ~9,500 current unit tests, so pytest-split weighted 77% of the suite at the 0.73s average. The expensive client-fixture tests (test_webhook.py, test_login.py - each pays a full create_app + lifespan boot per test, 60-120s late in a CI run) clustered into group 3's tail, making it ~8-10 minutes slower than its siblings (33:15 on 3.13, >40min on 3.12 which is additionally slowed by astrapy disabling SSL connection reuse on Python 3.12.0-11). The weekly store_pytest_durations workflow that should refresh the file has been dying at the 6-hour GitHub job limit every week (serial full-suite run no longer fits), so the file silently froze. This regenerates the file from real per-test wall-clock measured in the passing Python 3.13 nightly jobs (run 27246228762 attempt 1, all 5 groups, parsed from the -vv xdist logs: per-worker start-to-start deltas). 9,508 tests now have measured durations; old entries are kept where no new measurement exists. Simulated least_duration split goes from one outlier group to 5 even groups (~24.6 min each) with the >50s tests spread 1-2 per group instead of 7 in one. 2. timeout_minutes 40 -> 50 gives headroom for matrix-cell variance (3.12 runs ~20% slower than 3.13) so a slightly slow cell degrades gracefully instead of burning 2x40min and failing the whole run. Follow-ups (not in this PR): fix store_pytest_durations to run with xdist or split groups so it fits the 6h limit; investigate the in-worker degradation that makes client-fixture boots cost 8-12s early in a run but 60-120s after ~30 minutes. |
|||
| ee659ca38c |
fix(bundles): floor lfx pin at the minor line's .dev0 so nightlies resolve
The first 1.11.0.dev0 nightly failed its "Test Langflow Main CLI" step: the fork bump's sync_bundle_lfx_pin.py re-synced every bundle floor to lfx>=1.11.0, and PEP 440 sorts the nightly's 1.11.0.dev0 BELOW 1.11.0, so the workspace-built bundles (whose metadata shadows the satisfiable PyPI lfx-arxiv 0.1.1 during `uv pip install dist/*.whl`) reject the branch's own lfx while langflow-base pins it exactly — unresolvable. Floor at lfx>=X.Y.0.dev0,<(X+1).0.0 instead: X.Y.0.dev0 is the lowest version PEP 440 admits in the minor line, so every devN / rcN / final satisfies the floor while older lines and the next major stay excluded. This closes the NIGHTLY.md activation-gate hole structurally — future minor forks re-sync to a floor their own nightlies already satisfy. - sync_bundle_lfx_pin.py: lfx_floor_spec emits the .dev0 floor - port_bundle.py: mirrored _current_lfx_floor kept in step - bundle pyprojects restamped via the script (arxiv/docling/duckduckgo/ibm) - test_bundle_lfx_pin.py expectations updated (20/20 passing) - NIGHTLY.md gate section annotated with the post-activation fix uv.lock is unaffected (workspace lfx is recorded as an editable source with no specifier — which is also why uv sync/lock passed in the same job). The release.yml RC floor-relax sed still matches the new form; now redundant but harmless. No bundle version bump needed: published 0.1.1 floors >=1.10.0.rc0, satisfiable by the whole 1.11 line. |
|||
| 7a784515e0 |
fix(lfx): restamp component index version after 1.11.0 fork bump (#13574)
The release-1.11.0 fork bumped lfx to 1.11.0 but left component_index.json's version field at 1.10.0. _read_component_index fails closed on exact version mismatch, so the bundled registry loads as None and the upgrade-gate tests fail (UpgradeFlowError: 'bundled component registry is empty or missing') across the LFX test matrix. That failure blocks the nightly's release-nightly-build job, so no canonical 1.11.0.devN is published and check-nightly-status blocks CI everywhere. Surgical restamp: version -> 1.11.0 and recomputed sha256 (same orjson OPT_SORT_KEYS hashing and OPT_SORT_KEYS|OPT_INDENT_2 serialization as scripts/build_component_index.py). Entries unchanged. Verified the reader validation passes and the three failing tests go green. |
|||
| fb64e45e75 | chore: bump version to 1.11.0 | |||
| 13a937c5b7 |
feat(ci): nightly → stable bundles via canonical pre-releases + decision record [gated] (#13528)
* docs: record nightly→stable bundle cutover plan (gated on lfx 1.10.0) Add src/bundles/NIGHTLY.md documenting why langflow-nightly currently renames the bundles (lfx and lfx-nightly ship the same lfx/ import, so a stable bundle would co-install both and collide) and the deferred cutover (Approach A: canonical pre-releases; B: lfx as a bundle extra), gated on stable lfx 1.10.0 being published to PyPI. Also expand two docstrings in scripts/ci/update_lfx_version.py to state the deeper install-conflict reason, not just the resolve failure. No behavior change. * feat(ci): nightly Approach A — canonical pre-releases, drop nightly bundles [DRAFT/gated] (#13529) feat(ci): nightly Approach A — canonical pre-releases, drop nightly bundles DRAFT reference implementation of the nightly→stable-bundle cutover documented in src/bundles/NIGHTLY.md. Publishes the nightly under CANONICAL package names as .devN pre-releases instead of separate *-nightly distributions, so the stable lfx-* bundles resolve against a single canonical lfx (no dual-lfx install collision) and no nightly bundle packages are produced. - tag scripts (pypi/lfx/sdk_nightly_tag.py): count .devN against the canonical PyPI histories instead of the *-nightly projects - update scripts: stop renaming to *-nightly; set .devN versions; re-pin inter-package deps to exact canonical dev versions; delete the bundle rename/repin (update_lfx_dep_in_bundles, rename_bundles_for_nightly) - release_nightly.yml: publish canonical pre-releases; remove bundle build, dist-nightly-bundles artifact, publish-nightly-bundles job + its gate; verify canonical names; main wheel glob dist/langflow-*.whl - nightly_build.yml: drop the bundle git-add in the tag commit - NIGHTLY.md: Approach A marked implemented + activation gate + A1/A2 follow-ups Held as DRAFT: do not activate until stable lfx 1.10.0 is published AND the nightly base is the next minor (release-1.11.0). A 1.10.0.devN core sorts below 1.10.0 and would fail the bundles' >=1.10.0 floor. Stacked on #13528. (.secrets.baseline: incidental line-number shifts for the two workflows + prune of pre-existing stale Pokédex Agent.json entries.) * docs(ci): drop internal 'Approach A' label from nightly cutover comments Comment- and docstring-only change across scripts/ci/* and the two nightly workflows; no logic change. The two workflow edits stay single-line so .secrets.baseline line numbers are unaffected. src/bundles/NIGHTLY.md keeps its A/B decision-record framing intentionally. * feat(ci): make nightly consumers work with canonical pre-releases Follow-ups from the nightly cutover that are part of its blast radius (the nightly now publishes canonical `.devN` pre-releases, not `*-nightly` distributions): - version.py: derive the "Nightly" label from the `.dev` version marker, since the canonical `langflow`/`langflow-base` distribution matches first in the lookup. Keeps the startup banner and telemetry `package` field identifying nightlies. Adds a canonical-dev test; updates the base-dev assertion. - ci.yml check-nightly-status: query the canonical `langflow` project and pick the latest `.devN` release date instead of `langflow-nightly`'s `.info.version`. - db-migration-validation.yml: install the nightly as `langflow[postgresql]==<dev>` (pre-release) instead of `langflow-nightly[...]`; verify via version("langflow"). - src/lfx/README.md: nightly install is `uv pip install --pre lfx`. - NIGHTLY.md: rewrite the follow-ups section (these are addressed; Docker image, A2 meta-package, and website docs remain deferred by design). The `langflowai/langflow-nightly` Docker image name is intentionally unchanged. * fix(ci): correct nightly verify uv tree parsing + stale base-dep regex Addresses review of #13528: - release_nightly.yml LFX verify: `uv tree | grep lfx | head -n1` matches the bundle `lfx-ibm` first → 'Name lfx-ibm does not match lfx'. Root the tree with `uv tree --package lfx` so the first line is the lfx package itself. - release_nightly.yml base verify: under canonical naming `langflow-base` prints as a top-level `langflow-base v<ver>` line, so the old $2/$3 field parse read name="v0.10.0" and version="". Use `uv tree --package langflow-base` and $1/$2. - update_lf_base_dependency.py update_base_dep: regex only accepted ~=/==, so its CLI entry point couldn't match the current root dep `langflow-base[complete]>=0.10.0`. Add >= (parity with update_uv_dependency.py). The active nightly path uses update_uv_dependency.py and was unaffected. * docs(nightly): point NIGHTLY.md status at the activation gate, not draft state Per review of #13528: this file ships inside #13528 (#13529 was folded in), so the 'stacked on prep #13528' + 'held as a draft' framing is stale and misleading. Reword the status block to state the real guard is the activation gate (stable lfx 1.10.0 published AND next-minor base), not merge/draft state. Also reword the follow-ups heading 'decide before un-drafting' -> 'decide before activating'. |
|||
| 0195a132e2 |
Merge release-1.10.0 into main
Non-squash union back-merge, ahead of cutting release-1.11.0 / release-1.10.1. - Preserves main's settings-mixin refactor (#13141): release-1.10.0's 40 new settings ported into the per-group mixins, verified field-for-field and behaviorally against release's monolith. - Keeps main's model-handling (#13191, auto-merged). - CI workflows, docling deps, component index, starter projects, AGENTS.md, .secrets.baseline resolved to release-1.10.0. main: 1.9.6 -> 1.10.0. |
|||
| 8ee3493bb7 |
fix(observability): make grafana-loki reference stack instructions work end to end (#13454)
The reference stack README pointed LANGFLOW_LOG_FILE and LANGFLOW_LOG_DIR at different directories, so following it produced an empty Grafana dashboard: Langflow wrote to one path while Promtail scraped another. It also documented a stdout-scrape alternative the shipped Promtail config does not implement. Make the two paths consistent, require an absolute log path, correct the stdout note, and add a no-Langflow smoke test that writes a sample record and confirms it reaches Loki. |
|||
| 9690e69e86 |
fix(security): remove the disabled Python Code Structured tool component (#13560)
* fix(security): remove the disabled Python Code Structured tool component Follow-up to #13538, which neutered PythonCodeStructuredTool to a non-executable stub "for one release cycle, full removal later." This completes that removal. - Delete the component and its registration in lfx.components.tools. - Drop its entry from the component index (num_components 355 -> 354, sha256 recomputed surgically) and from stable_hash_history.json. - Remove its 18 i18n keys from every locale file. - Replace the dedicated stub unit test with a removal test in test_dynamic_import_integration.py. - Add a regressions entry to regressions/1.10.x.yaml. The unauthenticated public-build RCE fix (report H1-3754930) is unaffected: PythonCodeStructuredTool stays in CODE_EXECUTION_COMPONENT_TYPES, so build_public_tmp still rejects any saved or crafted flow that carries the type. instantiate_class execs the node's stored `code` field regardless of whether the class still exists, so the type-name block -- not the class -- is what closes the path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: document removal * docs: typo * Apply suggestion from @mendonk Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com> |
|||
| d7804149d8 |
docs: LF assistant ampersand and dot selectors (#13544)
* docs-lf-assistant-dot-selector * docs-add-lfx-matrix-to-1.10-version |
|||
| 2524604a19 |
fix(ci): make Biome lint non-blocking for release workflow calls (correct implementation using allow-failure input)
- Revert invalid continue-on-error syntax on reusable workflow uses: job - Add allow-failure input to ci.yml lint-frontend call - Define allow-failure input parameter in lint-js.yml workflow_call - Implement conditional exit-code swallow in lint-js.yml when allow-failure=true - When release=true, Biome errors are non-blocking for release pipeline - When release=false/unset, Biome failures block PR merge (existing ci_success gate) |
|||
| 801edf3bab |
Revert "fix(ci): make Biome lint non-blocking for release workflow calls (#13547)"
This reverts commit
|
|||
| a1adb1aa6f |
fix(ci): make Biome lint non-blocking for release workflow calls (#13547)
When ci.yml is called from release.yml (inputs.release == true), a
Biome failure was blocking all downstream publish and docker jobs via
needs.ci.result == 'success'. Biome was already excluded from the
ci_success gate for PR merges, but the reusable CI workflow itself
could still fail when the lint job errored.
Add continue-on-error: ${{ inputs.release == true }} to lint-frontend
so Biome failures are informational-only during release runs, while
remaining visible (and blocking ci_success) on regular PRs.
|
|||
| 6610091697 |
fix(bundles): republish lfx-* at 0.1.1 with corrected pin + relax lfx floor for RC builds (#13542)
* fix(bundles): bump lfx-* bundles to 0.1.1 to republish with corrected lfx pin The published 0.1.0 artifacts on PyPI carry stale lfx pins from before the lfx 0.5.0->1.10.0 version realignment: - lfx-arxiv, lfx-duckduckgo: lfx>=0.5.0,<0.6.0 (hard-broken; the <0.6.0 cap can never resolve lfx 1.10.0) - lfx-docling, lfx-ibm: lfx>=0.5.0 (uncapped floor/cap mismatch) The source pin was already corrected to lfx>=1.10.0,<2.0.0 in #13516, but the bundles were never re-published. PyPI versions are immutable, so shipping the fix requires a version bump. Bump all four to 0.1.1 so the Release Bundles workflow cuts fresh wheels carrying the correct pin. Root pyproject keeps its >=0.1.0 floors (satisfied by 0.1.1); only the bundle dist versions and their uv.lock stamps change. * fix(release): relax bundle lfx floor for pre-release builds + idempotent bundle publish The RC pre-release run builds lfx as 1.10.0rc0, but bundles floor lfx at >=1.10.0. Under PEP 440 a pre-release sorts below the final, so 1.10.0rc0 fails >=1.10.0 and the cross-platform install test cannot resolve the bundle wheels against the RC lfx wheel. build-base/build-main/build-lfx already rewrite their inter-package deps to the pre-release version when pre_release=true; build-bundles was the only release artifact missing that step. Add it: when pre_release=true, rewrite each bundle's lfx floor to the exact pre-release version, keeping the wide <2.0.0 BUNDLE_API cap. Stable source stays at >=1.10.0 -- only RC wheels are relaxed, at build time, so no source churn or re-tag. Also make publish-bundles tolerate 'already exists' duplicate wheels on rerun, matching release_bundles.yml. |
|||
| fa14076dec |
fix(security): block code-execution components on unauthenticated public flow builds (#13538)
* fix(security): block code-execution components on unauthenticated public flow builds (H1-3754930)
PythonCodeStructuredTool exec()'d its attacker-controlled `tool_code` field at
flow-build time. Because a PUBLIC flow can be built with no authentication via
POST /api/v1/build_public_tmp/{flow_id}/flow, that sink was reachable as an
unauthenticated server-side RCE — and other components (Python Interpreter/REPL,
Smart Transform) execute code on the same path, so removing one component alone
would not close the gap.
- Harden the public build path: build_public_tmp now rejects flows containing
code-execution components via validate_public_flow_no_code_execution(). The
check keys on the node `type`, so it holds regardless of the stored component
`code`, and is enforced ONLY on the unauthenticated public path —
authenticated /build is unchanged.
- Neuter PythonCodeStructuredTool to a non-executable compatibility stub: all
exec()/eval() sinks removed; build_tool returns a tool that raises a
deprecation error. The component stays registered with identical
display_name/inputs so saved flows still load and locale keys don't change.
It will be fully removed in a future release.
component_index.json (code_hash) is regenerated by autofix.ci.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* fix: validate_public_flow_no_code_execution
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
|
|||
| 4339011b6d |
docs: lfx compatibility matrix and lfx serve updates (#13518)
* docs-add-existing-changes * fix-combining-of-glyphs * docs-lfx-compatibility * peer-review |
|||
| 2411d8036e |
docs: build OpenAPI spec and cut version 1.10 (#13537)
* build-api * bump-version-to-1.10 * fix-broken-links |
|||
| 60afa18f05 |
fix: serialize DataFrames in tool mode to prevent pandas truncation (#13504)
* fix: serialize DataFrames in tool mode to prevent pandas truncation When Memory Base or Knowledge components are wired as agent tools, the DataFrame result was returned directly without serialization. LangChain would then stringify this for the agent observation using pandas' default repr, which truncates all cells to 50 characters (display.max_colwidth=50), inserting "...". This breaks the F3 agent use case where agents need to reliably recall facts from memory. The agent receives truncated content and cannot see the full text. The fix serializes DataFrames through the existing serialize() function, which converts them to list[dict] format with full, untruncated content. This maintains consistency with how other result types (Message, Data, etc.) are handled in tool mode. - Affects: Memory Base and Knowledge components used as agent tools - Does not affect: Component-to-component wiring or normal (non-tool) execution - Testing: Added tests verifying DataFrames serialize to list[dict] with complete content Improved MB GUI component description * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * [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> |
|||
| 0087ed895c |
chore: remove aws scripts (#13534)
remove aws scripts |
|||
| 7ad65e83df |
docs: add code agents and file processing components (#13285)
* add-code-agents-and-file-ingestion-components * docs: add release note for code agents * Apply suggestions from code review Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com> --------- Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com> |
|||
| ba16577079 |
docs: JSON logging structure and grafana integration (#13251)
* initial-cherry-pick * docs-move-config-to-logging * update-logging paths-and-clarify-behavior * fix-other-relative-link * peer-review |
|||
| c1f7054cd1 |
fix: Remove getenvvar component and update locales (#13521)
* refactor: remove getenvvar component and update locales * test: add test for GetEnvVar component removal and update component index --------- Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local> |
|||
| b08442a330 |
fix: standardize playground code block + container backgrounds on shadcn tokens (#13520)
fix: use shadcn canvas colors for playground code blocks and chat-bg for the container Standardize the playground tool-call surfaces on shadcn theme tokens so they swap correctly between light and dark mode: - Code blocks (SimplifiedCodeTabComponent) use bg-canvas: light grey (#F4F4F5) / dark black (#000) so they stand out as the code surface. - The 'Called tool' container (ContentBlockDisplay) uses bg-background so it matches the chat background in both themes instead of inverting to solid black in dark mode (it was bg-primary-foreground). Previously the container and code blocks were both bg-primary-foreground, which rendered the whole section black in dark mode with no contrast. |
|||
| 922c6f2e34 |
docs: gunicorn worker configuration (#13511)
* docs-add-gunicorn-config * docs-env-vars-gunicorn-configuration * align-links-with-title * fix-links * mismatched-timeout-values * Apply suggestions from code review Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com> * peer-review --------- Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com> |
|||
| 0d9f9112ef |
perf(telemetry): batched off-pool writer for transactions + vertex_builds (#13126)
* perf(telemetry): batched off-pool writer for transactions + vertex_builds
Adds TelemetryWriterService that buffers transaction and vertex_build rows
in memory and drains them in batched INSERTs via a dedicated AsyncEngine
(pool_size=1 for SQLite, 2 for Postgres, max_overflow=0). Retention is
amortized in a 60s sweeper instead of running on every insert.
Producers (log_transaction, log_vertex_build) enqueue instead of opening
a DB session, so telemetry traffic no longer competes with the
request-handling pool. Falls back to the legacy direct-write path via
LANGFLOW_TELEMETRY_WRITER_ENABLED=false.
Durability: in-flight rows spill to a diskcache.Deque per PID on shutdown
and are restored on next startup; orphan PID directories left by crashed
workers are adopted.
* perf(telemetry): tighten error handling and add coverage
Review feedback from pr-review-toolkit + silent-failure-hunter:
- Retention sweep snapshots the dirty-flow sets before commit and only
clears them after it lands; a crashed sweep no longer drops the flows on
the floor, so per-flow caps cannot drift unboundedly.
- Producer fall-through is no longer silent. transaction_service and
log_vertex_build each log a one-shot WARNING when telemetry_writer_enabled
is True but the writer is not running.
- Lifespan startup failure now logs ERROR instead of WARNING — if the user
opted into the writer and it didn't come up, that's an error.
- Shutdown drain timeout no longer suppressed silently: logs a WARNING with
pending row count + a hint to raise telemetry_writer_shutdown_drain_s.
- Writer's flush loop catches asyncio.CancelledError separately and
re-prepends the in-flight batch to the buffer so teardown's disk spill
catches it.
- After 6 consecutive batch failures the writer emits a loud ERROR with
buffer depths so operators see sustained data-loss risk.
Added tests:
- test_sanitization_survives_writer_round_trip
- test_retention_failure_preserves_dirty_flows
- test_in_flight_batch_returned_on_cancel
* perf(telemetry): address copilot review
- _restore_from_disk + _adopt_orphan_outboxes now route through _enqueue so a
large disk-spilled or orphan outbox can't bypass telemetry_writer_max_queue
and OOM the process. Oldest rows are dropped and counted via the existing
dropped_transactions / dropped_vertex_builds counters.
- chmod 0o700 the outbox root + per-PID directory so sanitized-but-still-
sensitive payloads aren't exposed cross-user on multi-tenant hosts.
Suppressed on platforms where chmod is a no-op (Windows).
- Added test_adopt_orphan_outboxes_honors_max_queue.
The remaining two copilot notes (private API access to DatabaseService and
diskcache.Cache.close) were also flagged by the in-tree review; tracking
separately. The "diskcache not declared" note is a false positive — the
dependency is at src/backend/base/pyproject.toml:76.
* perf(telemetry): address coderabbit review
- Sweeper hands off dirty sets via capture-and-clear so concurrent
flushes during a retention pass aren't wiped by the post-commit
subtract; failure path restores the snapshot.
- Stress README uses a concrete Postgres DSN matching the docker
example instead of an unset env var.
- Test PID-probe loops bounded via a shared helper with pytest.fail
fallback.
* perf(telemetry): swap diskcache outbox for stdlib sqlite (CVE-2025-69872)
Replaces the diskcache.Deque-backed spill outbox with a stdlib sqlite3
outbox (WAL mode, JSON payloads in TEXT). diskcache 5.6.3 has
CVE-2025-69872 (pickle deserialization RCE for an attacker with write
access to the cache dir), no fixed version released, and was never
declared as a dependency — import failed on Python 3.10.
The replacement is encapsulated in a small _Outbox helper that owns the
connection, schema, JSON codec, and lifecycle. The codec uses tagged
wrappers for datetime and UUID so SQLAlchemy's typed columns accept
restored payloads on the way back out.
Hardening informed by a survey of OTel collector, Fluent Bit, Vector,
Prometheus remote_write, Datadog agent, Logstash, and Filebeat:
- Per-PID outbox dirs now stamp an owner.json (hostname + Linux boot_id
or time()-monotonic() proxy). Adoption only proceeds when host+boot
match; cross-host or pre-owner-file dirs are logged and skipped so a
recycled PID after a container restart cannot pull in a stranger's
spill data.
- PRAGMA synchronous=FULL on the spill connection so the shutdown
commit hits the platter (NORMAL only fsyncs on WAL checkpoint, which
may never run if the process exits immediately after commit).
- Spill honors telemetry_writer_max_queue with drop-oldest, matching
the producer-side overflow policy so a backlogged buffer at shutdown
can neither stall teardown nor fill disk.
- append_all encodes payloads up front and only clears the deque after
the transaction commits — no more partial-drain on mid-flight SQLite
failure. drain collapses to a single DELETE FROM outbox after the
SELECT.
- Exception handlers at the spill/restore/adopt boundaries narrowed to
(sqlite3.Error, OSError) so genuinely unexpected exceptions
propagate rather than being silently logged.
Tests cover: cross-host orphan skipped, missing-owner orphan skipped,
spill cap drops oldest, and a realistic UUID+datetime payload
surviving spill → restore → SQLAlchemy INSERT.
* [autofix.ci] apply automated fixes
* fix(telemetry): name wait_for inner tasks so pyleak can filter
Integration tests using @pyleak_marker were flagging an inner asyncio
task created by the telemetry writer's `wait_for(Event.wait(), ...)`
loops. The wrapper task is auto-named (Task-N) and lands mid-await
across the test boundary, so pyleak counts it as leaked. The writer
itself shuts down cleanly via teardown; the "leak" is a pattern
mismatch with pyleak's per-test snapshot model, not a real lifecycle
bug.
Wrap Event.wait() in an explicitly named task (`telemetry-writer-tick`,
`telemetry-writer-backoff`, `telemetry-sweeper-tick`) via a small
_wait_or_shutdown helper, and extend pyleak_marker with a task_name_filter
that excludes `telemetry-*` from leak detection. CI was green on earlier
commits in this branch only because the diskcache import error
prevented the writer from starting at all — fixing the import surfaced
this latent pyleak collision.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* test(telemetry): disable writer in tests so reads see writes synchronously
* perf(telemetry): age out cross-host orphan outboxes on shared volumes
A pod on a shared PV (NFS/RWX) cannot adopt rows from a dead pod on a
different host because the dead pod's hostname doesn't match. Without a
janitor those directories accumulate forever. Add an owner-file mtime
heartbeat from the sweeper plus a prune pass that deletes cross-host
outboxes whose owner file hasn't been touched within
telemetry_writer_orphan_max_age_s (default 1h). Same-host orphans still
flow through the existing adoption path.
* perf(telemetry): add byte-aware flush + drop strategy
Today the writer bounds memory by row count only, so a worker logging fat
vertex_build artifacts can hold tens of MB per row and silently dwarf the
configured max_queue. Add a size_strategy switch ('count' | 'bytes' |
'either', default 'count' for parity) with two byte thresholds:
- batch_size_bytes (256KB) caps per-flush INSERT size when bytes apply
- max_queue_bytes (200MB) drops oldest by bytes when bytes apply
Parallel sizes deques mirror the payload buffers so accounting stays
consistent across drain, spill, restore, and the cancel/retry rollback.
Single rows above the byte budget are still emitted (no row is refused);
operators get dropped_*_bytes counters alongside dropped_*.
* test(telemetry): tighten byte-strategy assertions and cover end-to-end paths
Address gaps in the byte-aware strategy tests:
- pin exact dropped counts and remaining buffer state for the drop-by-bytes
case (was 'greater than zero')
- compute exact drain count from encoded size (was a 1-4 range)
- round-trip bytes through spill+restore to verify size deque is rebuilt
- round-trip bytes through orphan adoption for the same reason
- exercise the sweeper loop end-to-end to confirm heartbeat + prune are
wired in (previously only unit-tested in isolation)
Clarify in the settings docs that the byte caps measure encoded JSON size,
not Python in-memory footprint.
* ref: updates to telemetry writer PR (#13294)
* fix(telemetry-writer): harden error handling and add missing test coverage
Critical:
- teardown() now re-raises CancelledError after awaiting the writer task so the
asyncio cancellation chain propagates correctly on lifespan task kill
- suppress(OSError) on owner file write replaced with explicit error log so
operators know when disk-spilled rows will not be recoverable on restart
High / important:
- Escalation threshold check changed from == to >= so the error log fires on
every failure past the threshold, not just the 6th
- Dead `except OSError` on time.time() - time.monotonic() changed to
`except Exception` since time functions cannot raise OSError
- Removed redundant `from uuid import UUID` inside _run_retention_pass (already
imported at module top)
- Orphan directory cleanup: replaced blanket suppress(OSError) with per-child
suppress so ENOTEMPTY on an individual rmdir doesn't abort the whole loop and
leave the parent directory leaking silently; outer failure now logs at debug
Tests (3 new):
- test_retention_sweep_caps_vertex_builds_per_vertex: inserts 8 builds for a
single vertex with max_per_vertex=3 so the per-vertex DELETE subquery
actually executes (previous test used 8 distinct vertex IDs, bypassing it)
- test_either_strategy_trips_on_bytes_first: verifies bytes can be the first
trigger under 'either' strategy (previous test only covered the count-first path)
- test_writer_retries_on_batch_failure: injects 2 flush failures then success;
confirms failed_batches increments, rows are preserved in the buffer, and
flushed_rows reflects the final successful write
* ci: add stress-tests job to nightly build pipeline
Wires the stress-tests workflow into nightly so telemetry write stress
tests run automatically. Also gates release-nightly-build and
slack-notification on stress-tests result so a stress regression blocks
the nightly release and surfaces in Slack.
* ci: run stress tests in nightly without blocking release
Stress tests are informational for now — a failure is visible in the
workflow run and Slack but does not gate the nightly release or build.
* fix(telemetry-writer): address PR review on cancelled teardown and nightly stress tests
- Add the stress-tests.yml reusable workflow (was untracked, so the
nightly stress-tests job could never resolve)
- Notify Slack on stress-tests failure (add to slack-notification gate
and FAILED_JOB detection as non-blocking)
- Run sweeper cancel, disk spill, and engine dispose in a finally so a
cancelled teardown() still persists the in-memory buffer
- Add tests for the cancelled-teardown spill path and the >= escalation
threshold; drop the misleading wait-loop in the retry test
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
|
|||
| d3ee447e31 |
fix(canvas): anchor new note bottom-center to cursor on placement (#13441)
* fix(canvas): anchor new note bottom-center to cursor on placement * improve notes component * [autofix.ci] apply automated fixes * improve testcases --------- Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local> Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |