merge: sync release-1.11.0 into cz/hitl-v2 - #13985
Conversation
) 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.
…solve (forward-port) 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. Forward-port of ee659ca from release-1.11.0. Main is on the 1.10.0 line, so the bundle floors here restamp to lfx>=1.10.0.dev0,<2.0.0 via the same sync script; the next minor fork's make patch now produces a floor its own X.Y.0.devN nightlies satisfy.
…e unit-test step timeout (main) (#13584) 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.
…e 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.
…ility (main) (#13589) 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.
…ility (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.
…ild (#13591) 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.
…ild (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.
…3597) 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.
…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.
…idation (#13599) 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.
…idation (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.
…ack (#13603) 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.
…ack (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.
…ack (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.
…0.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.
) 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.
…1.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.
#13610) 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}.
…k (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}.
…k (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}.
…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>
…r 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).
…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>
* enforce the FileSystemTool credential deny-list * security checks gh review
…ages (#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>
* add-copy-page-keyboard-shortcut * peer-review
* 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>
* add-docs-page-feedback-component * make-buttons-same-size
…13975) fix(frontend): read fresh flow list in delete success
* fix(frontend): repair flow list card a11y * fix(frontend): clear flows page a11y debt * test(frontend): update header count a11y names * test(e2e): open flow cards via action button * fix(a11y): address flow card review * chore: update secrets baseline * fix(a11y): improve settings table scans * refactor(a11y): isolate table scan fixes * test(frontend): fix shard click targets
* feat(bundles): add torch-free all-no-torch extra to lfx-bundles lfx[bundles] resolves to lfx-bundles[all], which pulls torch via the cuga and codeagents providers. Add a generated all-no-torch aggregate (all minus those two, 70 of 72 providers) so slim/CPU images can install the full provider set without torch. Generated by consolidate_bundles.py via the new TORCH_EXTRAS set; bump lfx-bundles 1.0.0 -> 1.1.0. Add a guard test that re-resolves lfx-bundles[all-no-torch] and fails if any torch-family distribution appears, catching providers that pull torch transitively (e.g. via langchain/sentence-transformers) on future updates. * [autofix.ci] apply automated fixes * chore(bench): add lfx cold-start + forkserver benchmark harness (WIP) Benchmark harness used to measure lfx cold start and the planned TRM fork-from-warm path: forkserver_bench.py (cold/warm/concurrency/sustained/ ramp modes), Dockerfile.bench, the prewarm shim, and sample flows (no-LLM inputoutput, model flow, two CPU-intensive agent flows). Rough WIP; flow api_key fields are blank (read OPENAI_API_KEY from env at runtime). * test(bundles): fail-loud on real resolver errors in all-no-torch guard Address CodeRabbit review on #13886: the resolution guard skipped on any non-zero uv pip compile exit, so an invalid extra or a torch-introducing dependency conflict would turn the test green. Skip only on transient network/registry failures; pytest.fail on unexpected resolver errors. * [autofix.ci] apply automated fixes * fix(ci): green ruff in bench harness + teach extras guard the all-no-torch aggregate - forkserver_bench.py: split two D205 multi-line summaries and one E501 - test_lfx_bundles_extras.py: exclude both generated aggregates (all, all-no-torch) from the per-provider drift checks and add an explicit guard that all-no-torch == all minus TORCH_EXTRAS (cuga, codeagents) * docs(bundles): fix stale all-torch -> all-no-torch in TORCH_EXTRAS comment Leftover from the all-torch -> all-no-torch rename; the comment referenced a non-existent all-torch aggregate. No behavior change. * chore: remove bench harness from all-no-torch PR scripts/bench/* was unrelated scope creep on this bundle PR; remove it. * feat(install): make torch opt-in by default (#13894) Default `pip install langflow` no longer pulls torch. Two changes flip the default, plus the collateral fixes the flip requires: - langflow now depends on lfx-bundles[all-no-torch] instead of [all], dropping the torch-pulling bundle providers (cuga, codeagents) from the default install. - Remove the torch-pulling extras (cuga, docling, easyocr, opendsstar) from langflow-base[complete]. Their definitions remain, so each installs on demand; the File component's docling path is opt-in via `pip install "langflow[docling]"`. Collateral: - Declare openpyxl as a hard lfx dependency. It backs the File/Save-File .xlsx read/write path (pd.read_excel / to_excel(engine="openpyxl")) but was reachable only transitively via docling-slim -- without this the xlsx path breaks once docling becomes opt-in. - Regenerate uv.lock so the default resolution actually drops torch. Verified: `uv export --no-dev` resolves 0 torch packages and includes openpyxl (578 pkgs, down from ~689). - Update docs that claimed `pip install langflow` is the everything-included install. Builds on the lfx-bundles[all-no-torch] aggregate (#13886). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * [autofix.ci] apply automated fixes * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * chore: trigger CI * fix(bundles): drop dangling exa reference from all-no-torch extra The merge of release-1.11.0 (exa graduation to standalone lfx-exa, #13968) removed the exa provider extra from lfx-bundles, but the branch-only all-no-torch aggregate kept a stale lfx-bundles[exa] self-reference. hatchling then failed to build the lfx-bundles wheel (Unknown recursive dependency group: exa), breaking environment setup for every CI job. Regenerated via consolidate_bundles.py update_bundles_pyproject and uv lock. all=70, all-no-torch=68 (= all minus cuga/codeagents). * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Eric Hare <ericrhare@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
upgrade uv.lock
…#13665) * chore: upgrade wxo adk core/clients to 2.11 (which support python 3.14) * chore: update adk to 2.12 * fix: telemetry tests post/patch shapes (422 error) and random mocks (404 error)
… to lowercase (#13907) * fix(traces): migrate legacy uppercase spanstatus/spantype enum labels to lowercase Postgres databases whose `spanstatus`/`spantype` enum *types* were first materialized via `SQLModel.metadata.create_all` on a build predating #12820 carry UPPERCASE labels (`OK`, `ERROR`, `CHAIN`, ...), because a bare `sa.Enum(SpanStatus)` emits the enum *names*. The alembic migration `3478f0bd6ccb` instead creates them with lowercase labels. Since #12820 the app binds the lowercase enum *values* (`values_callable`), so on an uppercase-labelled DB every span/trace insert is rejected by Postgres at the type level: `invalid input value for enum spanstatus: "ok"`. The read-side `_LegacyCaseEnum` decorator (#13000/#13346) only normalises stored strings on read — it cannot widen the type's label set, so writes still fail. Add an idempotent, Postgres-only data migration that relabels the affected enum types to their canonical lowercase form via `ALTER TYPE ... RENAME VALUE` (metadata-only: no table rewrite, no data lock, transaction-safe). Each rename is guarded by a `pg_enum` lookup so it is a no-op on already-correct DBs and on SQLite. `spankind` is excluded (its members have name == value). Downgrade is a deliberate no-op to avoid recreating the broken state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(traces): scope enum-label lookup to the search-path-visible type CodeRabbit (#13907): _existing_labels matched the enum by typname across all schemas, but _rename_labels issues an unqualified ALTER TYPE that resolves only to the search-path-visible type. In a multi-schema DB, a same-named enum with lowercase labels in another schema could satisfy `new_label in labels` and skip the rename on the actually-broken visible type. Add `pg_type_is_visible(t.oid)` so introspection and DDL operate on the same type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [autofix.ci] apply automated fixes * fix: repoint span-enum migration onto 844cad9a53fa merge head release-1.11.0 merged main after this branch was cut, adding e1705947c729 and the merge revision 844cad9a53fa on top of the old head 4f0d2c9a8b7e. With c3e7a1b9d2f4 still revising 4f0d2c9a8b7e the merged graph had two alembic heads, so 'alembic upgrade head' failed at backend startup and cascaded across all CI jobs. Repoint down_revision (and the chain test) to 844cad9a53fa to linearize the graph. Also make test_upgrade_from_main_branch merge-aware: downgrading across a merge revision un-merges it, legitimately leaving one version-table row per joined lineage, so the single-head get_current_revision() call raised CommandError for any branch that adds a migration on top of a cross-branch merge head. Assert via get_current_heads() instead: main's head must be current again and the branch's own head unapplied. * [autofix.ci] apply automated fixes * fix: remove trailing comma corrupting .test_durations The checked-in pytest-split durations file ended with a trailing comma before the closing brace, so PytestSplitPlugin's json.loads raised JSONDecodeError at configure time and every unit-test group job died with INTERNALERROR before collecting a single test (Group 5 surfaced it; the rest were cancelled by fail-fast). The same corruption exists on release-1.11.0, so this repairs the base branch on merge. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(ci): publish bundle deps before nightly main * fix(ci): wait for bundle pypi propagation
* docs-wxo-python314-compatibility * docs: bump ADK version to 212
…13816) * feat: native v2 workflows endpoint with pluggable stream protocols Rebased onto release-1.10.0. The base independently rebuilt the v2 workflows backend (RBAC, body globals, share-aware fetch); keep our forward design and conform its auth to that work: 1. Auth: keep get_current_user_for_workflow (session-or-API-key authN that does not hold a DB connection during the inline run, avoiding the SQLite lock contention api_key_security would cause) and enforce the base's RBAC on top: ensure_flow_permission(EXECUTE) before run, (READ) before status reconstruct, with widen_for_shares fetch. 2. Port the base's request-body globals onto the v2 WorkflowRunRequest. The X-LANGFLOW-GLOBAL-VAR-* headers stay supported (the Responses API passes globals that way); body globals win on conflict. Converters echo the effective globals via effective_globals. 3. Public endpoint keeps the v1 build_public_tmp posture (access_type==PUBLIC, run-as-owner); RBAC applies to the authenticated endpoint only. 4. Preserve the base's post-build KB-cache invalidation in the AG-UI build path. The endpoint, AG-UI bridge, pluggable stream adapters, public endpoint, and re-attach are unchanged. * feat(api): add output_text and session_id to v2 workflow response The synchronous /api/v2/workflows response keyed every result under its component id, so reading the answer meant knowing an id you can't predict. Surface two additive fields: - output_text: the flow's single text answer (ChatOutput/TextOutput). None when the flow has zero or multiple text outputs, so callers read outputs rather than the shortcut guessing which channel is the answer. - session_id: echoes the resolved session so chat/memory callers can continue the same thread (v1 /run returned this; v2 had dropped it). outputs is unchanged, so this is non-breaking. * test(api/v2): cover output_text and session_id on the v2 workflow response Pin the sync-response shortcuts on the v2 workflows endpoint: - output_text surfaces the lone ChatOutput/TextOutput text and stays None for non-output message nodes, data-only flows, and multi-text flows - session_id echoes the resolved session; the error response exposes neither - each outputs entry exposes only {type, status, content, metadata}, with the component id carried by the dict key Also drop the component_id kwarg the converter passed to ComponentOutput, which has no such field and silently dropped it. * feat(api/v2): structured output with resolution reason on v2 response Replace the flat output_text shortcut with an `output` object carrying the resolved text answer plus a `reason` that explains why it resolved that way (single/multiple/none/non_string/failed), so a null answer is always diagnosable instead of silently None. `reason` follows the LLM-domain finish_reason/stop_reason convention, distinct from the lifecycle status. Also add `display_name` to each ComponentOutput (the stable component id stays the dict key) and a computed `has_errors` flag derived from errors. * feat(api/v2): add request-side output selection (output_ids) Let a sync caller name the output(s) they want via output_ids so output.text resolves deterministically (reason=single) on multi-output flows instead of going null. Selection is steer-only: it picks the answer among the named outputs without filtering the outputs map. Invalid ids are rejected with 422 before the flow runs (and before any job row is created), so a typo costs no compute. Resolution considers selected outputs that actually fired, so branching flows resolve to whichever candidate ran. * feat(api/v2): emit per-output events on the langflow stream Give v2-workflows sync and the langflow stream protocol one parser. The stream now emits a normalized "output" event per terminal output carrying an OutputEvent (the ComponentOutput shape sync returns in outputs[id], plus component_id). A shared build_component_output() backs both the sync converter and the adapter, and the build loop ships authoritative vertex metadata as an additive output_meta key on end_vertex (existing consumers read build_data and ignore it). This is access-pattern parity (one parser, same fields, same terminal set), not byte-identical content: the stream reuses the v1 build path whose display serialization differs from sync's run_graph output. * fix(api/v2): enforce no-code-execution gate on public workflow endpoint The v2 public endpoint only ran validate_flow_for_current_settings and skipped validate_public_flow_no_code_execution, which the v1 build_public_tmp path applies. A public flow containing a Python interpreter/REPL (or the legacy Python Code Structured tool, Smart Transform lambda) was therefore an unauthenticated server-side code-execution primitive (report H1-3754930). Mirror v1: import the validator and call it right after the public-access gate. PublicFlowValidationError subclasses CustomComponentValidationError, so the existing handler already sanitizes it to a 400 'This flow cannot be executed.' without leaking the blocked component class names. Add a non-mocking test that builds a public flow with a real PythonREPLComponent and asserts the sanitized 400 (verified RED: returns 200 without the gate). LE-1389 * fix(api/v2): reconstruct background workflow status from job-keyed vertex builds A completed background job's GET status 500'd with 'No vertex builds found for job_id'. The background build path differed from the sync path twice: 1. generate_flow_events minted a fresh run_id instead of using job_id, so vertex builds were keyed by an id the status query never uses. Thread run_id through _stream_event_frames -> generate_flow_events and pass job_id from the background buffer so graph.run_id == job_id (the sync path already does graph.set_run_id(job_id)). 2. The SSE build loop (build_vertices) only persisted builds when log_builds was set and never passed job_id. Tie log_builds to job-tracked runs (run_id present) and pass job_id=graph.run_id on the persist call. Job-tracked runs also persist streaming terminal vertices so reconstruction is complete; the live build path (run_id is None) keeps its original behavior, so the v1 build path is unchanged. Test: a real background run polled to completion, then GET status asserts a reconstructed 200 (verified RED: 500 'No vertex builds found' before the fix). Covers the non-streaming flow. v1 build path unchanged (35 build tests pass); AG-UI suite 46 pass. LE-1389 * fix(api/v2): merge workflow AG-UI cancellation hardening LE-1389 * fix(api/v2): signal cross-worker workflow stops LE-1389 * fix(api/v2): report unconfirmed workflow stops LE-1389 * fix(api/v2): keep background workflows out of polling watchdog LE-1389 * fix(api/v2): buffer parallel messages in the AG-UI translator instead of dropping them Parallel components stream tokens for different message ids interleaved. The translator tracked a single open message: the first foreign token closed the open message and tombstoned its id, so every later event for it was dropped and its remaining text never reached the client. Tokens for a message that cannot take the wire now buffer until the open message genuinely ends (its add_message finalizer), then flush in arrival order; complete messages landing mid-stream buffer the same way instead of interleaving a second START. end/error drain all buffers before the terminal event. The wire still carries at most one open text message, so the stream stays AG-UI-conformant. * fix(api/v2): gate AG-UI message finalization on non-partial state and purge removed buffers A partial add_message re-fire (the agent path emits these at tool start/end for a message it is still streaming) was treated as the finalizer: it closed and tombstoned the id, so the post-tool answer was dropped. Only a non-partial add_message finalizes now; state defaults to complete, so payloads without properties are unchanged. remove_message now purges a buffered message and tombstones its id, so text the backend retracted is not flushed to the client later. * Fix AG-UI workflow lifecycle edges * [autofix.ci] apply automated fixes * fix(frontend): enable downlevelIteration for jest Set/Map iteration ts-jest compiles with target es5; without downlevelIteration, [...set] and for...of over a Set/Map emit ES5 that yields nothing. That silently broke the AG-UI bridge tests: runningNodeIds spread, markRunningNodesFailed, and restoreOriginalBuildStatuses all iterated empty. Production (Vite/SWC, modern target) was never affected; only the ts-jest harness was. Fixes the 3 failing jest tests on this branch with no other suite changes (4994/4994 pass). * fix(api/v2): surface inactivated branch vertices over AG-UI A branch component (If-Else, Conditional Router) reports its not-taken vertices in build_data.inactivated_vertices, but the AG-UI translator only emitted the branch node's own success/error status and dropped that list. The canvas seeds every planned node as pending from vertices_sorted; skipped vertices then get no build_start/end_vertex, so they stayed stuck on pending instead of rendering as inactive (the v1 build path marked them INACTIVE). The translator now appends an inactive STATE_DELTA op per inactivated vertex, and the frontend bridge maps the new inactive status to BuildStatus.INACTIVE and tears its edges down like a completed node. Fixes the If-Else regression in general-bugs-reset-flow-run.spec.ts. * fix(api/v2): dedupe repeated inactive node deltas in AG-UI stream build.py keeps reporting a conditionally-excluded vertex in inactivated_vertices on every subsequent end_vertex (the excluded set persists until the ConditionalRouter clears it), so the translator was putting the same inactive STATE_DELTA on the wire once per remaining vertex. Track emitted inactive nodes and skip re-emitting; drop a node from the set when it actually runs again (build_start/end_vertex) so a loop re-activation can still re-emit inactive later. * fix(api/v2): address review findings on the v2 workflows endpoint - recover session_id for completed background jobs from the persisted terminal message instead of always returning null, so GET status can continue the same chat/memory thread - replay a user-cancel as a CUSTOM cancel marker + RUN_FINISHED (agui) and a `cancelled` terminal (langflow) instead of RUN_ERROR, so a re-attaching client no longer reads a deliberate stop as a failure - cancel the evicted still-running buffer writer when the background-run registry is full, so it stops appending into a run no reader can find - derive per-component status from the error artifact / valid flag instead of hardcoding COMPLETED, and stop the langflow adapter dropping `valid` - throttle the unauthenticated public endpoint per IP and bound its input_value/session_id length - document the sync-only scope of request-body globals - document that live event re-attach is intentionally owner-only * test(lfx): register public_flow_rate_limit_per_minute in settings composition * refactor(v2 workflows): split workflow.py and address review blockers Splits the ~1.5k-line workflow.py into focused modules and folds in the execution-timeout and error-sanitization fixes from Cristhianzl's review of #13307. - B1: workflow.py now holds only the four route handlers. Validation guards move to workflow_validation, the sync/stream run loop to workflow_execution, and the durable background machinery to workflow_background (layered, acyclic). - I1: add workflow_execution_timeout (default 300) and apply a single wall-clock ceiling across sync, stream, background, and public via _stream_event_frames. A timeout becomes a sanitized terminal error and marks a background job failed. - I3: the route error handlers no longer echo raw exception text. They return a generic, code-tagged message and log the full exception server-side. - R1: remove the "commented out / future scope" comments that sat over live dataframe-extraction code in converters.py. - R4: drop the worker-routing internals from the reattach 409 message. Tests cover the timeout terminal-error path and the error-body sanitization, and the settings field-count guard is updated for the new setting. * refactor(lfx): extract v2 workflow contract layer into lfx.workflow Moves the protocol-agnostic pieces of the v2 workflows API out of the langflow backend into lfx so both the backend and `lfx serve` can share one contract. First step toward giving lfx (the production runtime) the v2 workflows API. - Move api/v2/adapters/, agui_translator.py, and converters.py to lfx/workflow/. They depend only on lfx.schema.workflow and ag_ui (already an lfx dep), so lfx carries the contract with zero langflow imports. - Decouple the one langflow reference: converters typed run_response against langflow.api.v1.schemas.RunResponse (TYPE_CHECKING only). Replaced with a local RunResponseLike Protocol (outputs + session_id), the only attributes used. - Repoint the six backend v2 workflow modules to import from lfx.workflow. - Move the five protocol-agnostic contract tests into src/lfx/tests/unit/workflow/ (run in the lfx-only env). test_output_event_parity and test_workflow_agui stay in langflow (they need langflow.api.build) with repointed imports. Coverage unchanged: 201 contract tests pass in the lfx-only env, 191 backend v2 tests pass; 392 total, same as before the move. * feat(lfx serve): v2 workflow endpoints (sync + stream) on lfx serve Gives the production runtime (lfx serve) the same v2 contract as the langflow backend's POST /api/v2/workflows, built on the shared lfx.workflow layer. - New POST /workflows endpoint: WorkflowRunRequest in, WorkflowExecutionResponse (sync) or an SSE stream (langflow/agui protocols) out. flow_id resolves against the serve registry; per-request deepcopy+stamp mirrors the run/stream endpoints. - sync runs via run_graph_internal (the same primitive the backend sync path uses) so the converter sees the aggregated RunOutputs shape. - stream drives the run with a token-stream EventManager wired into execute_graph_with_capture and feeds queue events through the shared StreamAdapter; emits a terminal end so the adapter closes the run cleanly. - background and public modes are rejected (422); tweaks/data/files/globals and partial-run boundaries are rejected too (no per-request graph rebuild yet). - execute_graph_with_capture gains an optional event_manager param. Tests build a real ChatInput->ChatOutput flow (no mocks) and exercise sync, both stream protocols, and the guards. 329 lfx serve + contract tests pass. * feat(lfx): workflow-router seam owned by lfx via a host-DI seam lfx now exports a WorkflowHost Protocol + create_workflow_router() factory. The router owns the env-neutral handler body (run dispatch, single SSE loop, error mapping, dev-api guard); the host supplies auth/flow-lookup/session. Bare serve uses a no-db ServeWorkflowHost (supports_background=False), which collapses the duplicated serve handler into the shared router. Background/durable routes register only when host.supports_background is True, so bare serve's surface stays exactly POST /workflows. developer_api_guard is a factory flag (default True) so serve keeps its current open behavior. Adds a no-mock cross-host contract test pinning the request/response and SSE shape so the two hosts can't drift. * feat(api): route LF v2 workflow run through the lfx host-DI seam Langflow now mounts the shared lfx workflow router for POST /api/v2/workflows via a LangflowWorkflowHost (auth->UserRead in its own session, flow lookup, ensure_flow_permission, readonly session, background submit). The durable status/stop/events routes stay LF-rich on an LF-owned background router, so only one handler exists per method+path. The public workflow router is untouched. LF streaming and sync stay LF-specific via host.run_sync / host.stream_response seams (LF keeps the v1 build loop with agui side-channel + vertex persistence; bare serve keeps lfx's lean defaults). create_workflow_router gains auto_register_job_routes so LF can enable background submit without the router auto-registering its generic job routes. * fix(api): drop orphaned execute_workflow and keep stream-protocol precedence Self-review follow-ups on the workflow-router seam: - The shared router validated stream_protocol after fetching/authorizing the flow, so an unknown protocol against a missing/unauthorized flow returned 404/403 instead of the old 422. Move the 422 check above get_flow to keep the pre-seam precedence. - execute_workflow lost its route decorator when the run path moved to the lfx router via LangflowWorkflowHost, leaving a production-dead function kept only for three direct-call tests. Delete it and point those tests at the real helpers the host calls (build_stream_response, authorize_flow_action), which also drops their get_flow_by_id_or_endpoint_name mocks. * fix(api): echo requested flow identifier in v2 workflow 404 reframes A denial or owner-override 404 echoed str(flow.id) (the resolved internal UUID). When the caller referenced the flow by endpoint name, that leaked the canonical UUID and changed the flow_id in the error body vs the request. The pre-seam handler echoed the requested identifier; restore that. authorize_flow_action takes the requested id (LangflowWorkflowHost passes ResolvedFlow.flow_id, which already holds the caller's value); the owner-gate reframe uses parsed.flow_id. Adds a test that a denied endpoint-name request echoes the name, not the UUID. Also documents ResolvedFlow.graph as a host-defined artifact (Graph for serve, FlowRead for langflow), per review. * test+docs: pin precedence and leak fixes, restore POST OpenAPI schema From the ultracode review: - The 422 stream-protocol precedence was untested (all cases used an existing flow). Pin it: a host whose get_flow 404s plus a bad protocol must still 422. - The UUID-leak test exercised the helper directly, not the host wiring that supplies requested_id. Route it through LangflowWorkflowHost.authorize so a regression in that wiring is caught. - The authenticated POST lost its responses= OpenAPI schema when the route moved to the shared router. Thread responses through create_workflow_router and restore WORKFLOW_EXECUTION_RESPONSES on the langflow mount. - Soften the router docstring: SSE framing is single-sourced only for hosts on the lfx default (langflow overrides stream_response). * fix(v2): restore OperationalError->503 on the inline-run and background paths; correct router developer_api_guard docstring A DB OperationalError raised during the run (create_job runs outside the inner try) fell through to a 500 instead of the pre-seam 503 DATABASE_ERROR on both run_sync_with_mapping and submit_background_with_mapping. Add the missing OperationalError->503 branch (body byte-identical to the fetch-path mapping). Also fix the create_workflow_router docstring: it claimed langflow keeps developer_api_guard=True, but every mount passes False and the v2 surface never carried the guard. * fix(lfx-serve): align v2 workflow endpoints with backend contract Accept request-level globals (applied as request-scoped variables) instead of rejecting them with 422; validate output_ids against the flow's terminal nodes before running (422 on unknown) rather than wasting a run; and convert stream-queue overflow into an explicit error frame instead of silently dropping events for a slow SSE client. * fix(lfx-serve): carry the v2 contract fixes into the router seam The seam moved the bare-serve endpoint logic out of serve_workflow.py into lfx.workflow.router before three contract fixes landed on the inline version, so router.py's default path was missing them. Port them into the lfx-default run/stream functions (bare serve only; the langflow host overrides these): - accept request-level globals, applied as request-scoped variables on the per-request graph copy, and echoed as effective_globals (drop the globals rejection in _reject_unsupported_fields) - reject unknown output_ids up front with 422 UNKNOWN_OUTPUT_IDS - replace the plain bounded asyncio.Queue with _WorkflowEventQueue so a slow SSE client gets an explicit error frame instead of silently dropped events Repoint the serve test's _terminal_node_ids/_WorkflowEventQueue import at lfx.workflow.router (they moved out of serve_workflow.py). * fix: honor no_env_fallback on sync runs and map DB errors in authorize Two points from Debojit's review: - Sync /workflows bypassed the no_env_fallback isolation. run_workflow_sync activated request_variables but not the no_env_fallback contextvar, so a sync run under LFX_SERVE_NO_ENV_FALLBACK=1 still resolved credentials from os.environ while stream (via execute_graph_with_capture) was isolated. Activate it around the run and reset in finally, mirroring the stream path. - authorize_flow_action caught only HTTPException, so an OperationalError from ensure_flow_permission's audit write (DB lock under inline-run contention) escaped as a bare 500 instead of the retryable 503 DATABASE_ERROR contract the fetch path keeps. Add the same OperationalError -> 503 / Exception -> 500 arms. Tests: a no-mock real-graph test that the sync path activates the isolation mid-run and resets after, and a 503-on-DB-lock test through the host wiring. * fix(lfx-serve): mount v2 workflows at /api/v2/workflows to match backend Per Debojit's review: serve exposed the shared workflow router at /workflows while the langflow backend serves it at /api/v2/workflows, so switching runtimes meant changing the URL path, not just the host. Mount the serve router under /api/v2 so the path is identical across runtimes; a client points at a different host/environment with no path change. serve's other routes (/flows, /health) stay root-level. Updates the serve integration tests and docstring. * test(lfx-serve): handle FastAPI >=0.137 lazy _IncludedRouter in serve route assertions release-1.11.0's FastAPI bump makes include_router mount the /api/v2 workflow router as a lazy _IncludedRouter wrapper with no .path, so the three create_serve_app route-introspection tests hit AttributeError. Skip wrappers (these assert directly-mounted serve paths only), matching how the langflow backend already handles the same >=0.137 behavior. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Brings the workflow-router-seam (#13816) and a month of release onto the HITL branch, resolving 47 conflicts. Rule applied throughout: take release's structure and hygiene, keep the durable/HITL semantics on top. api/v2/workflow.py: release's seam skeleton (resolve_flow_for_execution, authorize_flow_action, run_sync_with_mapping, build_stream_response) with the durable route bodies (status reconstruct + Job.result fallback, stop signal ordering, service.events replay) and the /pending and /{job_id}/resume routes. Background submit goes through the durable facade, not release's process-local buffer. workflow_execution.py keeps release's timeout ceiling and event queue and gains job_id/resume plus the human_input_required frame type. build.py unions the pause plumbing with release's run_id vertex-build persistence. Also: idempotency_key now survives the parse boundary (ParsedWorkflowRun) so background dedupe actually reaches the service; durable runs key vertex builds by job_id so a completed job's status recovers its session_id; graph/base.py keeps the pause/checkpoint methods alongside release's branch exclusion; the HumanInputContent tag is restored to both ContentType unions; Cris's migration chain is re-parented onto release's head (single alembic head).
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Keeps Cris's LFX_SERVE_DURABLE_DB branch, which wraps the seam's create_workflow_router in its stateless else. DurableServeWorkflowHost inherits ServeWorkflowHost's resolve_caller/_run_user_id, so serve v2 identity threading still applies. runtime.py keeps release's fuller executor-kind docstring.
…13862) fix(lfx): read local files under S3 storage
…) into sync branch Conflicts were generated files only: component_index.json takes Cris's, and .secrets.baseline was regenerated against the merged tree. His workflow.py ownership tightening auto-merged into the durable get_workflow_status body.
|
✅ Migration Validation Passed All migrations follow the Expand-Contract pattern correctly. |
✅ Test Coverage AdvisorNo source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉
|
This comment has been minimized.
This comment has been minimized.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## cz/hitl-v2 #13985 +/- ##
==============================================
+ Coverage 58.68% 59.79% +1.10%
==============================================
Files 2342 2407 +65
Lines 224156 231287 +7131
Branches 31678 34636 +2958
==============================================
+ Hits 131556 138306 +6750
- Misses 91098 91343 +245
- Partials 1502 1638 +136
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Build successful! ✅ |
Hey @Cristhianzl, the v2 workflow router seam just landed on release-1.11.0 (#13816), and it rewrote
api/v2/workflow.pypretty heavily. Your branch was about a month behind release, so merging up was going to be painful. I did the merge for you here so you can just merge this intocz/hitl-v2instead of resolving it yourself.47 files conflicted. The rule I followed everywhere: take release's structure and hygiene, keep your durable + HITL semantics on top. I tried hard not to drop anything of yours, but you know the HITL intent better than I do, so please look closely at the files below.
What I had to make a call on
api/v2/workflow.pyis the big one. Release moved the POST run path into lfx (create_workflow_routerbound toLangflowWorkflowHost) and split helpers intoworkflow_execution.py. So this file is now release's seam skeleton (resolve_flow_for_execution,authorize_flow_action,run_sync_with_mapping,build_stream_response) with your durable route bodies dropped into it: status reconstruct with theJob.resultfallback, the stop signal-before-CANCELLED ordering,service.eventsreplay, and your/pending+/{job_id}/resumeroutes. Background submit goes through your durable facade, not release's process-local_BACKGROUND_RUNSbuffer.workflow_execution.pykeeps release's newer body (theasyncio.wait_forexecution ceiling,_WorkflowEventQueueoverflow adapter,terminal_error_seendedup) and gains yourjob_id/resumeparams plus thehuman_input_requiredframe-type mapping so the durable runner still detects the pause. I did not port yourevent_manager.on_errorcall indrive(), because release deliberately replaced it with the consumer-side guaranteed fallback. I think that's right, but flag me if you disagree.build.pyunions your pause plumbing with release'srun_id-keyed vertex-build persistence.Three things I fixed that weren't strictly conflicts
idempotency_keynever reached the service.ParsedWorkflowRundidn't carry the field, soparse_workflow_run_requestdropped it and your dedupe +DuplicateJobError-> 409 was dead through the seam. Added it to the dataclass and threaded it throughsubmit_background_with_mapping.run_id=str(job_id)into the build loop so vertex builds are keyed by the durable job_id. Without itreconstruct_workflow_response_from_job_idfound nothing, fell back to theJob.resultrebuild, and a completed job's status reportedsession_id: null.track_job_status=Falsestill holds, so no duplicate WORKFLOW row.HumanInputContentfrom bothContentTypeunions (the class survived, theTag("human_input")arm didn't). Restored in lfx and langflow-base.Also re-parented your migration chain onto release's head (
c3e7a1b9d2f4), so there's a single alembic head again. Andgraph/base.pyneeded care: release rewroteexclude_branches_conditionally, and taking release's file wholesale silently deletedcheck_and_handle_pause/request_pause/build_checkpoint/resume_from_checkpoint. Kept both.Tests
test_workflow.py28/28,test_workflow_agui.py70/70,test_workflow_public.py+test_mcp_utils.pygreentests/unit/workflow224,tests/unit/graph275, serve suites greenTwo follow-ups I left alone:
api/v2/workflow_background.py(release's process-local buffer) is now production-dead since your durable substrate replaced it, though some tests still exercise it directly. Andtest_workflow_agui.pyhad no HITL-specific tests on either side, so if yours live elsewhere nothing was lost, but worth a check.You pushed while I was merging
I pulled in
durable HITL background on lfx serveandcode review improvementstoo, so this is current with your branch tip.Your
LFX_SERVE_DURABLE_DBmount inserve_app.pykept its shape: the statelesselsestill returns the seam'screate_workflow_router(ServeWorkflowHost(...)). Worth knowing, since it landed after the seam did:ServeWorkflowHost.resolve_callernow resolves the verified identity and_run_user_idthreads it into the run, soDurableServeWorkflowHostinherits serve v2 identity for the sync and stream paths. Yoursubmit_backgroundoverride doesn't thread it, which may be fine for background, but you'd know better than me.Your ownership tightening in
get_workflow_statusauto-merged into the durable body.test_serve_durable.pyis 9/9.