Skip to content

merge: sync release-1.11.0 into cz/hitl-v2 - #13985

Merged
ogabrielluiz merged 220 commits into
cz/hitl-v2from
cz/hitl-v2-seam-sync
Jul 8, 2026
Merged

merge: sync release-1.11.0 into cz/hitl-v2#13985
ogabrielluiz merged 220 commits into
cz/hitl-v2from
cz/hitl-v2-seam-sync

Conversation

@ogabrielluiz

@ogabrielluiz ogabrielluiz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Hey @Cristhianzl, the v2 workflow router seam just landed on release-1.11.0 (#13816), and it rewrote api/v2/workflow.py pretty 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 into cz/hitl-v2 instead 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.py is the big one. Release moved the POST run path into lfx (create_workflow_router bound to LangflowWorkflowHost) and split helpers into workflow_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 the Job.result fallback, the stop signal-before-CANCELLED ordering, service.events replay, and your /pending + /{job_id}/resume routes. Background submit goes through your durable facade, not release's process-local _BACKGROUND_RUNS buffer.

workflow_execution.py keeps release's newer body (the asyncio.wait_for execution ceiling, _WorkflowEventQueue overflow adapter, terminal_error_seen dedup) and gains your job_id / resume params plus the human_input_required frame-type mapping so the durable runner still detects the pause. I did not port your event_manager.on_error call in drive(), because release deliberately replaced it with the consumer-side guaranteed fallback. I think that's right, but flag me if you disagree.

build.py unions your pause plumbing with release's run_id-keyed vertex-build persistence.

Three things I fixed that weren't strictly conflicts

  • idempotency_key never reached the service. ParsedWorkflowRun didn't carry the field, so parse_workflow_run_request dropped it and your dedupe + DuplicateJobError -> 409 was dead through the seam. Added it to the dataclass and threaded it through submit_background_with_mapping.
  • Durable runs now pass run_id=str(job_id) into the build loop so vertex builds are keyed by the durable job_id. Without it reconstruct_workflow_response_from_job_id found nothing, fell back to the Job.result rebuild, and a completed job's status reported session_id: null. track_job_status=False still holds, so no duplicate WORKFLOW row.
  • The auto-merge silently dropped HumanInputContent from both ContentType unions (the class survived, the Tag("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. And graph/base.py needed care: release rewrote exclude_branches_conditionally, and taking release's file wholesale silently deleted check_and_handle_pause / request_pause / build_checkpoint / resume_from_checkpoint. Kept both.

Tests

  • test_workflow.py 28/28, test_workflow_agui.py 70/70, test_workflow_public.py + test_mcp_utils.py green
  • lfx: tests/unit/workflow 224, tests/unit/graph 275, serve suites green

Two 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. And test_workflow_agui.py had 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 serve and code review improvements too, so this is current with your branch tip.

Your LFX_SERVE_DURABLE_DB mount in serve_app.py kept its shape: the stateless else still returns the seam's create_workflow_router(ServeWorkflowHost(...)). Worth knowing, since it landed after the seam did: ServeWorkflowHost.resolve_caller now resolves the verified identity and _run_user_id threads it into the run, so DurableServeWorkflowHost inherits serve v2 identity for the sync and stream paths. Your submit_background override doesn't thread it, which may be fine for background, but you'd know better than me.

Your ownership tightening in get_workflow_status auto-merged into the durable body. test_serve_durable.py is 9/9.

erichare and others added 30 commits June 9, 2026 13:30
)

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
Cristhianzl and others added 14 commits July 7, 2026 07:51
…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).
@ogabrielluiz
ogabrielluiz requested a review from Cristhianzl July 8, 2026 17:57
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • release-.*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 418f7ad4-561b-4157-8141-fdb344c7930c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cz/hitl-v2-seam-sync

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

ogabrielluiz and others added 3 commits July 8, 2026 15:00
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.
…) 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.
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Migration Validation Passed

All migrations follow the Expand-Contract pattern correctly.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions

This comment has been minimized.

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.28571% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.79%. Comparing base (d5ff101) to head (39d5bb0).
⚠️ Report is 226 commits behind head on cz/hitl-v2.

Files with missing lines Patch % Lines
src/backend/base/langflow/__main__.py 57.14% 3 Missing ⚠️
src/backend/base/langflow/agentic/api/router.py 71.42% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@              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     
Flag Coverage Δ
backend 64.64% <64.28%> (-1.46%) ⬇️
frontend 58.88% <ø> (+1.28%) ⬆️
lfx 58.33% <ø> (+2.94%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ackend/base/langflow/agentic/flows/model_config.py 100.00% <ø> (+12.50%) ⬆️
...nd/base/langflow/agentic/flows/translation_flow.py 96.15% <ø> (ø)
...end/base/langflow/agentic/helpers/code_security.py 99.28% <ø> (+0.70%) ⬆️
...nd/base/langflow/agentic/helpers/error_handling.py 96.82% <ø> (+1.74%) ⬆️
src/backend/base/langflow/agentic/mcp/server.py 53.27% <ø> (-3.55%) ⬇️
...ase/langflow/agentic/services/assistant_service.py 87.33% <ø> (-0.39%) ⬇️
...nd/base/langflow/agentic/services/flow_executor.py 91.05% <ø> (-5.67%) ⬇️
...base/langflow/agentic/services/flow_preparation.py 92.50% <ø> (+0.83%) ⬆️
...base/langflow/agentic/services/provider_service.py 97.91% <ø> (+26.25%) ⬆️
...nd/base/langflow/agentic/utils/assistant_runner.py 93.33% <ø> (ø)
... and 76 more

... and 589 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 45%
45.18% (62380/138056) 69.88% (8660/12391) 43.85% (1437/3277)

Unit Test Results

Tests Skipped Failures Errors Time
5217 0 💤 0 ❌ 0 🔥 18m 38s ⏱️

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Build successful! ✅
Deploying docs draft.
Deploy successful! View draft

@ogabrielluiz
ogabrielluiz merged commit c98878b into cz/hitl-v2 Jul 8, 2026
161 of 164 checks passed
@ogabrielluiz
ogabrielluiz deleted the cz/hitl-v2-seam-sync branch July 8, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.