Commit Graph

17907 Commits

Author SHA1 Message Date
c897a23ca9 Update version and project name v1.10.0.dev48 2026-05-27 00:59:12 +00:00
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>
2026-05-26 21:47:39 +00:00
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>
2026-05-26 21:01:40 +00:00
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>
2026-05-26 20:31:44 +00:00
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>
2026-05-26 19:23:30 +00:00
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>
2026-05-26 18:51:44 +00:00
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.
2026-05-26 17:56:13 +00:00
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>
2026-05-26 17:51:10 +00:00
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.
2026-05-26 17:02:00 +00:00
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>
2026-05-26 14:46:41 +00:00
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>
2026-05-25 20:55:01 -03:00
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>
2026-05-25 21:19:44 +00:00
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>
2026-05-25 21:06:30 +00:00
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.
2026-05-25 20:49:44 +00:00
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>
2026-05-25 20:42:43 +00:00
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>
2026-05-25 19:15:05 +00:00
63e1401f92 feat: wxo oauth sso support (#12917)
* expose OAuth variables from wxo

* address ruff errors

* [autofix.ci] apply automated fixes

* Align WXO variable resolution with ADK/TRM runtime contract.

Prefer LANGFLOW_REQUEST_VARIABLES as request-scoped source of truth, keep env fallback behavior, and add tests for request-variable lookup plus WXO bearer alias synthesis.

* [autofix.ci] apply automated fixes

* Unify lfx variable resolution across token aliases.

Resolve token/access_token aliases from request variables, environment variables, and x-langflow-global-var names while keeping bearer-token synthesis compatible for prefixed connection keys.

* [autofix.ci] apply automated fixes

* Fix Ruff violations in variable service tests.

Normalize env-var fixture naming and add targeted lint suppressions for token-like test values and lowercase x-langflow-global-var keys required by runtime lookup semantics.

* Enforce strict variable key matching in lfx variable resolver

* Remove non-WXO bearer alias assertion from variable service test

* [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]

* adhere to TRM variable namings

* clean up comments

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-25 18:59:01 +00:00
2910754e60 fix(playground): distinguish active session row during multi-select (#13312)
When a session was both active (in focus) AND checked in multi-select,
the previous className logic killed the active-state emphasis. Every
checked row shared the same `bg-accent` background, so the active row
was visually indistinguishable from its peers. Screen readers had no
hook either.

Tighten the rule so the active row is the only row that carries a
background and bold weight:

  - Active (any) → `bg-accent font-semibold` + `aria-current="page"`
    + `data-active="true"`.
  - Selected-only → no row background, no font change. The red
    checkbox icon alone signals selection.
  - Neither → `font-normal`, no background.

This keeps the active row unambiguously the highlighted one while
multi-select still reads cleanly via the row's red checkbox state.

Adds focused tests covering all four state combinations
(neither, active-only, active+selected, selected-only).
2026-05-25 18:34:11 +00:00
700dd65a17 fix(playground): auto-focus chat input when active session changes (#13310)
* fix(playground): auto-focus chat input when active session changes

Previously the playground chat textarea stayed unfocused when a new
session was created, when the user switched sessions from the sidebar,
or on initial playground mount, forcing an extra click before typing.

Subscribe ChatInput to ``useSessionManagerStore.activeSessionId`` and
focus the textarea via ``requestAnimationFrame`` whenever that id
changes. rAF defers focus past the AnimatePresence/motion mount
transition so focus is not stolen back. Guarded with ``noInput`` (no
textarea rendered in that branch). Cleanup cancels a pending rAF on
unmount or before the next effect run.

Adds ``__tests__/chat-input.test.tsx`` covering: no-session, initial
mount with session, session change re-fires focus, noInput skip,
unmount-before-rAF cancel, and same-id-no-refire. 6/6 green; wider
playground jest suite 142/142.

* test(playground): replace `any` in chat-input test mocks with typed selectors

Biome flagged six `lint/suspicious/noExplicitAny` errors in the new
chat-input.test.tsx. Replace each store mock's `(selector: any)` with a
generic `Selector<TState, TResult>` parameterised on the mock's
concrete state shape. Replace `motion.div: (props: any)` with
`React.HTMLAttributes<HTMLDivElement>`. Biome + tsc clean; 6/6 still
green.
2026-05-25 18:33:49 +00:00
f1b64b1c6f fix(db): serialize concurrent Alembic migrations via Postgres advisory lock (#13204)
* fix(db): serialize concurrent Alembic migrations via Postgres advisory lock

Workers booting concurrently against a fresh PostgreSQL each call
alembic upgrade("head") independently. PostgreSQL serialises individual
DDL statements via catalog locks, but the lock is released at commit, so
the losing worker's CREATE TYPE / CREATE TABLE runs after the winner
commits and fails with UniqueViolation / DuplicateObject. Documented
reproduction: ``langflow run --workers 3`` against an empty Postgres
deterministically loses migrations on 2 of 3 workers, leaving the
database in a partially initialised state.

Wrap _run_migrations in a Postgres session-level advisory lock so only
one worker mutates the schema at a time; the others block, then find the
schema already at head and exit clean. No-op for non-Postgres URLs.
Mirrors the URL normalisation in check_postgresql_version_sync to handle
async-driver suffixes (postgresql+asyncpg, postgresql+aiosqlite).

Verified: 3 concurrent workers against a fresh Postgres complete with
the fix in place, all succeed, schema at head. Without the fix, a direct
SQL race against the same database confirms the failure mode: 5
concurrent CREATE TYPE statements via psql produce 1 success and 4
"type already exists" errors.

* fix(db): bound the migration advisory lock wait so a hung holder can't block boot indefinitely

Addresses review feedback on PR #13204: pg_advisory_lock blocks forever
with no upper bound and lock_timeout does not apply to advisory locks,
so a worker hung mid-migration would silently freeze every other worker
on startup with no log line explaining the wait.

Replace the blocking acquire with pg_try_advisory_lock in a polling loop:
returns immediately when the lock is free, otherwise emits an info log
"waiting for migration advisory lock held by another worker" and polls
every 2s until either the lock is acquired or the timeout expires (5
minutes by default, override via LANGFLOW_MIGRATION_LOCK_TIMEOUT_S). On
timeout, raise RuntimeError with an actionable message pointing
operators at the stuck holder.

Test coverage: immediate-acquire happy path, wait-then-acquire when
another worker holds, timeout when the holder never releases (engine
still disposed, no spurious unlock), and the existing release-on-block-
exception path.
2026-05-25 13:28:58 +00:00
1fe9b39243 feat(memory): session_id filtering as optional for MemoryBases (#13298)
Modified fetch messages mechanism to support all messages as well in memory bases and session_id filtering to be optional.
2026-05-22 22:41:52 +00:00
dbee5128c0 fix: chromadb trust remote code mitigation (#13288)
* fix: disable chroma server-side embedding functions

* [autofix.ci] apply automated fixes

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

* Update component_index.json

* [autofix.ci] apply automated fixes

* Update component_index.json

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-22 14:50:42 -07:00
6563c7c4be fix: error with loop in grouped components (#13281)
* fix: error with loop in grouped components

* Update utils.py

* [autofix.ci] apply automated fixes

* fix: grouped empty output

---------

Co-authored-by: Antônio Alexandre Borges Lima <104531655+AntonioABLima@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-22 14:46:54 -07:00
0f95576db9 fix(auth): AUTO_LOGIN parity on protected endpoints + model-load error UI (#13192)
* fix(auth): AUTO_LOGIN parity on protected endpoints + model-load error UI

Two related regressions surfaced when upgrading deployments from v1.7.1 to
v1.9.x:

1. ``GET /api/v1/flows/`` (and every other ``CurrentActiveUser``-protected
   endpoint) returns 403 to AUTO_LOGIN clients that don't pass an API key
   or JWT, even when ``LANGFLOW_SKIP_AUTH_AUTO_LOGIN=true`` is set.
   ``_api_key_security_impl`` honors that flag, but ``get_current_user``
   (which most endpoints depend on) goes through
   ``authenticate_with_credentials`` and unconditionally raises
   ``MissingCredentialsError`` — so the opt-in escape hatch is effectively
   broken for the majority of the API.

2. The Language Model component on the canvas can get stuck on
   "Loading models…" forever on a fresh install. The axios response
   interceptor swallows 401/403 errors under AUTO_LOGIN without rejecting
   the promise, so React Query never sees a clean error or completion,
   and the dropdown has no error UI to surface what happened.

Backend fix: ``authenticate_with_credentials`` now mirrors
``_api_key_security_impl`` — when ``AUTO_LOGIN`` is enabled and the
operator opts in via ``skip_auth_auto_login``, fall back to the
configured superuser instead of rejecting. Default behavior is
unchanged; the security tightening from #8513 stays in place.

Frontend fixes:
- Response interceptor always rejects on auth errors so callers and
  React Query can react to the failure rather than reading ``.data``
  off an undefined response.
- ``ModelInputComponent`` reads ``error``/``refetch`` from both model
  queries and renders a click-to-retry button when both have failed
  and no refetch is in flight, instead of showing the loading spinner
  forever. Adds the ``modelInput.loadFailed`` locale string.

Tests:
- 4 new pytest cases covering the AUTO_LOGIN parity matrix in
  ``test_auth_service.py``.
- 3 new jest cases in ``ModelInputComponent.test.tsx`` covering the
  error state, retry click, and in-flight-refetch fallback.

* fix: address review feedback on auth + model-load fixes

Three issues from review of the prior commit:

1. (P1) The axios response interceptor wired up ``tryToRenewAccessToken``
   but always rejected the original 401/403 even when the refresh
   succeeded — because ``mutationRenewAccessToken`` was callback-based
   and ``remakeRequest`` ran fire-and-forget on success. Callers saw a
   failure even when the retry would have succeeded.

   Switched to ``mutateAsync`` so refresh awaits cleanly. On success,
   return ``await remakeRequest(error)`` from the interceptor so the
   caller transparently receives the retried response. On failure,
   reject with the original error. ``remakeRequest`` now returns the
   full ``AxiosResponse`` so callers can read ``.data`` normally —
   previously it returned ``response.data`` which would have
   double-unwrapped through the interceptor.

2. (P2) ``ModelInputComponent`` showed the retry UI whenever an error
   was present, even when TanStack Query had preserved usable stale
   data — meaning a transient background-refetch failure could replace
   a working dropdown with "couldn't load models". The gate now requires
   the failed query to have no usable data, evaluated per-query:
   - providers: error AND (no data OR empty)
   - enabled_models: error AND data is undefined
   Either condition surfaces the retry button; if both queries have
   stale data, the dropdown keeps working through the refetch error.

3. (P2) The AUTO_LOGIN superuser fallback in
   ``authenticate_with_credentials`` returned without checking
   ``is_active``. ``CurrentActiveUser`` re-checks this for HTTP routes,
   but ``get_current_user_for_sse`` and websocket dependencies delegate
   directly to ``authenticate_with_credentials``, so an inactive
   superuser could be accepted on those paths. Now raises
   ``InactiveUserError`` before returning, matching the token and
   API-key auth paths.

Tests:
- New backend test for the inactive-superuser rejection.
- New frontend test verifying the dropdown keeps working when both
  queries have stale data plus a refetch error.

* fix(auth): guard refresh interceptor against recursion and replay loops

A 401/403 on ``/refresh`` (or any other auth-maintenance endpoint) would
re-enter the response interceptor and try to refresh again, because the
refresh mutation uses this same axios instance. With the awaited
``mutateAsync`` path that lands earlier in this PR, that recursion can
hang instead of cleanly logging the user out.

Adds two defensive guards:

1. ``isAuthMaintenanceURL`` skips the refresh-retry branch for
   ``/refresh``, ``/login``, ``/logout``, and ``/auto_login``. A 401 on
   any of those is a real auth failure that callers (typically the
   refresh mutation's own catch block) need to see immediately so they
   can drive logout.

2. A one-shot ``_retry`` flag on the original request config prevents
   a single request from looping through the refresh path more than
   once. If a request still 401s after a successful refresh, we reject
   instead of retrying again.

Includes a small unit test for ``isAuthMaintenanceURL`` covering the
positive matches, false positives on unrelated business endpoints,
and the ``/login`` vs ``/auto_login`` non-overlap.

* Update ModelInputComponent.test.tsx

* [autofix.ci] apply automated fixes

* fix(auth): address review findings on PR #13192

Backend:
- _api_key_security_impl: guard model_validate against None superuser (503 vs silent 500)
- ws_api_key_security: add None guard + is_active enforcement in skip_auth bypass;
  move AUTO_LOGIN_WARNING log after both guards
- test_auth_service: patch logger directly to assert warning; add SimpleNamespace-based
  test for empty SUPERUSER guard; add explicit SUPERUSER="admin" to missing-row test;
  add ws_api_key_security tests for None and inactive-superuser rejection

Frontend:
- isAuthMaintenanceURL: replace substring .includes() with character-boundary matching
  so /refresh_tokens etc. are not false-positively excluded from retry path
- checkErrorCount: explicit boolean return type; return false instead of undefined
  on login page
- tryToRenewAccessToken: throw original error on login page instead of silent return;
  distinguish network errors from auth failures before calling mutationLogout
- remove dead _retry flag code (remakeRequest uses bare axios bypassing interceptors)
- handleRefreshButtonPress: drop silent:true so refresh failures surface via alertStore

Tests:
- api-auth-maintenance: rewrite cross-match test against function; add path-segment
  collision test for /refresh_tokens, /login_history, /logout_sessions
- ModelInputComponent: rename misleading spinner test title; add "enabledModels fails
  alone" case; update silent flag assertion to false

* fix(auth): enforce is_active in _api_key_security_impl auto-login bypass

Matches the guard already present in ws_api_key_security so an inactive
superuser is rejected via HTTP (403) as well as WebSocket (1008).

* Update component_index.json

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
2026-05-22 19:32:58 +00:00
dce4d46c45 fix: canvas deploy attach/detach flow bugs (#13247)
* fix: canvas deploy attach/detach flow bugs

Set pendingAttachment in pre-selection effect so the connection panel
has a valid effectiveAttachmentKey. Only populate preExistingFlowIds
in edit mode so detach always hard-deletes in create mode.

* fix: prevent pre-selection effect from re-triggering on version changes

Change handledPreselectedAttachmentRef from key tracker to boolean flag
so the canvas deploy pre-selection effect runs at most once, preventing
unwanted panel switches on attach/detach/skip.
2026-05-22 19:13:56 +00:00
831c69f9b6 feat(memories): make preprocessing instructions required with tooltip hint (#13250)
* 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

---------

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>
2026-05-22 19:01:09 +00:00
68d4e8488e fix: update mem0 and qdrant dependencies (#13292)
* fix: update mem0 and qdrant dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-22 18:06:03 +00:00
63ee455a2b test: Refactor Playwright suite with shared helpers and remove redundant tests (#13278)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-22 14:35:04 -03:00
cf3c2d30c4 fix: Sync Agent-as-tool description by removing deprecated agent_description (#13151)
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>
2026-05-22 11:04:47 -03:00
5dd63ed61f fix: quiet extension migration warnings (#13275) 2026-05-21 20:33:54 -07:00
3bb93cd273 fix(flows): show readable API error messages on flow creation failure (#13140)
* fix(flows): show readable API error messages on flow creation failure

* [autofix.ci] apply automated fixes

* remove any from testcases

* [autofix.ci] apply automated fixes

* add safe guard

---------

Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-21 20:31:15 +00:00
81672ef849 fix: upgrade litellm to 1.85.1 (#13272) 2026-05-21 20:26:49 +00:00
44fd116c17 fix(tracing): forward request body user_id to Langfuse trace (#13266)
* fix(tracing): forward request body user_id to Langfuse trace (#9505)

The user_id field in POST /api/v1/run/{flow_id} request bodies was being
silently dropped by SimplifiedAPIRequest, and simple_run_flow hardcoded
the API-key owner's UUID as the trace user_id. As a result Langfuse
traces were always tagged with the Langflow user's UUID instead of the
end-user identifier supplied by the caller.

Add an optional user_id field to SimplifiedAPIRequest and a separate
Graph.tracing_user_id attribute that overrides the auth user_id passed
to TracingService.start_tracers. The auth user (API-key owner) remains
the security boundary for authentication, authorization, global
variables, and job ownership; only tracing providers see the override.

* refactor(tracing): dedicated tracing_user_id field + stamp auth user_id in metadata

Address review feedback on #13266: instead of collapsing the override into
``user_id`` at the Graph layer, plumb ``tracing_user_id`` as a dedicated
parameter through ``start_tracers`` and ``TraceContext``. The merge policy
(``tracing_user_id or user_id`` for trace labels) now lives in the tracing
service where individual providers can apply their own semantics.

Langfuse additionally stamps the auth ``user_id`` into trace metadata as
``langflow.auth_user_id`` whenever a distinct override is in effect, so
downstream consumers that filtered by the Langflow user UUID can still
recover it.

* refactor(tracing): keep LangFuseTracer.user_id as auth user, expose override as tracing_user_id

Reviewer feedback: preserve backwards compatibility on ``LangFuseTracer.user_id``
(still the authenticated Langflow user) and expose the caller-supplied override
on a new ``tracing_user_id`` field instead of an ``auth_user_id`` rename.

The merge into Langfuse's ``trace.userId`` (and the metadata stamp under
``langflow.auth_user_id`` when an override is in effect) is performed inside
``_setup_langfuse`` at the Langfuse SDK boundary, not at the tracer-attribute
boundary. Any consumer that read ``tracer.user_id`` before keeps seeing the
auth user.

* refactor(tracing): keep trace.userId as auth user; stamp override in metadata

Per PR #13266 review (HzaRashid): ``trace.userId`` stays as the authenticated
Langflow user so existing Langfuse consumers keep receiving the same identity.
The caller-supplied ``tracing_user_id`` is now stamped into trace metadata as
``langflow.tracing_user_id`` instead of redefining ``trace.userId``.

Also refreshes the now-stale comments and docstrings across the lfx and
backend tracing layers that still described the old "override takes
precedence" semantics, and adds regression tests pinning the new behavior
on the ``update_trace`` kwargs.
2026-05-21 18:24:33 +00:00
599878da1d fix(docker): pre-create LANGFLOW_CONFIG_DIR so named volumes inherit uid=1000 ownership (#13212)
* fix: pre-create LANGFLOW_CONFIG_DIR in docker images to fix named volume permissions

The Langflow runtime stage runs as uid=1000 (non-root user), but the
official docker_example compose file mounts a named volume at
/app/langflow (LANGFLOW_CONFIG_DIR). When Docker creates a fresh named
volume, it copies the ownership and permissions of the mount-point
directory from the image. Because /app/langflow did not exist in the
image, Docker materialized the mount point as root:root, and the
non-root container user could not write secret_key, profile_pictures,
or per-user subfolders. The container crashed during initialization
with PermissionError on /app/langflow/secret_key.

Pre-create /app/langflow in every runtime stage and chown it to 1000:0
so a fresh named volume inherits the correct ownership and the
container starts cleanly on a rootful Docker daemon.

Podman's rootless user-namespace mapping masked this for anyone who
validated the compose file on Podman; on a rootful Docker daemon the
container exits in a restart loop.

Fixes #10437

* revert: drop docker_example/README.md changes

The Troubleshooting note will live in the user-facing docs site
instead of the in-repo README.
2026-05-21 18:14:28 +00:00
0663fce847 fix(guardrails): add message pass-through outputs (#13096)
* fix(guardrails): add message pass-through outputs

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

* [autofix.ci] apply automated fixes

* feat(guardrails): unify data output payload

* fix(guardrails): add message pass-through outputs

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

* [autofix.ci] apply automated fixes

* feat(guardrails): unify data output payload

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

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* fix(guardrails): set result data default to null

* [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>
2026-05-21 18:10:39 +00:00
02e1b402e5 fix(frontend): blur upload button so "Upload File" tooltip doesn't persist after file picker closes (#13178)
fix(frontend): blur upload button so tooltip doesn't persist after file picker closes

On the My Files page, clicking the "Upload Files" button kept the
"Upload File" tooltip visible after the OS file picker closed. The
button is wrapped in a Radix tooltip, which shows on focus; when the
file dialog closed, focus was restored to the button and re-triggered
the tooltip. Blur the button on click so focus doesn't return to it.

Also tighten pre-existing `any` types in FilesTab to satisfy the
no-explicit-any lint rule on the staged file.
2026-05-21 17:02:11 +00:00
b5db6fd0b2 fix(memory-base): Product Review Improvements (#13248)
* fix(memory-base): rename tool method to "retrieve_memory" and pad trace status badges

- Rename MemoryBaseComponent.retrieve_data to retrieve_memory so the
  derived tool label in tool mode reads "Retrieving Memory" instead
  of "Retrieve Data". Output.name kept as "retrieve_data" to preserve
  saved flow edges.
- Add vertical padding to status badges in TraceAccordionItem and
  SpanDetail so the success/failure label has breathing room inside
  the badge.

Added guardrails to MB system prompt.

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-21 15:20:22 +00:00
838175a132 fix(composio): handle Python keywords and non-identifier field names in action schemas (#13139)
* fix: coerce Message and Data to primitives before Composio API execution

* fix: coerce Message and Data to primitives before Composio API execution

* [autofix.ci] apply automated fixes

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

* Fix Ruff line

* fix(composio): handle Python keywords and non-identifier field names in action schemas

* [autofix.ci] apply automated fixes

* fix(composio): remap Python keyword fields to original names in execute_act

* ruff changes fixed

---------

Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-21 13:14:31 +00:00
288bc8efe4 fix(ci): use local bundle wheels in nightly install tests (#13257) 2026-05-20 19:46:40 -07:00
2df2032443 fix: Expose Message connection handle on Run Flow's exposed text inputs (LE-1233) (#13180)
* fix: Expose input handle for run flow

* Update test_run_flow.py
2026-05-20 21:50:02 +00:00
8ee70279f8 feat(memories): surface memory config in summary card with popover (#13209)
* 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

---------

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>
2026-05-20 20:16:25 +00:00
dfb34c7be4 fix(agent): scope chat history retrieval by flow_id to prevent cross-flow leak (#13087)
* fix(agent): scope chat history retrieval by flow_id to prevent cross-flow leak (#13059)

AgentComponent.get_memory_data filtered chat history by session_id only, so
the playground's default session names (e.g. "New Session 0") leaked
conversation history across unrelated flows whenever two flows happened to
use the same default name.

The fix replaces the ad-hoc MemoryComponent spawn (whose internal _vertex is
None and therefore cannot see the running flow's flow_id) with a direct
aget_messages call that passes flow_id from the agent's own graph context.
MemoryComponent.retrieve_messages is also updated to scope by self.graph.flow_id
when available, so the standalone Message History component does not have
the same leak.

Adds regression tests covering: UUID coercion of string flow_ids, isolation
of two flows that share a session name, graceful fallback when flow_id is
missing or non-UUID, untouched external-memory path, and filtering out the
current input message.

* fix(agent): preserve n_messages=0 disable-memory contract and apply scoping to Cuga

Review follow-up on #13087:

- AgentComponent.get_memory_data regressed n_messages=0: before this PR the
  call went through MemoryComponent.retrieve_messages which short-circuits to
  [] when n_messages==0. The direct aget_messages call lost that contract,
  because `if self.n_messages:` is falsy for 0 and `messages[-0:]` returns
  every message fetched under limit=10000.

- CugaComponent.get_memory_data had the same leak pattern (issue #13059)
  because it also spawned an ad-hoc MemoryComponent whose _vertex is None.

Extract aget_agent_chat_history(session_id, flow_id, context_id, n_messages)
into memory.py:
  * returns [] when n_messages == 0
  * coerces flow_id to UUID (gracefully falls back on invalid values)
  * applies n_messages slicing

Both AgentComponent and CugaComponent now route through it.

Tests:
  * aget_agent_chat_history: passes flow_id as UUID, short-circuits on
    n_messages=0, slices to most-recent N, returns all on n_messages=None,
    falls back to unscoped query on invalid flow_id.
  * AgentComponent integration: routes through helper with flow_id, filters
    out current input, n_messages=0 disables memory (asserts aget_messages
    is never awaited so a future regression resurfaces as a real DB call).
  * CugaComponent integration: scopes by flow_id, n_messages=0 disables
    memory. Module-import gated for envs without optional Cuga deps.

* [autofix.ci] apply automated fixes

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

* [autofix.ci] apply automated fixes

* Template update

* [autofix.ci] apply automated fixes

* Update .secrets.baseline

* [autofix.ci] apply automated fixes

* Update starter projects

* [autofix.ci] apply automated fixes

* Update .secrets.baseline

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* fix(memory): address PR #13087 review — symmetric flow_id, DESC fetch, loud fallback

Addresses the review comments on PR #13087:

I1 (symmetric flow_id handling): `store_message` now routes the
internal-memory write through `_coerce_flow_id_to_uuid(_safe_graph_flow_id(self))`
instead of accessing `self.graph.flow_id` directly, so an ad-hoc /
custom-subclass MemoryComponent without `_vertex` no longer crashes
on the write half before reaching the safe read half. Both calls share
a single `flow_id_scope` variable.

I2 (>10k row ordering bug): both `aget_agent_chat_history` and
`MemoryComponent.retrieve_messages` (internal-memory branch) now query
in `DESC` order with `limit=n_messages` (falling back to
`MAX_CHAT_HISTORY_FETCH_LIMIT` when no explicit limit is set), then
reverse to the caller's preferred order. The previous ASC + slice shape
returned the chronological FIRST window once sessions exceeded 10k
rows, silently serving stale history.

I3 (loud fallback observability): when `_coerce_flow_id_to_uuid` is
forced to return `None` for a malformed `flow_id`, the log call is now
`logger.error` (was `warning`) and carries a structured `event` tag
(`memory_flow_id_unscoped`). The unbounded-fetch ceiling-hit path
emits a parallel `memory_chat_history_limit_reached` warning. Both
events are explicit alert hooks for observability pipelines so a
regression cannot silently re-enable the cross-flow leak that motivated
the PR.

R1 (magic number): extracted `MAX_CHAT_HISTORY_FETCH_LIMIT = 10_000` as
a module-level constant; reused in both call sites and the test suite.

R2 (signature): tightened `_coerce_flow_id_to_uuid` and
`_safe_graph_flow_id` from `Any` to `str | UUID | None`; tightened
`aget_agent_chat_history`'s `flow_id` argument accordingly.

Tests: extended `test_memory_flow_id_scoping.py` with coverage for
each of the above — symmetric store/read flow_id (I1), DESC+reverse
ordering on retrieve_messages (I2), ceiling-warning emission (I2),
structured error log on flow_id fallback (I3), and explicit-limit
suppression of the warning. All 26 tests pass.

* [autofix.ci] apply automated fixes

* Template update

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-20 19:46:51 +00:00
ec3aea7ff8 fix(extensions): discover editable bundles; clean up ticket refs and reload UX (#13219)
* fix(extensions): discover editable installs via langflow.extensions entry-point

`lfx extension list` was silently dropping bundles installed via `uv pip
install -e` / `pip install -e`. Editable installs surface only
`dist-info/` entries in `dist.files`, so the wheel-shaped scan in
`_distribution_manifest_path` finds no `extension.json`. The bundle
pyproject.toml comment already promised an entry-point fallback for this
case, but it was never implemented in discovery.py.

Add `_distribution_manifest_path_via_entry_points` that resolves the
package via `importlib.util.find_spec` (no module body execution) and
locates the manifest under the resulting package directory. The fallback
runs only when `dist.files` produces no manifest, so wheel installs are
unaffected.

Tests:
- editable distribution with dist-info-only files is discovered
- entry-point pointing at an unimportable module yields no record
- wheel-install path never consults find_spec (guards against
  re-importing every bundle package on startup)

* chore(extensions): drop internal ticket refs; relax reload --bundle and --all

Two related changes:

1. Strip internal `LE-NNNN` ticket references from the extension source,
   bundles, tests, and public docs. The references were not actionable
   outside the project and surfaced in user-visible CLI messages and the
   author guide.

2. Relax `lfx extension reload` CLI ergonomics now that local discovery
   (`discover_all_extensions`) gives us the install map without needing
   an HTTP list endpoint:

   - `lfx extension reload <ext_id>`: `--bundle` is now optional; when
     omitted, the bundle name is resolved from local discovery. Explicit
     `--bundle` still wins for cases where the local install isn't
     visible to the running server.
   - `lfx extension reload --all`: iterates over every locally-discovered
     bundle, POSTs reload to each, renders per-bundle status, and exits 1
     if any reload fails. Previously hard-errored as "not yet wired".
   - `--all` is mutually exclusive with a positional id / `--bundle`
     (exit 2 with a clear message).
   - Missing both `extension_id` and `--all` exits 2 pointing at both.

Updated `test_reload_requires_explicit_bundle` (which enforced the old
"--bundle is mandatory" behavior) to cover the new resolution paths.

* docs(bundle-api): changelog entries for editable-install discovery + reload CLI

Satisfies the BUNDLE_API surface-change gate for the editable-install
discovery fallback (entry-point lookup in discovery.py) and the relaxed
`lfx extension reload` CLI (`--bundle` optional, `--all` implemented).

No additional behavior change in this commit -- it only documents the
two changes already shipped in this branch.

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Debojit Kaushik <Kaushik.debojit@gmail.com>
2026-05-20 18:10:59 +00:00
651d9e71a8 fix: coerce numpy scalars in Memory Base metadata to Python primitives (#13211)
* fix: coerce numpy scalars in Memory Base metadata to Python primitives

Chroma persists integer/float metadata as numpy.int64 / numpy.float64
scalars. When the Memory Base component is used as an Agent tool, the
LangChain tool-output path calls vars() on and iterates these values
during serialization, raising:

    TypeError("'numpy.int64' object is not iterable")
    TypeError('vars() argument must have __dict__ attribute')

Coerce metadata values (and the derived _score) to Python primitives in
_format_results so the emitted DataFrame is JSON-safe regardless of how
downstream consumers serialize it.

* [autofix.ci] apply automated fixes

* Update component_index.json

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-20 15:40:21 +00:00
4915126efb feat(i18n): globalization pipeline — frontend i18n with backend locale support (#12933)
* feat(i18n): globalization pipeline — frontend + backend i18n support

Squash of PR #12825 (feat/gp-frontend-i18n-batch-c) onto release-1.10.0.

- IBM Globalization Pipeline integration with GP REST API client
- react-i18next setup with 7 language support (en, fr, ja, es, de, pt, zh-Hans)
- Lazy-load non-English locale files via Vite dynamic imports
- Language selector in Settings > General page
- Backend: serve translated component metadata via Accept-Language header
- Translate canvas node field labels, outputs, templates on language change
- Translate note nodes via dedicated endpoint
- Translate all UI modals and pages (MCP, Knowledge Base, Settings, etc.)
- CI: GP upload/download workflows, auto-bake note keys
- Content-hash component keys for stable GP translations
- Preserve user-customized node display_name/description on language switch

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

* revert: restore scripts/gp to release-1.10.0 state (no functional changes)

* revert: restore package-lock.json and flows_helpers.py to release-1.10.0

package-lock.json: npm noise, not part of this feature
flows_helpers.py: unrelated blank-line change

* revert: remove out-of-scope backend changes

- starter_projects/*.json: trivial trailing newline only, not i18n content
- api/v1/flows.py: unrelated deployment guard removal and flow update refactor
- tests/unit/test_i18n_note_translation.py: minor non-functional changes

* revert: restore Pokédex Agent.json to release-1.10.0

* fix(i18n): remove unused import and fix loop variable shadowing in i18n.py

Resolves ruff F401 (content_hash imported but unused) and PLW2901
(for-loop variable `node` overwritten by assignment).

* [autofix.ci] apply automated fixes

* fix(i18n): fix ruff I001 import sort in i18n.py

* fix: remove duplicate useTranslation hook declaration in recentFilesComponent

* fix: restore deployment components overwritten by squash merge and fix i18n test mock

The squash merge brought in older versions of deployment page components,
stripping functionality added by PRs that landed on release-1.10.0 after
our feature branch diverged (duplicate tool name validation, deploy choice
dialog update flow, step-type/step-attach-flows logic).

Restored 14 files to their release-1.10.0 state. Also added __esModule: true
to the i18n mock in flowStore.test.ts to fix the i18n_1.default.t TypeError.

* fix: restore addMcpServerModal, SaveSnapshotButton, KnowledgeBaseUploadModal to release-1.10.0

These files had functionality stripped by the squash merge (buildKeyPairPayload/
buildArgsPayload helpers, save version dialog logic, modal height/validation
constants). Restored to release-1.10.0 state.

* fix: restore flowSidebarComponent ShadTooltip wrappers lost in squash merge

* fix(i18n): revert node_copy rename back to translated_node in translate_flow_notes

The rename was unnecessary — release-1.10.0 already used translated_node
(not reassigning the loop variable node), so ruff PLW2901 doesn't apply.

* chore: sync frontend locale files from GP [skip ci]

* chore: sync backend locale files from GP [skip ci]

* chore: sync backend locale files from GP [skip ci]

* fix(i18n): add missing en.json keys and sync frontend locale files from GP

Added 7 missing keys: alerts.modelsRefreshed, mcp.servers.toolsCount,
misc.fetchErrorDescription/Message, misc.timeoutErrorDescription/Message,
store.results.

* [autofix.ci] apply automated fixes

* ci: add frontend i18n key coverage check with PR report

Adds a script and GitHub Actions workflow that:
- Scans all .ts/.tsx source files for t("key") calls
- Verifies every key exists in en.json
- Posts a markdown report as a sticky PR comment explaining:
  - Missing keys (actionable failures)
  - The expected gap between en.json total and detected keys
    (pluralization suffixes, dynamic keys, test files excluded)

Fails the check if any hardcoded t("key") call has no en.json entry.

* [autofix.ci] apply automated fixes

* feat(i18n): translate remaining hardcoded trace column headers and sync locale files

* fix: resolve ruff style check failures in check_frontend_i18n_keys.py

- Use set membership for multiple comparisons (PLR1714)
- Replace ambiguous Unicode character with plain text (RUF001)
- Split long lines to comply with 120 char limit (E501)
- Replace magic number with named constant (PLR2004)

* [autofix.ci] apply automated fixes

* fix(i18n): narrow zh-* mapping to Simplified Chinese variants only

zh-TW and zh-HK (Traditional Chinese) now fall back to English instead
of incorrectly receiving Simplified Chinese (zh-Hans) translations.

* fix(i18n): use canonical folder names instead of translated strings in use-add-flow

Folder names are stored identifiers used by backend MCP setup and
useGetFolders — translating them caused non-English users to get
unrecognized folder names, breaking starter-project flows and MCP wiring.

* [autofix.ci] apply automated fixes

* fix(i18n): use hardcoded English name for new project to avoid translated MCP server names

* fix(i18n): truncate long sidebar labels and show full text in tooltip

Long translated strings (e.g. Japanese) were wrapping inside the
fixed-height get-started panel. Labels now truncate with ellipsis
and reveal the full text via ShadTooltip on hover.

* [autofix.ci] apply automated fixes

* feat(i18n): translate Deploy button label and sync locale files

* fix(i18n): sync SUPPORTED_LANGUAGES with actual locale files

Remove ru and ko (no locale files exist) and add de (de.json exists).

* fix(i18n): fall back to English for null and empty string translations

Add returnNull and returnEmptyString options so partially translated
locale files with null or empty placeholder values still show English.

* fix(i18n): prevent crash on Chinese browser locale by routing through normalizeLanguage

index.tsx had its own detectedLang using navigator.language.split("-")[0],
which stripped "zh-CN" to "zh" and bypassed normalizeLanguage, causing
loadLanguage to attempt importing a non-existent zh.json and crash.

Now index.tsx imports detectedLang from i18n.ts so all locale detection
goes through normalizeLanguage — zh/zh-CN/zh-TW all resolve safely.

* feat(i18n): translate knowledge base table headers, status labels, and context menu

- Wire useTranslation into FilesPanel (Sources label, file/files count)
- Pass t() into createKnowledgeBaseColumns for all column headers and menu items
- Convert STATUS_CONFIG labels to i18n keys so Ready/Ingesting/etc. translate
- Upload updated en.json to GP and download machine translations for all 6 locales

* feat(i18n): translate run component tooltip and export modal button

* feat(i18n): wrap hardcoded strings with t() across deployment stepper, flow version sidebar, knowledge base, and account pages

Adds 53 new translation keys to all 7 locale files and wraps previously hardcoded English strings with t() calls in 17 source files: deployment stepper components (connection search, connection panel, flow list panel, step-type, step-review, success content, footer, delete dialog), flow version sidebar (save dialog, snapshot button, sidebar, delete confirm), knowledge base drawer and chunk card, delete account page, and store API key form.

* feat(i18n): translate edit node modal, table modal, and AG Grid strings

Wraps hardcoded strings in editNodeModal (Field Name, Description, Value,
Show, Expose Input, Close), textAreaModal (Finish Editing), intComponent
placeholders, tableModal Save button, ColumnConfig Open Table button, and
AG Grid built-in overlays (No Rows To Show, pagination strings) with t().

* feat(i18n): translate playground chat, file management, user modal, notifications, deployment dialogs, crash page, and toolbar shortcuts

Wraps hardcoded strings with t() across ~50 files and adds ~156 new keys to all 7 locale files (en, de, es, fr, ja, pt, zh-Hans).

Areas covered:
- Playground: sessions sidebar, voice input tooltips/errors, chat input placeholder, drop zone, file attachment tooltip
- File management: context menu (rename/download/duplicate/delete/remove), confirmation modals, CRUD notifications
- User management modal: username, password, confirm password, active, superuser fields
- Notifications panel: title
- MCP server modal: ja.json fix for Streamable HTTP/SSE URL field label and placeholder
- Messages table: "files" column header
- Deployment dialogs: Select Deployment phase, Flows step (available/attached/removed badges, connection panel, flow list panel)
- Crash error page: all text regions including split description around GitHub Issues link
- Toolbar shortcuts: shortcutDisplay renders translated name via shortcuts.name.* keys (keys already existed in all locales)

* feat(i18n): translate node toolbar action tooltips, update bar labels, and confirmation modal strings

* feat(i18n): translate note toolbar, inspection panel, sidebar config trigger, and table pagination strings

* feat(i18n): translate header GitHub/Discord tooltips, playground disabled tooltip, and session options tooltip

* feat(i18n): translate IOModal new chat, edit/copy message, and helpful/not-helpful tooltips

* feat(i18n): translate voice assistant audio settings, labels, tooltips, and API key inputs

* translations of previous entries

* feat(i18n): translate hardcoded strings across error messages, JSX text, and aria/title attributes

Migrated ~55 new keys to en.json and updated ~45 source files: error toasts (streaming,
build failures, file ops, shortcuts), modal titles (session logs, flow details, view text,
restore version, CSV separator, chunk preview), node/output labels (incompatible with, no
output, component output, no tools), model panel (configure provider, not enabled), JSON
editor validation messages, Langflow logo title attributes, and various aria-labels.

* feat(i18n): translate remaining hardcoded strings in alt texts, placeholders, and step titles

- Add useTranslation + translate alt text in ProfileIcon, assistant-empty-state, CanvasControls, assistant-message, assistant-no-models-state
- Translate assistant welcome text and suggestion labels in assistant-empty-state
- Fix dropdownComponent search placeholder to use existing input.searchOptions key
- Fix mcp-server-notice image alt text
- Fix empty-page Langflow logo alt texts (light/dark)
- Add useTranslation + translate file upload step titles in code-tabs via STEP_TITLE_KEYS lookup map
- Add 9 new keys to en.json: sidebar.mcpNoticeImageAlt, common.langflowLogoLight/Dark/userProfileAlt, assistant.welcomeText, assistant.suggestion.*, apiModal.uploadFilesStep/executeFlowStep

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

* feat(i18n): translate remaining hardcoded strings across modals, panels, and components

- codeAreaModal: Done button, Check & Save, Discard Changes, Caution confirmation
- shareModal: Share header, Replace confirmation, status public label, Export button, footer labels, success message, attention text
- mustachePromptModal: Edit Prompt title, Prompt Variables label, Check & Save button
- updateComponentModal: Standard label, backup checkbox, update button, warning paragraphs
- update-phase: Close/Test buttons, sr-only dialog title/description strings
- provider-phase: Cancel/Continue buttons, provider description
- assistant-component-result: Inputs/Outputs headings, Approved/Add to Canvas/View Code actions
- message-options: Edit message, Helpful, Not helpful, Copied, Copy message tooltips
- storeCardComponent: Private, Components, Likes, Downloads, Like, Install Locally, error liking
- ProviderList: Loading providers text
- outputModal: Outputs/Logs tabs, inspect description
- io-field-view: No node found, Enter text placeholders
- InspectionPanelFields: No editable fields, No advanced settings
- InspectionPanelOutputs: No output data message
- FlowVersionSidebar: No saved versions yet
- step-attach-flows-version-panel: Select flow/version, loading/no versions, Created date
- StepReview: No files, generating preview, could not generate, Summary, labels, not selected
- VisibilityToggleButton: Hide field / Show field aria-labels
- ModelSelection: Disable/Enable model aria-labels
- MemoizedSidebarTrigger: Components label
- file-card, file-preview: File label

Adds 63 new keys to en.json (1,500 total).

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

* fix(i18n): add missing useTranslation hook to SidebarTrigger component

SidebarTrigger used t("ui.toggleSidebar") without calling useTranslation(),
causing a ReferenceError crash when the TemplatesModal opened (which renders
a SidebarTrigger with no children prop, hitting the sr-only span).

* feat(i18n): translate visibility tooltips, connect other models, global variables placeholder, and status filter truncation

- Translate 4 visibility toggle tooltips in tableAdvancedToggleCellRender
- Fix "Connect other models" always showing English by using t() directly instead of backend display_name fallback
- Translate Global Variables search placeholder in inputGlobalComponent
- Add SelectTrigger truncation for status filter dropdown in FlowInsightsContent

* feat(i18n): resolve ModelSelection conflict — merge Deprecated badge with translated aria-labels

Combines changes from both branches:
- Keep translated aria-labels for enable/disable model toggles using existing keys
- Add Deprecated badge from release-1.10.0 with t("modelProvider.deprecated")
- Add modelProvider.deprecated key to en.json

* [autofix.ci] apply automated fixes

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

* feat(i18n): fix missing en.json keys and translate hardcoded strings across Knowledge Base, Memory, and component files

Adds 53 new translation keys (knowledge.*, memory.* namespace — new, and misc) and wires up t() calls in 19 source files to eliminate hardcoded English strings in placeholders, labels, modal titles, aria-labels, and empty states.

* chore: remove check-frontend-i18n CI workflow

* chore: remove check_frontend_i18n_keys.py script

* [autofix.ci] apply automated fixes

* chore: revert component_index.json version bumps from release-1.10.0 merge

* chore: sync component_index.json to release-1.10.0

* feat(i18n): update backend locale translations from GP

* fix(i18n): fix frontend jest test failures from i18n migration

- Fix paginator empty state: strip "of " from translation keys and
  add it inline in the non-empty branch so 0 items shows "0 items"
  instead of "of 0 items"
- Revert text renames back to original English copy: "Ingest Content",
  "Add Files", "DB Provider", "1 VERSION", and the full deploy-from-draft
  message so existing tests keep passing without modification
- Fix punctuation mismatches: remove trailing "!" from changesSaved and
  "..." from loadingProviders
- Add i18next plural form resolution (_one/_other) to the jest t() mock
  so pluralised strings resolve correctly in tests
- Default t param in createKnowledgeBaseColumns to en.json fallback
  instead of key passthrough so the function works correctly without
  a translation function argument

* test(frontend): fix deploy version assertion

* fix(i18n): add missing useTranslation import to MemoizedComponents

* translation updated

* [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>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Viktor Avelino <viktor.avelino@gmail.com>
2026-05-20 13:38:53 +00:00
f1ecb51319 fix: UI polish for Memories and Traces sidebar sections (#13205)
* 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

* fix: replace magic index, template literals, and inline noops in Memories/Traces sidebar

* [autofix.ci] apply automated fixes

* ruff fix

* fix testcases

---------

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>
2026-05-19 23:47:16 +00:00
4f9ef2af37 refactor: Move File System component to Files & Knowledge category (#13223)
* change fs category

* component index

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-19 23:26:54 +00:00
cb10f1d10f docs: file system component (#13183)
* docs-file-system-component

* delete-internal-feature-doc

* docs-filesystem-component

* fix-links

* Apply suggestions from code review

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>

* clarify-metadata

* fix-merge

---------

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>
2026-05-19 18:46:30 +00:00
bd54c1b952 feat(memory): improve memory UI with empty states, toasts, and model (#13116)
* 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

---------

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>
2026-05-19 18:33:51 +00:00
bc656b44e8 fix(openrouter): validate against /api/v1/auth/key and disconnect all variables (#13214)
Two follow-ups to PR #13185 surfaced during release-1.10.0 QA.

BUG-1 (high) — OpenRouter API key validation never rejected invalid keys.
``validate_model_provider_key`` probed OpenRouter's ``/api/v1/models``
endpoint, which is *public* and returns 200 for any bearer token (including
missing/invalid). So ``POST /api/v1/variables/`` accepted any string as an
OPENROUTER_API_KEY with 201, the catalog swapped to the live 356-model list,
and runs failed at request time. Route the probe through
``/api/v1/auth/key`` instead — auth-required, returns 401 on invalid, the
existing ValueError → HTTP 400 mapping in ``api/v1/variable.py`` does the
rest. Same fix lifts BUG-3 since the generalized validation path is shared.

BUG-2 (medium) — clicking Disconnect on OpenRouter in Settings → Model
Providers did nothing. ``handleDisconnect`` resolved the variable name via
the static ``PROVIDER_VARIABLE_MAPPING`` constant, which doesn't include
"OpenRouter" (and the constant is deprecated in favour of the dynamic
``GET /api/v1/models/provider-variable-mapping`` API). Source the variable
keys from ``providerVariables`` instead and delete every configured one in
parallel so multi-variable providers (OpenRouter's API key + attribution
headers, IBM WatsonX's apikey + project_id + url) are fully removed. The
static mapping is kept as a fallback for the brief window before the
provider-variable API resolves.

Tests
- ``test_openrouter_provider.py``: update happy/regression cases to pin the
  new ``/api/v1/auth/key`` URL and add an explicit assertion that the public
  ``/api/v1/models`` endpoint is never used for validation.
- ``test_variable.py``: add ``POST /api/v1/variables/`` integration tests
  covering both a 401 (returns 400 with "Invalid OpenRouter API key") and a
  200 (returns 201), and assert the auth-required URL is used.
- ``useProviderConfigurationDisconnect.test.tsx``: new suite — disconnect
  for OpenRouter deletes all three variables in one batch, fall-back to the
  static map for Anthropic still works, no-ops when nothing is configured,
  surfaces an error toast when a delete fails.

Fixes the issues reported against release-1.10.0 OSS QA of PR #13185.
2026-05-19 12:27:12 -07:00