mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 18:18:10 +08:00
v1.10.0.dev49
17952 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 7e1cbdc814 | Update version and project name v1.10.0.dev49 | |||
| ebb20c58b0 |
fix: Restore custom Agent prompts in starter projects (#13282)
* fix templates prompts * add regression test * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |
|||
| 1e75a269c8 |
fix: Run Flow mutable state leak (#13340)
* fix: isolate Run Flow component state * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * chore: wake ci * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * fix: tune component template copying * [autofix.ci] apply automated fixes * fix: preserve explicit load-from-db state * fix: file component error in run flow * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |
|||
| 104a83c83c |
docs: lfx bundles (#13142)
* docs-initial-overview-bundles-content * fix-broken-link * remove-production-page * docs: check bundles against code and cleanup * docs: fix links |
|||
| a7b8170d53 |
fix: Force streaming on AgentComponent regardless of toggle (#13358)
* fix streaming default true on release * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * add robust tests around streaming to avoid any future errors * [autofix.ci] apply automated fixes * improve fe flaky tests * fix outdated action test * fix flaky outdated action tst --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |
|||
| 1bb95275a5 |
docs: openrouter promoted to global provider (#13271)
docs: check openrouter content and add global |
|||
| e694bbcdff |
docs: troubleshooting for container mismatch (#13217)
* add-troubleshooting-for-container-mismatch * grep-for-volume-name * fix-spacing * danger-admonition |
|||
| 9b7486efe8 |
docs: macos support matrix (#13363)
* docs: macos support matrix * docs: fix linking errors and clarify limitations * cleanup |
|||
| 5e86ab6c5f |
feat: accept v2 workflow globals in request body (#13351)
* fix: accept v2 workflow globals in request body * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * review feedback --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |
|||
| fb3d6ec90b |
feat: OSS authorization foundations for enterprise RBAC (#13153)
* feat: add OSS authorization foundations for enterprise RBAC
Introduce pluggable AuthorizationService, authz plugin tables, and flow
API enforcement hooks. Disabled by default (LANGFLOW_AUTHZ_ENABLED=false);
enterprise Casbin plugin can override via lfx.services entry point.
* fix(authz): allow non-superusers when OSS authorization service is active
OSS LangflowAuthorizationService is a pass-through until enterprise Casbin
replaces it; the previous fail-closed superuser-only check caused 403 on flow APIs.
* fix(authz): harden OSS authorization foundations (bug + tests + docs) (#13216)
* fix: harden OSS authorization foundations (PR (a) — bug + lint + tests)
Hardening pass for #13153 to clear pre-merge checks and raise unit-test
coverage on the new authorization surface before broader feature work.
- Fix read_public_flow: query the flow directly and return when
access_type == PUBLIC; remove user-impersonation via
get_user_by_flow_id_or_endpoint_name and the read_flow delegation
(public means public — no authz call).
- Fix _token_alias_candidates ordering so the _access_token branch is
reachable (previously shadowed by the _token check).
- Register BaseAuthorizationService in import_all_services_into_a_dict so
AuthorizationServiceFactory can resolve its create() return annotation
at startup (mirrors the BaseAuthService pattern).
- Rename test_non_wxo_access_token_does_not_create_bearer_alias to
test_generic_access_token_exposes_bearer_alias; body asserts the alias
IS created.
- Uppercase env-var literals in test_minimal_services to clear
Ruff SIM112/S105; per-line noqa only for the lowercased global header
name x-langflow-global-var-access-token.
- Add unit coverage for batch_enforce (enabled/disabled/empty),
get_allowed_actions, ensure_permission, ensure_flow_permission,
AuthorizationServiceFactory.create, and the LFX no-op default
AuthorizationService.
- Persist+read tests for the seven previously-untested authz models
(AuthzRoleAssignment, AuthzTeam, AuthzTeamMember, AuthzShare,
AuthzEditLock, AuthzAuditLog).
- One-line docstrings on undocumented public symbols across the new
authorization modules and authz database models.
* fix(authz): align authz_role.permissions model nullability with migration
Migration f7a8b9c0d1e2 declares ``permissions`` as ``nullable=False`` but
the model used the default ``Column(sa.JSON)`` (nullable=True), which made
``test_no_phantom_migrations`` detect a phantom modify_nullable diff after
upgrading to head. Explicitly set ``nullable=False`` so the model matches
the migration.
* feat(authz): finish Phase 1 OSS — FlowAction enum, audit, owner override, project domain (#13220)
PR (b) of the OSS authz rollout. Lands every piece the enterprise Casbin
plugin needs to enforce RBAC on flows end-to-end:
- New FlowAction(str, Enum) — canonical action vocabulary (read/write/
create/delete/execute/deploy). Re-used by enterprise PolicySync when
compiling roles + shares into casbin_rule.
- ensure_flow_permission now accepts FlowAction|str, computes domain
from folder_id ('project:{folder_id}' or '*'), and applies an owner
override: when current_user.id == flow.user_id, the call short-circuits
before contacting the plugin (so solo installs keep working with
LANGFLOW_AUTHZ_ENABLED=true). Owner-override is audited too, with
result='owner_override'.
- Guards wired on all 9 flow CRUD routes (was 4): create, read, patch,
delete, put-upsert (write|create branches), batch-create, upload,
bulk-delete, bulk-download. /public_flow/{id} stays unguarded by
design (public means public), and GET /flows/ list-filtering is
deferred to a follow-up PR.
- audit_decision() schedules an AuthzAuditLog row via asyncio.create_task
on every allow/deny/owner_override decision. Failures inside the task
are logged and swallowed so audit never blocks a request. Gated on a
new AUTHZ_AUDIT_ENABLED setting (defaults True; ignored when AUTHZ is
off).
- filter_visible_resources() helper — generic batch_enforce-based filter
for list endpoints. No-op when AUTHZ disabled. Plumbing for PR (c);
GET /flows/ rewrite is intentionally NOT in this PR.
- BaseAuthorizationService gains invalidate_user / invalidate_role /
invalidate_all hooks (no-op default bodies). Enterprise plugin
overrides these for L1/L2 cache invalidation when roles or shares
change.
OSS LangflowAuthorizationService remains a pass-through; enforcement is
entirely the enterprise plugin's job. Tests use a stub fail-closed
service via monkeypatch to verify guards forward the right tuple, and
source-level checks on flows.py ensure no route silently regresses to
a bare-string action or loses its guard.
47 backend + 12 lfx new/extended tests. Migration regression suite
still green.
* feat(authz): add deployment API guards aligned with Phase 1 flow authz
Wire ensure_deployment_permission on deployment CRUD and run routes using
DeploymentAction, project/workspace domain resolution, owner override, and
audit logging consistent with Eric's flow authorization helpers.
* feat(authz): add `langflow authz dry-run` CLI for policy simulation (#13224)
Adds a self-contained subcommand that simulates every flow-CRUD guard
site under a configurable stub policy and prints what *would* happen —
the Casbin tuple (user, domain, obj, act), the audit row that would be
written, and the resulting HTTP outcome. No network, no DB writes.
Useful for:
- validating an upcoming Casbin policy before shipping it
- showing operators what the audit log will look like
- smoke-testing the guard wiring after a refactor
Built-in policies:
- allow-all (matches the OSS default)
- deny-non-owner (owners + superusers allowed)
- deny-writes (read/execute allowed; write/create/delete denied)
- owner-only (only the flow owner; no superuser bypass)
Usage:
langflow authz dry-run
langflow authz dry-run --policy deny-writes
langflow authz dry-run --policy deny-non-owner --json --output report.json
Implementation: monkey-patches the three helpers in
langflow.services.authorization.utils (get_settings_service,
get_authorization_service, audit_decision) for the duration of the run,
walks _FLOW_GUARDS x three actors (owner / non-owner / superuser), and
renders a Rich table or JSON document with per-row decision + summary
footer.
Tests: 18 cases covering each stub policy, the runner output shape, JSON
serialisation, and CLI integration via typer.testing.CliRunner.
* fix(authz): keep user-derived is_superuser authoritative in ensure_permission
Caller-supplied context was merged after `_auth_context(user)`, which let a
caller forge `context={"is_superuser": True}` and bypass intent of the guard.
Flip the merge order so user-derived auth fields always win, and add a
regression test that asserts a malicious caller cannot override the flag.
* fix(authz): per-flow CREATE checks in batch and upload paths
The batch (POST /flows/batch/) and upload (POST /flows/upload/) handlers were
calling ensure_flow_permission once at the route boundary without per-flow
scope. Because the FlowCreate payload carries caller-supplied workspace_id and
folder_id, a single coarse check let payloads target workspaces/folders the
caller has no create permission on — invisible under OSS pass-through but a
real bypass once an enterprise plugin enforces workspace-scoped roles.
Both handlers now iterate flow_list.flows and call ensure_flow_permission with
the flow's own workspace_id and the effective folder_id (the query parameter
wins on upload, matching _upsert_flow_list's actual write behavior).
Adds two AST-level regression tests that assert each route calls
ensure_flow_permission inside a `for ... in flow_list.flows` loop with the
flow's destination kwargs.
* fix(authz): authorize destination scope when PATCH/PUT moves a flow
update_flow (PATCH) and upsert_flow's update branch (PUT) only checked WRITE
permission against the existing flow's workspace_id/folder_id. Because
_patch_flow and _update_existing_flow apply payload workspace_id/folder_id via
model_dump(exclude_unset=True, exclude_none=True), a caller authorized to
write a flow in scope A could move it into scope B without ever being checked
against B — invisible under OSS pass-through but a real bypass once an
enterprise plugin enforces workspace/share-scoped roles.
Both handlers now compute the effective destination (payload wins when
non-None, else existing) and, when it differs from the existing scope, run a
second ensure_flow_permission(WRITE, ...) call against the destination. No
extra DB hits — the audit row already captures move attempts.
Adds two AST-level regression tests asserting each route makes the
destination check using target_workspace_id/target_folder_id kwargs.
* fix(authz): partial unique indexes on authz_role_assignment for NULL domain_id
The original UNIQUE(user_id, role_id, domain_type, domain_id) constraint on
authz_role_assignment allowed duplicate global role assignments because SQL
treats NULL domain_id values as never-equal. Two ("global", NULL) rows for the
same (user, role) pair both passed.
Replaces the broken constraint with two partial unique indexes:
- uq_authz_role_assignment_scoped: unique on (user_id, role_id, domain_type,
domain_id) WHERE domain_id IS NOT NULL — covers workspace/org assignments.
- uq_authz_role_assignment_global: unique on (user_id, role_id, domain_type)
WHERE domain_type='global' AND domain_id IS NULL — covers global assignments
without relying on NULL equality.
Postgres and SQLite both support partial unique indexes via WHERE clauses.
New Alembic migration (Phase: EXPAND) drops the broken constraint and creates
the two indexes, using batch_alter_table so SQLite recreates the table cleanly.
Three new DB-level regression tests in test_authz_models.py:
- test_authz_role_assignment_blocks_duplicate_global
- test_authz_role_assignment_blocks_duplicate_scoped
- test_authz_role_assignment_allows_distinct_workspaces
* test(lfx): remove redundant @pytest.mark.asyncio decorators
src/lfx/pyproject.toml has asyncio_mode = "auto", so pytest-asyncio
auto-detects async test functions. The decorators on the seven async tests in
test_default_authorization_service.py are no-ops. Removed for clarity; tests
continue to pass via auto-detect (mode=Mode.AUTO confirmed in pytest output).
import pytest stays for the @pytest.fixture decorator on `service`.
* test(authz): complete DeploymentAction enum coverage in test_actions
test_deployment_action_values_match_casbin_strings only asserted READ, CREATE,
EXECUTE — WRITE and DELETE were missing despite being members of the enum.
Mirrored the FlowAction test structure: full value coverage plus a new
test_deployment_action_is_iterable_and_complete that enumerates members.
DeploymentAction has no DEPLOY (deploy is a flow-level action that mints a
deployment), so the expected set is five values.
* fix(authz): partial unique indexes on authz_share for NULL target_id
The original UNIQUE(resource_type, resource_id, scope, target_id) constraint
on authz_share did not prevent duplicate PRIVATE/PUBLIC shares because
target_id is NULL for those scopes and SQL treats NULL values as never-equal.
Replaces the constraint with two partial unique indexes:
- uq_authz_share_targeted: unique on (resource_type, resource_id, scope,
target_id) WHERE target_id IS NOT NULL — covers TEAM/USER scopes.
- uq_authz_share_untargeted: unique on (resource_type, resource_id, scope)
WHERE target_id IS NULL — covers PRIVATE/PUBLIC scopes without relying on
NULL equality.
Chose partial indexes over CodeRabbit's sentinel-UUID suggestion to keep
target_id genuinely nullable (sentinel values leak into every read path that
filters by target_id, and any future FK on target_id would break).
Consistent with the d9e8f7a6b5c4 fix for authz_role_assignment.
New Alembic migration (Phase: EXPAND, validator: ✅) drops the broken
constraint via batch_alter_table and creates both partial indexes.
Three new DB-level regression tests in test_authz_models.py:
- test_authz_share_blocks_duplicate_targeted (USER scope dup → IntegrityError)
- test_authz_share_blocks_duplicate_untargeted (PUBLIC dup → IntegrityError)
- test_authz_share_allows_distinct_targets (sanity: different users OK)
* fix(authz): prefer project over workspace in _resolve_flow_domain
Casbin g2 inheritance is directional: a child resource inherits from its
parent, not the other way round. Declaring `g2, project:xyz, workspace:abc`
lets a workspace-scoped grant flow down to checks made against project:xyz,
but a project-scoped grant does NOT flow up to checks made against
workspace:abc.
The original precedence picked workspace whenever it was set, which made
project-scoped grants invisible to the enforcer the moment a workspace was
also known. Flip the precedence so the more specific domain (project) wins —
project-scoped grants now match directly, and workspace-scoped grants still
match via g2 inheritance. Both ids stay in the enforce context for ABAC.
- _resolve_flow_domain returns project first, then workspace, then "*".
- Docstrings on both _resolve_flow_domain and its callers updated.
- test_ensure_flow_permission_workspace_beats_project renamed to
test_ensure_flow_permission_project_beats_workspace with inverted assertion
and an explanation of g2 directionality.
- test_resolve_flow_domain_precedence updated accordingly.
Deployment helper uses the same function but only one deployment test passes
both ids; deployment behavior is unchanged in tests that pass project_id only.
* feat(authz): Phase 2 — list filtering, execute guards, project routes (#13255)
* feat(authz): Phase 2 — list filtering, execute guards, project routes
Extends the Phase 1 authorization foundations (#13153) with the route coverage
the IBM RBAC design note flags as "Not hooked yet" under §4.1 and the Phase 2
deliverables under §5.3 (workspace/project domain, list/filter APIs).
List filtering
- GET /flows/ now calls filter_visible_resources(FlowAction.READ) on the loaded
list. OSS pass-through returns the input unchanged; the enterprise plugin
uses batch_enforce to honor role + share grants.
- GET /projects/ applies the same filter against ProjectAction.READ.
Execute guards (FlowAction.EXECUTE)
- POST /build/{flow_id}/flow (chat.py): authorizes after loading the flow row.
- POST /run/{flow_id_or_name} (endpoints.py, api-key auth).
- POST /run/session/{flow_id_or_name} (endpoints.py, session auth).
- POST /webhook/{flow_id_or_name} (endpoints.py, webhook auth).
Project (folder) authorization
- New ProjectAction enum (READ/WRITE/CREATE/DELETE).
- New ensure_project_permission helper with workspace domain + owner override,
mirroring the flow/deployment helpers.
- Guards wired into create_project, read_project, update_project,
delete_project, download_file, upload_file.
Tests
- 7 new ensure_project_permission tests in test_utils.py (enum coercion,
workspace domain, owner override, wildcard create object).
- 4 new test_execute_surfaces_guard_with_flow_execute parametrized cases.
- 6 new test_project_routes_guarded parametrized cases.
- test_read_flows_list_unchanged_in_this_pr renamed to
test_read_flows_list_uses_filter_visible_resources; it now asserts the
filter is called exactly once.
Docs
- Added an "Authorization (RBAC)" section to AGENTS.md describing the plugin
model, route helpers, Casbin request shape, and the dry-run CLI.
Out of scope for Phase 2 (per design note §5.3):
- Knowledge bases and global variables (Phase 4: deployments, components,
secrets, audit UI).
- Deployment list filtering (paginated + shaped items; needs SQL-level
prefiltering once authz_share lands in Phase 3).
- authz_share CRUD APIs and UI (Phase 3 — required for full RBAC).
* docs(authz): correct domain precedence note in AGENTS.md
Follows the upstream _resolve_flow_domain fix on the parent branch — project
wins over workspace so project-scoped grants match directly and workspace
grants still flow down via Casbin g2.
* fix(authz): align dry-run CLI with project-over-workspace domain precedence
The _resolve_flow_domain flip (project wins over workspace,
|
|||
| 2d375b2784 | Merge release-1.9.4 into release-1.10.0 | |||
| e248f4f854 |
fix(api-keys): add expiration date support for API keys (#13330)
* Added is_active check to API Keys, expiresAt field that tracks API Key expiry, UI elements to surface this to the Langflow user. * [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> |
|||
| 8e5dd71c0f |
fix(tracing): isolate Langfuse OTel tracer to stop HTTP span pollution (#13344)
* fix(tracing): isolate Langfuse OTel tracer to stop HTTP span pollution Pass an explicit `TracerProvider` to `Langfuse(...)` so the SDK does not register itself as the global OpenTelemetry tracer provider. Without this, `FastAPIInstrumentor.instrument_app` (which uses the global provider) exports every HTTP request span to Langfuse, flooding traces with health checks, flow list calls, and other unrelated routes. Adds regression tests verifying: - the shared Langfuse client is constructed with an explicit `tracer_provider`, and - initializing the client does not swap out the process-wide OTel `TracerProvider`. Fixes #13319 * [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> |
|||
| 6901cf68e9 |
fix(mcp): fix validation, session isolation, and update_flow_from_spec parity (#12528)
* fix(mcp): fix validation, session isolation, and update_flow_from_spec parity
- create_flow_from_spec now calls validate_flow (polls for completion)
instead of fire-and-forget build_flow
- validate_flow filters poll results by job_id to avoid stale builds
- update_flow_from_spec uses server-side tools (configure_component,
connect_components) for feature parity with create_flow_from_spec,
with rollback on failure
- login() only closes the current session's client and registry,
no longer disrupts other concurrent SSE sessions
- _set_client invalidates _shared_registry when the client changes
to prevent stale registries after server switch
- validate_flow returns consistent "errors" list format
* test(mcp): add unit tests for validation, session isolation, and rollback fixes
* test(mcp): remove obsolete validate_flow job_id polling tests
The polling-by-job_id approach was superseded by streaming validation
in release-1.9.0 (event_delivery=direct). Remove the 4 tests that
exercised the removed polling code path.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* fix(mcp): coerce primitive config values to declared field types
create_flow_from_spec parses YAML config blocks, so numeric / boolean
inputs naturally land as int / float / bool primitives. The flat
configure_component path wrote those into the template as-is and the
downstream build then choked: e.g. ChatInput.input_value is a str-typed
MultilineInput, but YAML 'A.input_value: 42' delivered int 42 and the
Message constructor refused ("The message does not have the required
fields (text, sender, sender_name).") -> RuntimeError 'Flow validation
failed: Build error' rolled the whole spec back.
Adds _coerce_param_value that reads each field's declared 'type' from
the template and coerces str/int/float/bool primitives accordingly.
Containers, code, and unknown shapes pass through unchanged so existing
non-primitive call sites are unaffected. None passes through too --
clearing a field is a valid intent.
bool->str converts to lowercase 'true'/'false' to match the YAML spec
convention (str(True) would give 'True'/'False' capitalized).
Test test_create_flow_from_spec_coerces_numeric_config now passes; the
assertion was tightened to compare against the coerced string form
"42" since the coercion target for input_value is str.
Pre-existing test_create_flow_from_spec_with_tool_mode failure (the
Agent component requires a model + API key to build) is unrelated to
this change -- it fails the same way against the parent commit and is
left for a separate fix.
* [autofix.ci] apply automated fixes
* test(mcp): skip build validation in tool_mode wiring test
Agent requires a model + API key to build, but this test only verifies
that URLComponent.component_as_tool -> Agent.tools wiring round-trips
through the spec parser. Pass validate=False so the test no longer
depends on a buildable graph.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
|
|||
| aea4dd05be |
fix(server): refuse to start with multiple workers and the default in-memory job queue (#13202)
* fix(server): refuse to start with multiple workers and the default in-memory job queue The default JobQueueService keeps build queues in process-local memory. With workers > 1, Gunicorn round-robins POST /api/v1/build/<flow>/flow and the follow-up GET /api/v1/build/<job>/events across workers, so the polling worker finds no queue and returns "Job queue not found for job_id" roughly 45-66% of the time under load. Refuse to start in that config rather than letting operators discover it in production. The RuntimeError lists the two operator-actionable workarounds (configure a Redis-backed shared queue, or run --workers 1) and notes that event_delivery=direct also works since the POST endpoint streams events back inline on the same worker — but the server cannot enforce that at startup because it does not know which delivery mode every future client will pick. Only triggered on the Gunicorn (Linux) path; the direct-uvicorn path on Windows and macOS already clamps workers to 1. * fix(server): skip the multi-worker refusal when LANGFLOW_JOB_QUEUE_TYPE=redis Addresses review feedback on PR #13202: the function listed the redis queue as one of the operator-actionable workarounds but still refused to start even when LANGFLOW_JOB_QUEUE_TYPE=redis was configured, so the workaround was undocumented broken. ensure_multi_worker_safe now reads settings.job_queue_type and returns silently when redis is configured. Redis-backed queues share state across workers and support every event_delivery mode, so the race the check guards against cannot occur. Add a positive test covering the redis case; switch the existing refusal tests to patch the settings service explicitly so they are deterministic regardless of the surrounding environment. |
|||
| 61baba9499 |
fix(traces): bypass impl result_processor in _LegacyCaseEnum so legacy uppercase rows load (#13346)
* fix(traces): bypass impl result_processor in _LegacyCaseEnum to load legacy uppercase rows
The previous attempt at normalising legacy uppercase enum values failed
because ``TypeDecorator.result_processor`` chains the impl's
``result_processor`` *before* ``process_result_value``:
impl (SQLEnum) -> process_result_value
SQLEnum's processor calls ``_object_value_for_elem`` which raises
``LookupError`` for any raw DB string not in the lowercase values list.
Pre-v1.9.2 trace/span rows persist the enum *names* (``'OK'``,
``'ERROR'``, ``'CHAIN'``...), so the impl's processor blew up before
``_LegacyCaseEnum.process_result_value`` had a chance to normalise the
value. The traces panel hit this on every read, returning 500 from the
backend with ``LookupError: 'OK' is not among the defined enum values``.
Override ``result_processor`` to skip the impl's processor entirely and
run only ``process_result_value`` against the raw DB string. Old rows
(uppercase names) and new rows (lowercase values) both round-trip
cleanly.
Adds an end-to-end ORM regression test that seeds a row through the
ORM, rewrites the enum column to the legacy uppercase form via raw SQL,
and asserts the ORM SELECT decodes it back to the right enum member.
The existing ``TestLegacyCaseEnumResultProcessor`` tests called
``process_result_value`` directly and missed this bug — the new tests
exercise the full SQLAlchemy result_processor pipeline.
Fixes #13318
* [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>
|
|||
| 27ba63c1d7 |
fix: set graph inputs by component type (#13343)
* fix: set graph inputs by component type Fixes #10796 Co-authored-by: Yuri Chukhlib <yuri.v.chu@gmail.com> * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) --------- Co-authored-by: Yuri Chukhlib <yuri.v.chu@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |
|||
| b95d309c46 |
fix(tracing): surface Langfuse setup failures, pin pydantic>=2.13 (Py3.14) (#13341)
* fix(tracing): surface Langfuse setup failures and pin pydantic>=2.13 for Py3.14 Docker v1.9.3 silently dropped Langfuse traces because the Docker image was bumped to Python 3.14 while the lockfile still resolved pydantic 2.12.x. Langfuse v3 imports `pydantic.v1.BaseModel`, which only gained Python 3.14 support in pydantic 2.13. On the user's Docker container, `from langfuse import Langfuse` raised `pydantic.v1.errors.ConfigError`, the broad `except Exception` in `_setup_langfuse` logged at debug level, and the tracer initialized with `_ready = False` — no error in default logs, no traces in Langfuse. PyPI installs worked because users tend to run Python 3.10-3.13 where pydantic.v1 is still happy. Two changes: - Replace `logger.debug` with `logger.warning`/`logger.exception` in `_setup_langfuse` so future failures (network, auth, dependency conflicts) surface in logs by default instead of vanishing silently. - Add `pydantic>=2.13.0` to the `langfuse` extra in `src/backend/base/pyproject.toml` so the Python 3.14 import path is guaranteed to work whenever the extra is installed, regardless of what other deps resolve transitively. Adds regression tests asserting `_setup_langfuse` calls `logger.warning`/`logger.exception` on the three failure modes (auth check returning False, auth check raising, post-auth setup exception). Fixes #13317 * [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> |
|||
| e24b265482 |
fix(frontend): Safari Selected Components (#13283)
* refactor(frontend): rename useShiftDragSelectFix → useCanvasDragSelectFix * add testcases * ruff changes --------- Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> Co-authored-by: Viktor Avelino <64113566+viktoravelino@users.noreply.github.com> Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local> |
|||
| 1e138141e0 |
feat: LE-1017 Event bus for bundle events (#13253)
* Adding lfx bundle lifecycle events. Routed the useExtensionService to poll for bundle extension updates. Added reload event deduplication to bundles. Make bundle events user scoped. Aligned bundle schemas between BE and FE. * [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> |
|||
| 06666736ca |
feat: add DB migration validation workflow (LE-1259) [backport 1.9.4] (#13348)
* feat: Add DB migration validation workflow for nightly builds (LE-1259) - Implements automated DB migration testing for nightly builds - Tests two scenarios: pip/venv and Docker Compose migrations - Validates migration from stable to nightly versions - Verifies data persistence (witness flows) across migrations - Integrated into nightly_build.yml workflow - Includes Slack notifications for migration test results This addresses the critical blocker identified by QA team for ensuring safe database migrations in production deployments. * docs: Add DB migration validation documentation - Comprehensive guide for LE-1259 implementation - Detailed test scenarios and execution details - Environment configuration and success criteria - Monitoring and troubleshooting guidelines - Placed in docs/docs/Deployment/ for easy access * fix: address PR review comments for DB migration validation - Use actual nightly tag from create-nightly-tag output instead of hardcoded :latest - Wire POSTGRES_VERSION env var into postgres service image tag - Remove unnecessary checkout steps from both migration jobs - Update step name from 'Create witness flow and credentials' to 'Create witness flow' - Add -f flag to curl commands for fail-fast behavior - Add flow creation verification with error handling - Fix version extraction logic to properly test nightly build instead of PyPI latest - Remove deprecated docker-compose version field - Remove duplicate Slack notification job (consolidated in nightly_build.yml) Addresses all 9 issues identified by @ogabrielluiz in PR review * docs: remove implementation summary from user-facing docs Per @ogabrielluiz review feedback, this file reads as an implementation summary (Jira ticket, branch name, 'Next Steps', 'Files Changed: 2') rather than user-facing documentation. The Deployment section is for end-user docs, and this content is better suited for the PR description. Also not added to sidebars.js, so would be an orphan page. * fix(workflows): address 6 issues from Gabriel's second review of DB migration validation Fixes all remaining issues identified in PR #13249 review: 1. Remove schedule trigger - only works on default branch, would cause duplicate runs 2. Fix postgres service image - hardcode to postgres:16 (env context not available in services) 3. Add curl fail-fast flags - use -fsSL for immediate failure on errors 4. Add flow ID verification - check witness data creation succeeded before proceeding 5. Remove orphaned Slack JSON - cleanup leftover from removed notify-results job 6. Fix version extraction - strip 'v' prefix for pip install (${VERSION#v}) 7. Fix Docker image reference in nightly_build.yml - pass full image path with tag 8. Fix success notification - check migration validation didn't fail All changes validated locally: - YAML syntax validation passed - Docker Compose config validated - Tag manipulation logic tested (v prefix handling) - Curl command structure verified - Test credentials marked with pragma allowlist secret comments Related: LE-1259, PR #13249 Depends on: PR #13212 (Docker volume permissions fix) |
|||
| 87e9c908c6 |
fix(playground): add gap between consecutive error cards in chat messages (#13325)
* fix(playground): add gap between consecutive error cards in chat messages
When a chat message carries more than one error ContentBlock (e.g. a
component or future agent emits multiple step-level errors per run),
ErrorView used to render the cards as sibling <div>s with no parent
flex / spacing utility, so they butted edge-to-edge and read as one
merged block.
Wrap the blocks.map output in `<div class="flex flex-col gap-2">` so
each card keeps an 8px breathing space. Matches the inter-card rhythm
used by IngestionRuns / IngestionHistory row stacks elsewhere in the
app. Single-block case is unchanged (gap utility no-ops with one
child).
Drive-bys (lines biome would re-flag because the file is touched):
- Remove dead Markdown / remarkGfm imports and the unused
``errorMarkdownComponents`` constant (truly unreferenced).
- Remove the dead ``CodeTabsComponent`` import and the unused
``ComponentPropsWithoutRef`` type import.
Adds ``__tests__/error-message.test.tsx`` covering: single block in
the gap-aware stack, three blocks each rendered as a direct child of
the stack (regression guard against wrapping that would defeat
``gap-2``), and loading state path that should not render the stack.
Fixtures include the required ``allow_markdown`` + ``component`` fields
on ``ContentBlock`` so ``tsc --noEmit`` stays clean. Locally re-mocks
``genericIconComponent`` to expose the ``ForwardedIconComponent`` named
export the SUT imports.
* test(playground): drop className assertions from error-message tests
Address review feedback on PR #13325:
- viktoravelino: className-string expectations couple the test to
Tailwind utilities that can change anytime and are not behavioural.
Replace ``expect(stack.className).toContain("gap-2"|"flex"|...)``
with the structural assertion that each error card is a *direct
child* of the stack — that is the actual behavioural guarantee
that lets any spacing rule (gap utility today, CSS module
tomorrow) apply. A future regression wrapping cards in an extra
div still trips this test without us encoding class strings.
- CodeRabbit: remove the dead CodeTabsComponent mock; the import
was removed from error-message.tsx so the mock had no consumer.
Describe rename: "multi-card spacing" -> "multi-card structure" to
match the new framing. 3/3 tests still green.
|
|||
| 8452d41642 |
feat(memory): align Create Memory modal layout and spacing with Create Variable modal design system (#13287)
* feat(memory): align Create Memory modal layout and spacing with Create Variable modal design system * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * fix(memory): fix spacing, tag inconsistencies, required field markers, and scrollbar position in Create Memory modal * fix(memory): fix spacing, tag inconsistencies, required field markers, and scrollbar position in Create Memory modal --------- Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local> |
|||
| 75b3cc9f6d | chore: upgrade uv.lock | |||
| b531f559d6 |
chore: port pre-create LANGFLOW_CONFIG_DIR #13212
backport of https://github.com/langflow-ai/langflow/pull/13212 |
|||
| c3ae41e6c7 |
fix(playground): hide session-row checkbox until hover or selection (#13314)
The selectable-row checkbox was always rendered at full opacity, adding
visual noise to every session in the sidebar. Hide it by default and
reveal it only when the row is hovered, while keeping checked boxes
permanently visible so users can still see what they have selected
without re-hovering each row.
Implementation:
- Apply ``invisible group-hover:visible`` to the icon when the row is
not selected; the row already carries the ``group`` class, so the
utility resolves without further plumbing.
- Skip those classes when ``isSelected`` so a checked box stays
visible regardless of pointer location.
- Keep the wrapping ``w-4 h-4`` column so the row layout does not
jump on hover/unhover. ``visibility: hidden`` also disables pointer
events so a stray click on the hidden column cannot toggle
selection.
- Add ``transition-opacity`` for a smooth reveal.
The Select All checkbox at the top of the multi-select region remains
always visible — it is the entry-point affordance for multi-select.
Adds ``__tests__/session-selector-checkbox-visibility.test.tsx`` (5
cases): hidden default + hover utility wiring, always-visible-on-
selected, layout-jump guard via reserved column, click toggles
selection without bubbling to row toggleVisibility, and showCheckbox
false renders nothing.
|
|||
| 87039b46d3 |
fix: port chroma collection hardening (#13339)
* fix: port chroma collection hardening * [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> |
|||
| 01cd7dc88f | chore: remove dup doc line | |||
| 26736d6d7d | fix: align diskcache removal port | |||
| 0a5f25f32b |
chore: port Remove the diskcache dependency
port of https://github.com/langflow-ai/langflow/pull/12953 |
|||
| 22b17d1640 |
feat(memories): add in-product docs links and tooltips to Memory Base UI (#13265)
* feat(memory): improve memory UI with empty states, toasts, and model filtering * [autofix.ci] apply automated fixes * fix: capture memory id/name before setTimeout to prevent stale closure null access * error improvement and testcase addition * fix: surface refresh errors, restore API error messages, flatten sidebar ternary, and add keyboard access to batch-size tooltip * [autofix.ci] apply automated fixes * biome fix * fix types * [autofix.ci] apply automated fixes * fix: UI polish for Memories and Traces sidebar sections * [autofix.ci] apply automated fixes * feat(memories): surface memory config in summary card with popover details * fix: replace magic index, template literals, and inline noops in Memories/Traces sidebar * [autofix.ci] apply automated fixes * ruff fix * fix testcases * improve testcase cov import clean up * [autofix.ci] apply automated fixes * fix import order * accessibility fix * [autofix.ci] apply automated fixes * feat(memories): make preprocessing instructions required with tooltip hint * ruff fix * [autofix.ci] apply automated fixes * feat(memories): redesign MemoryDetailsHeader UI and update tests * [autofix.ci] apply automated fixes * feat(memories): add in-product docs links and tooltips to Memory Base UI * fix text * [autofix.ci] apply automated fixes --------- Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local> |
|||
| e192f91eeb | fix: ChatBedrock playwright failures | |||
| 23f3453a65 | [autofix.ci] apply automated fixes | |||
| 7ac198ac37 | chore: update opik, perplexity , weaviate | |||
| 00fe8c001c | chore: dup langchain-qdrant to langchain-mongodb | |||
| a752275fbd | chore: add to deps and loose missing test | |||
| a7ae55e5d5 | [autofix.ci] apply automated fixes | |||
| 9224534f70 |
chore: port over mem0ai changes from 1.10.0
https://github.com/langflow-ai/langflow/pull/13292 |
|||
| cb6d09fc5d | chore: update pkg in test_all_modules_importable | |||
| 1bae798321 | [autofix.ci] apply automated fixes | |||
| d5bdc6175f | chore: update test_all_modules_importable | |||
| a11ef3113b | [autofix.ci] apply automated fixes | |||
| c2b440cede | chore: update qdrant | |||
| 71567829f5 |
fix: use langchain-mongodb
use langchain-mongodb instead of depricated langchain_community.vectorstores for MongoDBAtlasVectorSearch import |
|||
| a181f1ec6f |
refactor: change agent to use langchain 1.0 (#12992)
* change agent to use langchain 1.0 * templates changes * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * add token usage * fix token usage displaying * remove console debug * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * fix tool call for ibm watsonx models * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * feat(agent): eager bind_tools, info text, history resilience (#13002) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * single tool call, watsonx placeholder and agent middleware improvements * [autofix.ci] apply automated fixes * templates fixes * fix ruff style and checker * fix test agent create * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * gh suggestions * ruff style and checker * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * test: drop manual smoke-run artifacts from test docstrings Per review on #12992: ephemeral test-run identifiers like 'Manual Smoke #5' won't mean anything to a future maintainer. Keep the symptom description, drop the run reference. * fix langchain openai installation on test * ruff style and checker * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Mirror the current-turn guard in _append_input * remove unecessary file commited * gh suggestions * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * fix biome checker * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * fix(altk-agent): preserve legacy AgentExecutor semantics in input overrides ALTK Agent inherits AgentComponent.inputs, but it still runs on AgentExecutor (its run_agent overrides the create_agent path). Without this override, AgentComponent's new create_agent-specific info text on handle_parsing_errors and max_iterations would surface in the ALTK Agent UI, and the verbose toggle (still consumed by ALTK's AgentExecutor call) would silently disappear from the panel. Override the two info strings and re-add the verbose BoolInput so the ALTK Agent panel keeps describing what its runtime actually does. * chore: auto-bake note keys and regenerate backend locales/en.json [skip ci] * [autofix.ci] apply automated fixes * QA fixes * ruff style and checker * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) * ruff fuxes * fix test message * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: ogabrielluiz <gabriel@langflow.org> |
|||
| 1ec8e24d5d |
fix(canvas): remove fixed width on zoom percentage causing excess gap before chevron (#13284)
Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> |
|||
| de780fd3bf |
fix(canvas): replace panel toggle icon with SlidersHorizontal and add pressed/unpressed visual states (#13286)
Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> |
|||
| 0a11614d7f |
fix(playground): align Select All bulk-delete icon with session ⋮ column (#13313)
The bulk-delete trash icon in the Select All row sat off-axis from the
``⋮`` MoreMenu icons of the session rows below: lower (the wrapper had
``py-1 mb-1`` while session rows are ``h-8`` with no vertical padding)
and further left (the trash button was ``p-0`` inside an unpadded
column while the SessionMoreMenu trigger is ``h-8 w-8 p-2``).
Mirror the SessionSelector row shape exactly:
- Outer wrapper: ``flex h-8 items-center justify-between`` — no extra
vertical padding, no bottom margin.
- Trash button: ``size="icon" h-8 w-8 p-2 rounded`` — matches the
SessionMoreMenu trigger inner padding so the icon centers in the
same 32×32 column as ``⋮`` below.
- Inner padding moved to the Select All checkbox cluster (``px-2``)
so the left edge still lines up with the SessionSelector internal
padding.
Drive-bys (touched lines biome would flag otherwise):
- Remove unused ``isDefaultSession`` local in the sessions map.
- Sort the alertStore/flowStore imports.
- Convert the Select All click target from ``<div onClick>`` to a
real ``<button type="button" aria-pressed>`` so keyboard users can
toggle it and the a11y lint warning no longer fires.
Adds ``__tests__/chat-sidebar.test.tsx`` covering: button hidden when
nothing selected, ``h-8 w-8 p-2`` button shape, host row uses ``h-8``
with no ``py-*`` / ``mb-*`` padding, and bulk delete fires with the
expected (default-session-excluded) selection.
|
|||
| f46c8a140c |
feat(memories): redesign MemoryDetailsHeader UI and update tests (#13263)
* feat(memory): improve memory UI with empty states, toasts, and model filtering * [autofix.ci] apply automated fixes * fix: capture memory id/name before setTimeout to prevent stale closure null access * error improvement and testcase addition * fix: surface refresh errors, restore API error messages, flatten sidebar ternary, and add keyboard access to batch-size tooltip * [autofix.ci] apply automated fixes * biome fix * fix types * [autofix.ci] apply automated fixes * fix: UI polish for Memories and Traces sidebar sections * [autofix.ci] apply automated fixes * feat(memories): surface memory config in summary card with popover details * fix: replace magic index, template literals, and inline noops in Memories/Traces sidebar * [autofix.ci] apply automated fixes * ruff fix * fix testcases * improve testcase cov import clean up * [autofix.ci] apply automated fixes * fix import order * accessibility fix * [autofix.ci] apply automated fixes * feat(memories): make preprocessing instructions required with tooltip hint * ruff fix * [autofix.ci] apply automated fixes * feat(memories): redesign MemoryDetailsHeader UI and update tests * [autofix.ci] apply automated fixes * ruff fixes --------- Co-authored-by: Olayinka Adelakun <olayinkaadelakun@mac.war.can.ibm.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local> |
|||
| 2ddac97c77 |
feat(job_queue): cross-worker cancel + polling watchdog for multi-worker Redis-backed builds (#13084)
* feat: implement Redis job queue and fakeredis support
- Refactored the job queue service to support Redis-backed management for cross-worker scaling.
- Added environment variables for configuration:
- `LANGFLOW_JOB_QUEUE_TYPE=redis`
- `LANGFLOW_REDIS_QUEUE_DB=1`
- Updated job ownership methods to be asynchronous for improved concurrency handling.
- Enhanced Redis cache service with namespacing via key prefixes.
- Introduced `fakeredis` for in-memory Redis simulation in testin>
- Added comprehensive unit tests for Redis job queue components.
* fix: run experimental warning for RedisCache usage only once
- Introduced a mechanism to emit a one-time warning for the RedisCache experimental feature during server runtime.
- The warning is logged only if no other worker has already emitted it, ensuring clarity for users regarding the experimental status of RedisCache.
- The implementation includes a temporary file check to prevent multiple warnings across different processes.
* docs: document environment variables for worker management
- Added documentation for LANGFLOW_GUNICORN_PRELOAD to explain preloading for better performance.
- Detailed the use of LANGFLOW_JOB_QUEUE_TYPE for specifying backends (e.g., Redis).
- Included LANGFLOW_REDIS_QUEUE_DB to define the database index for job queues.
- Updated the "High-Load Environments" guide with these optimal configurations.
* docs: updated 'High-load and multi-worker environments' section
* feat: enhance RedisJobQueueService with consumer wrapper management
- Introduced a caching mechanism for Redis stream consumers to optimize job data retrieval.
- Added methods to manage consumer wrappers, ensuring they are reused across sequential polls.
- Implemented cleanup logic to cancel and clear consumer wrappers during job cleanup and service stop.
- Expanded unit tests to verify consumer wrapper reuse and cleanup behavior.
* fix: ensure Redis keys are deleted during job cleanup even on cancellation
- Updated the cleanup_job method in RedisJobQueueService to guarantee Redis keys are removed even if the job cleanup is interrupted by a CancelledError.
- Added a new unit test to verify that Redis keys are deleted correctly when cleanup is called during task cancellation.
* fix: manage connection check task in RedisJobQueueService
- Added handling for the connection check task in the stop method to ensure it is properly cancelled and awaited if still running.
- This change improves resource management and prevents potential issues during service shutdown.
* fix: handle unpublished sentinel requeue on cancellation in RedisJobQueueService
- Updated the job processing logic to ensure that if a job is cancelled during the xadd operation, the unpublished sentinel is requeued instead of being dropped.
- Introduced a new unit test to verify this behavior, ensuring robustness in job handling during cancellations.
* fix: improve Redis job queue service with enhanced configuration and cleanup
- Added atexit cleanup to remove stale temporary files for RedisCache.
- Refactored Redis job queue service to use shared constants for stream prefixes, improving maintainability.
- Updated type hints for better clarity and consistency in RedisQueueWrapper and RedisJobQueueService.
- Enhanced error handling with configurable backoff for transient read failures.
* fix: enhance Redis job queue service with maxlen configuration for xadd
- Updated the xadd method in RedisJobQueueService to include maxlen and approximate parameters, improving stream management and preventing excessive memory usage.
* fix: enhance job ownership retrieval in RedisJobQueueService
- Updated the get_job_owner method to refresh the Redis key TTL on successful lookups, ensuring long-running jobs maintain their ownership anchor.
- Improved code clarity by extracting the owner key into a variable and adding detailed docstring explanations for better understanding of the TTL management.
* fix: improve job ownership handling in cleanup_job method
- Enhanced the cleanup_job method in RedisJobQueueService to accurately capture job ownership before deleting Redis keys, preventing potential data corruption in multi-worker scenarios.
- Added comments for clarity on ownership logic and its implications during job cleanup.
* fix: optimize TTL management in RedisJobQueueService
- Introduced periodic TTL refresh logic in the _bridge_to_redis method to enhance Redis stream management, reducing round-trips and improving throughput.
- Added constants for TTL refresh events and seconds to maintain clarity and configurability.
- Updated event handling to ensure TTL is refreshed appropriately based on event count and time elapsed.
* fix: improve event handling in flow response management
- Removed unnecessary error handling for missing event tasks in get_flow_events_response, allowing for smoother operation when no task exists.
- Updated create_flow_response to handle optional event_task parameter, ensuring proper cleanup during disconnections.
- Added unit tests to verify behavior when event tasks are missing, enhancing robustness in streaming scenarios.
* fix: enhance RedisQueueWrapper with startup grace period and stream observation
- Added a startup grace period to prevent premature end-of-stream signals when the producer has not yet issued its first XADD.
- Introduced a flag to track whether the stream has been observed, improving the handling of early polling scenarios.
- Updated logic to ensure proper handling of stream existence checks and logging for better debugging during job processing.
* fix: implement cleanup for old cross-worker job queues in RedisJobQueueService
- Added a new method to clean up done cross-worker consumer wrappers that are not owned by the current worker, ensuring proper resource management.
- Enhanced the existing cleanup logic to prevent memory leaks by explicitly pruning stale entries from the consumer wrappers dictionary.
- Improved logging to provide better visibility into the cleanup process for cross-worker jobs.
* fix: enhance job handling in JobQueueService with guarded task execution
- Updated the start_job method to accept a Coroutine type for task_coro, ensuring type safety.
- Introduced a new _guarded_task method to wrap job coroutines, guaranteeing that unhandled exceptions emit an error event and write a sentinel to the Redis Stream, improving reliability in job processing.
- Enhanced documentation to clarify the behavior of the new task handling mechanism and its implications for cross-worker consumers.
* fix: improve client disconnection handling in create_flow_response
- Added logging for scenarios where a client disconnects without an associated event_task, clarifying that the producer will continue running until the build completes.
- Documented the limitation regarding cross-worker passive disconnects and the need for a Redis side-channel for proper cancellation, enhancing observability in the logs.
* fix: enhance RedisQueueWrapper to manage initial read state and buffer behavior
- Introduced a flag to track the completion of the first XREAD call, ensuring proper buffer management during the initial read phase.
- Updated the empty method to reflect the state of the buffer accurately, preventing premature exits from the drain loop until the first read is complete.
- Improved documentation to clarify the behavior of the new flag and its impact on job processing.
* fix: clarify RedisQueueWrapper behavior in tests for buffer state and no-op operations
- Updated test documentation to explain the behavior of the empty() method in relation to the first XREAD completion, ensuring accurate understanding of buffer state during job processing.
- Adjusted assertions in tests to reflect the intended behavior of the RedisQueueWrapper, specifically regarding the no-op nature of put_nowait and its impact on the internal buffer.
* fix: update dependencies in pyproject.toml and uv.lock
- Added fakeredis dependency with a minimum version of 2.0.0 to both pyproject.toml and uv.lock files.
- Ensured proper formatting and comments in pyproject.toml for clarity on onnxruntime version constraints.
* feat(job_queue): cross-worker cancel via Redis pub/sub + configurable startup grace
- Add LANGFLOW_REDIS_QUEUE_STARTUP_GRACE_S (default 30.0) so deployments
with slow producer-worker cold-start can bump the wrapper's early-poll
grace period without editing code.
- Add LANGFLOW_REDIS_QUEUE_CANCEL_CHANNEL_ENABLED (default True) wiring a
per-job Redis pub/sub channel (langflow:cancel:<job_id>) so
POST /build/{job_id}/cancel works cross-worker. The producer subscribes
when the job starts; any worker can publish via
RedisJobQueueService.signal_cancel and the subscriber cancels the local
build task on receipt.
- cancel_flow_build now falls back to signal_cancel when event_task is
None on this worker, replacing the previous no-op for cross-worker
cancellation.
- Subscriber tasks are tracked in _cancel_subscribers and torn down in
cleanup_job/stop alongside the bridge tasks.
- Tests: 3 new unit tests covering cross-worker signal propagation, the
disabled-flag no-op, and the wrapper's per-instance startup_grace_s
override.
* fix(job_queue): close three cross-worker cancel corner cases
Discovered by a deeper local stress pass over the prototype:
1. **Signal-before-subscribe race**: publish from worker A would be lost if
worker B's subscriber had not finished SUBSCRIBE yet. signal_cancel now
also sets a short-lived langflow:cancel-marker:<job_id> key, and the
subscriber checks it immediately after subscribing — so a cancel that
raced the SUBSCRIBE still fires.
2. **Restart subscriber leak**: start_job called twice for the same job_id
silently overwrote the dict entry and orphaned the previous subscriber
(and its Redis pubsub connection). Now cancels the previous subscriber
before storing the new one.
3. **Slow end-of-stream after cross-worker cancel**: cancelling the local
task did not unblock cross-worker consumers — the bridge sat waiting on
local_queue.get() until periodic cleanup ran ~5 min later. The
subscriber now puts a sentinel on the local queue after task.cancel()
so the bridge flushes a clean end-of-stream marker to Redis. Measured
8ms from signal_cancel to consumer end-of-stream against real Redis.
Tests: 3 new unit tests covering each corner case; 9-scenario local
stress harness against real Redis confirms all green.
* refactor(job_queue): address self-review of cross-worker cancel
Addresses the eight points from the PR review of the previous commits:
1. Configurable marker TTL — new LANGFLOW_REDIS_QUEUE_CANCEL_MARKER_TTL
setting (default 60s) replaces the hardcoded constant.
2. Connection-pool footprint is O(1) in active jobs — replaced the per-job
pub/sub subscriber with a single PSUBSCRIBE dispatcher per worker
listening on langflow:cancel:*.
3. Prompt stream cleanup after cancel — the dispatcher now waits for the
bridge to drain the sentinel before triggering cleanup_job, so the
Redis stream + owner keys are deleted within milliseconds instead of
waiting for the 5-minute periodic cleanup grace.
4. signal_cancel raises on Redis failure — publish errors propagate to the
caller instead of silently returning 0. The cancel HTTP endpoint
catches and returns False so the client can retry.
5. Auth note in signal_cancel docstring — explicit note that callers must
verify authorization; the HTTP cancel endpoint already does via
_verify_job_ownership before calling through.
6. Structured cancel logging — INFO-level logs on publish, marker hit,
owned dispatch, foreign dispatch. _cancel_stats counters expose
{published, marker_hit, dispatched_owned, dispatched_foreign,
publish_errors} for ops/metrics.
7. redis-py version compatibility — _close_pubsub falls back from
aclose() to close(), handles both sync and async return values.
8. Fire-and-forget tasks hold strong references — _background_tasks set
keeps marker checks and post-cancel cleanups from being GC'd before
they run; each task self-removes via add_done_callback. stop() drains
the set on shutdown.
Tests: 24 passing (1 skipped due to timing); deep stress harness verified
6 scenarios against real Redis.
* feat(job_queue): propagate client disconnect via signal_cancel for cross-worker builds
Closes the remaining cross-worker passive-disconnect gap. Previously, when a
client closed its streaming connection on a non-owner worker, the producer
worker kept emitting events until the build completed naturally. Now the
disconnect handler in create_flow_response publishes a cross-worker cancel
via signal_cancel so the owning worker stops promptly.
- create_flow_response accepts optional queue_service + job_id kwargs and
uses them in on_disconnect for the cross-worker case (event_task is None).
Both are keyword-only and default to None, preserving the single-worker
contract.
- get_flow_events_response wires both through.
- New unit test test_cross_worker_disconnect_publishes_signal_cancel covers
the full pubsub propagation path with two services sharing a fake Redis.
- Tested end-to-end against real Redis with a two-worker harness: 10/10
scenarios pass, including the new disconnect propagation case.
* feat(job_queue): production-harden cross-worker cancel for multi-worker setups
Five complementary improvements that make the Redis-backed cancel/lifecycle
path safe to leave running unattended under real ops pressure.
A. Dispatcher reconnect loop with exponential backoff. _run_cancel_dispatcher
used to exit silently on any Redis-side error (broker restart, network
blip, listen() hiccup), leaving the worker permanently blind to
cross-worker cancels until process restart. The loop now reconnects with
capped backoff (max 30s), and a dispatcher_reconnects counter exposes the
event for monitoring.
B. Outer timeout on _post_cancel_cleanup. The background cleanup task could
hang indefinitely if cleanup_job stalled (Redis pathology during DELETE);
wrap in asyncio.wait_for and let periodic cleanup retry instead.
C. Public metrics_snapshot() on JobQueueService (memory + Redis backends)
plus GET /monitor/job_queue endpoint behind get_current_active_user.
Surfaces backend, active_jobs, bridge_count, consumer_wrapper_count,
background_task_count, cancel_dispatcher_running, and the full
cancel_stats counter set.
D. Deterministic rewrite of test_redis_service_signal_cancel_flushes_sentinel_to_consumer.
The previous version was skipped roughly half the time due to a fakeredis
timing race; the new version no-ops cleanup_job for the duration of the
assertion so the bridge XADD ordering can be observed reliably. No more
skipped tests in the suite (33 pass, 0 skip).
E. Polling-mode stale-client watchdog. Polling has no persistent connection
for on_disconnect, so abandoned polling builds previously ran to natural
completion even after the client gave up. New flow:
* touch_activity(job_id) writes langflow:activity:<job_id> on every poll
and on streaming-response open; consume_and_yield refreshes it every
10s while the connection is held.
* _run_polling_watchdog scans owned jobs every
LANGFLOW_REDIS_QUEUE_POLLING_WATCHDOG_INTERVAL_S (default 15s) and
publishes signal_cancel for jobs whose activity is older than
LANGFLOW_REDIS_QUEUE_POLLING_STALE_THRESHOLD_S (default 90s).
* Streaming clients are protected by the heartbeat refresh; threshold <=
0 disables the watchdog entirely.
* cleanup_job folds the activity key into the existing DEL round-trip
so successful builds clean up after themselves.
End-to-end coverage: 33 unit tests pass (up from 25, with the previous skip
now passing deterministically); a two-worker real-Redis harness exercises 12
scenarios including dispatcher reconnect, post-cancel timeout, watchdog
reclamation, and metrics_snapshot schema completeness.
* fix(job_queue): address PR review + Codex adversarial review findings
Acts on every must-fix finding from a multi-agent review pass (code-reviewer,
pr-test-analyzer, silent-failure-hunter, comment-analyzer) plus the Codex
adversarial review. No behavior is left to chance under realistic operational
conditions.
Critical fixes:
- Streaming heartbeat now runs as an INDEPENDENT task for the lifetime of the
response, not coupled to event yield cadence. A quiet build (long graph
step, slow LLM, no tokens for a while) previously had no heartbeat path, so
the polling watchdog could reclaim a live streaming client after the
threshold. The new heartbeat fires every streaming_activity_refresh_s
regardless of queue activity, and is cancelled cleanly on both
on_disconnect AND natural stream end.
- Watchdog start-grace window. A new in-memory _job_start_times[job_id]
timestamp is set in start_job() BEFORE super().start_job() so a job can
never appear in self._queues without a corresponding start time. When the
watchdog scans and the Redis activity key is missing, it skips the kill
until (now - start_ts) >= threshold. Without this, a slow background
touch_activity (event-loop pressure, Redis blip) could nuke a brand-new
build on the very first watchdog tick — the prior code's comment promised
the guard but didn't implement it.
- touch_activity errors are now observable. Replaces a blanket
contextlib.suppress(Exception) with an explicit try/except that bumps
activity_touch_errors and emits a debug log. CancelledError is no longer
swallowed. Operators get a signal when the heartbeat is silently failing.
- Watchdog UnboundLocalError closed. Initializes last = 0.0 before the
raw-is-None branch so a malformed activity value (parse failure) can't
crash the entire watchdog task. Adds activity_parse_errors and
activity_get_errors counters.
- Dispatcher except is split: ConnectionError/TimeoutError/OSError stay at
awarning (transient Redis), every other Exception goes to aerror with
exc_info=True AND increments dispatcher_internal_errors. Code bugs in
_handle_cancel no longer look identical to Redis disconnects.
- _post_cancel_cleanup narrows the bridge-wait suppress to TimeoutError
only, with a dedicated awarning naming the consequence ("cross-worker
consumers may see late end-of-stream"). Real bridge failures bubble up
via a separate Exception arm with awarning instead of being silent.
- Polling watchdog uses local _handle_cancel for owned jobs instead of
round-tripping through pubsub. Faster (no Redis RTT), dispatcher-
independent (works during a reconnect backoff), and keeps the
cancel_stats["published"] counter honest as a count of *external* cancels.
- /monitor/job_queue gated to get_current_active_superuser instead of
get_current_active_user — the snapshot exposes process-wide tenant
workload + cancel activity, which matters in multi-tenant deployments.
Docstring corrections:
- Removed the "cross-worker cancel is a known limitation" remnant from
RedisJobQueueService.get_queue_data — the limitation was closed in the
prior commit, and the new docstring tells callers exactly how cross-worker
cancel travels (signal_cancel → dispatcher).
- touch_activity docstring rewritten. The previous text described a
"TTL-based reasoning" fallback that didn't exist; the new text correctly
explains the 4x-threshold TTL and how it interacts with the watchdog's
start-time grace window.
New tests (5 added, total 38 pass):
- test_polling_watchdog_grants_start_grace_window: brand-new job with
deleted activity key survives until the threshold passes, then dies.
- test_polling_watchdog_skips_malformed_activity_value: malformed value
bumps activity_parse_errors and does NOT kill the job.
- test_streaming_heartbeat_runs_independent_of_event_yield: quiet streaming
build is not reclaimed; heartbeat task is cancelled on disconnect.
- test_concurrent_cancels_from_multiple_workers_are_idempotent: two workers
publish cancel simultaneously, no crashes, single observable cancel.
- test_dispatcher_internal_error_logged_at_error_level: bug in
_handle_cancel bumps dispatcher_internal_errors and dispatcher_reconnects;
the dispatcher task is still running afterwards.
Plus: test_metrics_snapshot_exposes_cancel_stats_and_counters now pins the
full cancel_stats key set (using set equality), so adding an increment
without registering the key — or vice versa — fails the test instead of
producing a silent KeyError in production.
End-to-end coverage: 38 unit tests pass; the two-worker real-Redis harness
passes all 12 scenarios on commit HEAD.
* fix: harden redis job queue cancellation
* feat(job_queue): seamless redis setup + actionable errors for unsupported delivery
- RedisQueueWrapper: restore _BUFFER_MAXSIZE bounded buffer and the
_on_fill_done done-callback safety net that release-1.10.0 added in #12588,
so a slow consumer cannot grow the buffer without bound and a crashing or
cancelled _fill_task cannot leave consumers stuck on await get().
- build.get_flow_events_response: explicit exhaustiveness guard. Unknown
EventDeliveryType values now return HTTP 400 with the supported set and a
remediation hint instead of silently falling through to the polling path.
- lfx settings.set_event_delivery: when workers > 1 without a redis queue,
upgrade the warning to name the requested mode, the forced fallback, and
the LANGFLOW_JOB_QUEUE_TYPE env var that would preserve the original mode.
- Tests: port the three RedisQueueWrapper safety tests from release-1.10.0
and add coverage for the new event_delivery guard.
* fix(job_queue): polling watchdog only watches jobs with a registered owner
Surfaced by locust load testing on a Redis-queue, 2-worker setup:
2016 requests / 2016 polling_watchdog_kills (1:1 ratio)
TaskService.launch_task uses JobQueueService.start_job for server-internal
tasks (telemetry, background work) without calling register_job_owner.
These tasks never refresh the activity heartbeat because no polling
client is involved, so the watchdog's start-time fallback was killing
every single one once it crossed polling_stale_threshold_s.
Under fast-completing flows the user-visible result was unaffected (the
build finished before the watchdog struck) — but a long-running internal
task (slow LLM call, retrieval, etc.) would be cancelled mid-flight even
though no one was waiting on it.
Scope the watchdog to jobs in self._job_owners: user-facing flow builds
register an owner; TaskService tasks don't. After this change the same
locust run reports polling_watchdog_kills=0 and dispatched_owned=0.
Existing watchdog tests register an owner explicitly to mirror the real
flow path. Adds a regression test (TaskService-style start_job with no
owner must not be reclaimed).
* test(job_queue): add coverage for fixes surfaced by load testing
Three regression tests for the recent fixes:
- TaskService integration: launch a task through
TaskService.fire_and_forget_task and assert the polling watchdog
leaves it alone (no owner registered, no kill). Catches a future
regression where someone adds register_job_owner inside TaskService.
- Settings multi-worker warning: verify Settings.set_event_delivery
warns with the requested mode AND names LANGFLOW_JOB_QUEUE_TYPE in
the fallback log when workers > 1 and the job queue is in-memory.
An operator seeing missing events needs that env var name to fix it.
- Bounded buffer backpressure: floods the fill task past the maxsize
ceiling and verifies qsize() never exceeds _BUFFER_MAXSIZE. The
previous test only asserted the constant, not the behavior.
Skipped two suggested tests:
- TestClient version of the unknown-event_delivery guard: FastAPI's
enum validation returns 422 before our handler runs, so HTTP cannot
reach the defensive 400. The existing unit test is the right test.
- Exact watchdog kill-count assertion (== 1): the watchdog can tick
again before cleanup_job removes the job from _queues, making any
exact-count check flaky on slow CI. The existing >= 1 is correct.
* fix(job_queue): use asyncio.create_task for trace cleanup on cancel
background_tasks.add_task() is silently dropped after FastAPI drains
the POST /build response queue — by the time any cancel fires (local,
cross-worker pub/sub, or polling watchdog), the task list from the
original HTTP request is already consumed.
Replace the background_tasks call in _run_vertex_build's CancelledError
handler with asyncio.create_task(graph.end_all_traces_in_context()())
so the trace cleanup runs as an independent task regardless of the
background_tasks lifecycle.
Adds a regression test that exercises generate_flow_events end-to-end:
a blocking vertex is cancelled mid-flight and the test asserts that
end_all_traces is eventually called via the spawned task.
* [autofix.ci] apply automated fixes
* fix: addresses a few pr review findings (#13179)
* fix(job_queue): address PR review findings from cross-worker cancel implementation
- Restore _last_id cursor update to after await buffer.put() in _fill_from_redis;
advancing before the await loses the event if the task is cancelled while put()
is blocked on a full buffer
- Restore XREAD persistent-error grace period: track elapsed error time and deliver
an end-of-stream sentinel after _STARTUP_GRACE_S of continuous failures instead
of looping forever, which left consumers stuck on await get()
- Fix cancel_flow_build returning True when no task and no signal_cancel (in-memory
backend); return False to correctly signal that cancellation did not take effect
- Decouple polling watchdog from cancel_channel_enabled; the watchdog uses only
local state and _handle_cancel with no pub/sub dependency, so disabling the
cancel channel must not silently disable stale-client reclamation
- Restore _BUFFER_MAXSIZE to 1000 (reverts unexplained 10x increase to 10_000)
* test(job_queue): add regression coverage for PR review fixes
- test_fill_from_redis_last_id_not_advanced_before_put_completes: verifies
the XREAD cursor (_last_id) stays at the prior message's position when the
fill task is cancelled while put() is blocked on a full buffer
- test_fill_from_redis_persistent_xread_error_delivers_sentinel: verifies a
persistent XREAD failure eventually delivers the end-of-stream sentinel
after _STARTUP_GRACE_S of continuous errors (not an infinite retry loop)
- test_cancel_flow_build_returns_false_for_in_memory_backend_without_task:
verifies cancel_flow_build returns False when no local task exists and the
queue service has no cross-worker cancel path
- test_polling_watchdog_runs_when_cancel_channel_disabled: verifies the
polling watchdog reclaims stale jobs even when cancel_channel_enabled=False
* format and comment updates
* comments
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(job_queue): address CodeRabbit review findings
- cancel_flow_build: gate signal_cancel on a new cross_worker_cancel_enabled
property so Redis with cancel_channel_enabled=False is treated the same as
the in-memory backend (signal_cancel short-circuits to 0 without setting
the marker, so the previous unconditional return True was misleading).
- _run_vertex_build: hold a strong reference to the trace cleanup task on
CancelledError (Ruff RUF006). Tasks self-discard via add_done_callback.
- RedisQueueWrapper.finish_with_sentinel: stop awaiting on a bounded buffer
during teardown. Evict + put_nowait mirrors _on_fill_done so shutdown can
no longer hang on a slow or abandoned consumer.
- RedisJobQueueService.cleanup_job: wrap the Redis DEL in try/except so
Redis failures during teardown surface as warnings instead of escaping
stop() and explicit cancel paths.
- Settings (lfx): add Field bounds on the new Redis timing knobs so a
negative startup_grace_s or non-positive cancel_marker_ttl /
watchdog_interval_s fails fast at config load instead of silently
reopening races.
- Streaming heartbeat interval: lift to module-level STREAMING_ACTIVITY_REFRESH_S
so the heartbeat test can monkeypatch it.
Tests:
- _make_service tracks shared_client ownership so the first _stop_service
doesn't aclose the FakeRedis under a sibling worker in cross-worker tests.
- test_polling_watchdog_skips_fresh_activity now registers a job owner;
without it the watchdog skips the job entirely and the test would pass
even with a broken touch_activity.
- test_streaming_heartbeat_runs_independent_of_event_yield patches the
interval down and waits for the spawned task to refresh the activity
timestamp, instead of calling touch_activity manually.
- Fix Ruff ARG001, EM101, TRY003, PT018 in test stubs.
* test(job_queue): cover behavior changes from CodeRabbit review fixes
- test_cancel_flow_build_returns_false_for_redis_with_disabled_cancel_channel:
Redis with cancel_channel_enabled=False has signal_cancel but no marker
delivery; cancel_flow_build must report failure, not false success.
- test_finish_with_sentinel_does_not_hang_on_full_buffer: regression cover for
the await-on-bounded-buffer hang. Fills the buffer to capacity and asserts
finish_with_sentinel returns within a tight budget.
- test_redis_service_cleanup_swallows_redis_delete_error: proxies a FakeRedis
whose delete() raises; cleanup_job must absorb it and complete local
teardown.
- test_redis_queue_bounds (lfx): pydantic ValidationError on negative
startup_grace_s / non-positive cancel_marker_ttl /
polling_watchdog_interval_s; zero remains valid for startup_grace_s and
polling_stale_threshold_s (documented disable switch).
* log updates
* [autofix.ci] apply automated fixes
* test(cors): accept both '*' and ['*'] for Python 3.14 compatibility
pydantic-settings parses LANGFLOW_CORS_ORIGINS='*' as the raw string '*'
on Python 3.13 but as ['*'] (a single-element list) on Python 3.14+. The
`str | list[str]` union on Settings.cors_origins resolves differently
between versions, but both shapes carry the same 'all origins' semantic.
Updates two assertions in test_security_cors.py that previously required
the exact string form and were failing on Group 4 / Python 3.14 in CI:
- test_default_cors_settings_current_behavior
- test_wildcard_with_credentials_allowed_current_behavior
The PR itself (cross-worker cancel + polling watchdog) is unrelated to
CORS handling -- the failure is a Python-3.14-introduced incompatibility
in this pre-existing test that happens to surface against this branch's
CI matrix.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: Arek Mateusiak <arek@a4bits.com>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
|