Commit Graph

17635 Commits

Author SHA1 Message Date
1d9bf51b88 feat: enable F.Ks in SQLite Pragmas 2026-04-22 18:04:31 +00:00
3ea28ed902 fix: propagate x-api-key and authorization headers to nested MCP calls (#12541)
* fix: propagate x-api-key and authorization headers to nested MCP calls (fixes #12529)

When Langflow is used as an MCP server containing flows with nested MCP
components, authentication headers like x-api-key were silently dropped
because extract_global_variables_from_headers() only captured headers
with the X-LANGFLOW-GLOBAL-VAR-* prefix.

Add _AUTH_HEADERS_TO_PROPAGATE to also capture x-api-key and authorization
under their lowercase header names. These values are stored in the
request_variables context, making them available for resolution in nested
MCP server configs. Users can now reference them in their server headers
config as {x-api-key: x-api-key} to propagate the incoming key.

* fix: remove duplicate verify_public_flow_and_get_user from core.py

The duplicate function in core.py referenced undefined names (uuid, session_scope)
and shadowed the canonical implementation in flow_utils.py, which is the one
re-exported from __init__.py and has the fuller signature including
authenticated_user_id. Removing the dead copy resolves the F821 ruff errors.

* Tighten scope of fix

* Update .secrets.baseline

* Clean up test locations

* Update .secrets.baseline

---------

Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-21 23:53:43 +00:00
3bc012440f fix: mcp component dyanamic tool (#12779)
* fix(mcp): support real-time tool onboarding via per-request auth headers

Problem
-------
When Langflow runs an agent that has an MCPTools component attached and an
API request supplies authentication headers via tweaks, the MCP server can
legitimately return an *expanded* tool list that is broader than the
unauthenticated base set (the server filters its listing by the caller's
identity). Today those extra tools never reach the agent at runtime: the
component always binds the first preloaded tool list and ignores the new
auth context for the lifetime of the flow.

Root cause
----------
Several caching / binding layers combine to prevent per-request tool
refresh:

1. Flow JSON persists ``component_as_tool`` with ``cache: true`` and
   Langflow's ``Output`` memoization reuses the first ``to_toolkit()``
   result for all subsequent requests regardless of headers.
2. The shared MCP server cache was keyed by server name only, so
   requests with different auth contexts collided on the same cache slot.
3. In tool-mode, ``_get_tools()`` returned ``[]`` when
   ``_not_load_actions`` was true, starving the agent of MCP tools.
4. Concurrent ``update_tool_list`` calls raced on the same Streamable
   HTTP client session, producing intermittent HTTP 404 /
   "Session terminated" errors from the MCP SDK.

What changed
------------
``src/lfx/src/lfx/components/models_and_agents/mcp_component.py``:

- FIX 1 - Output cache bypass: ``_build_tool_output`` declares the
  Toolset output with ``cache=False``, and ``map_outputs`` overrides any
  ``cache: true`` value persisted in saved flow JSON. Together these
  guarantee every run resolves a fresh ``to_toolkit()`` call regardless
  of whether the flow was loaded from disk. Independent of the
  user-facing ``use_cache`` / "Use Cached Server" toggle.

- FIX 2 - Header-aware cache key: ``_mcp_servers_cache_key`` now hashes
  the component headers into the shared server-cache key so requests
  with different auth contexts land in distinct cache slots instead of
  masking each other. The shared cache is bounded at
  ``SHARED_SERVERS_CACHE_MAX_ENTRIES`` = 64 with FIFO eviction so a
  tenant rotating session tokens does not grow the map without limit.

- FIX 3 - ``_get_tools`` always fetches: removed the ``_not_load_actions``
  short-circuit that returned ``[]`` in tool-mode, so the agent always
  binds the current (possibly expanded) tool list. ``_not_load_actions``
  still gates only the UI build_config dropdown.

- FIX 4 - Serialized ``update_tool_list``: an ``asyncio.Lock`` prevents
  concurrent refreshes from racing on the same Streamable HTTP client
  session, eliminating the intermittent HTTP 404 / "Session terminated"
  errors the MCP SDK raised when session DELETE and POST overlapped.

- FIX 5 - TTL tool cache: ``_get_tools`` keeps a per-instance,
  header-hash-keyed TTL cache (``TOOL_TTL_SECS`` = 30s, disable with 0)
  bounded at ``TOOL_TTL_MAX_ENTRIES`` = 32 with FIFO eviction and
  stale-on-read drop. Parallel agent steps that share the same auth
  reuse the tool list instead of each paying for a fresh MCP round-trip.
  This is deliberately distinct from ``use_cache`` / "Use Cached Server"
  which controls the cross-request shared cache. The dict is initialized
  in ``__init__`` so every instance gets its own cache.

Impact on users
---------------
- Backward compatible. Unauthenticated / non-tweaked flows behave
  identically: the TTL cache is per-instance and scoped to a single
  (server, header-hash) pair, and the base-class ``to_toolkit`` chain is
  unchanged (no override of filtering via ``tools_metadata`` /
  ``enabled_tools``).
- New behaviour only triggers when tweaks/UI headers include an auth
  context; the header-hash cache key then isolates per-caller tool lists
  and the agent binds the correctly-filtered expanded set.
- No changes to ``lfx/services/settings/base.py``: the global
  ``mcp_server_timeout`` default stays at its existing value.

Test plan
---------
- ``python -m py_compile`` passes on the edited module.
- ``python -m ruff check`` reports no new violations introduced by this
  change.
- Local verification against an MCP server that returns different tool
  lists per caller identity:
  * Unauthenticated request returns the base tools as before.
  * Authenticated request (auth headers via tweaks) returns the
    expanded tool set on the first call and on subsequent calls with
    the same auth - TTL cache hits log
    ``MCP _get_tools: TTL cache hit``.
  * Switching to a different auth context on the same flow returns the
    correct per-caller tool list (header-hash cache key isolates the
    two contexts).
  * Parallel agent runs no longer surface HTTP 404 / "Session
    terminated" from the MCP SDK.
  * Disabling a tool via the UI (``tools_metadata``) correctly hides it
    from the bound agent - base-class filtering is preserved.

* [autofix.ci] apply automated fixes

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

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

* test(mcp): cover dynamic-tool onboarding fixes

Add unit coverage for the fixes introduced in this PR so regressions in the
cache-key, TTL cache, concurrency lock, shared-cache eviction, and Toolset
Output.cache=False behaviour are caught by CI instead of by users:

- Header normalization across list / dict / None / malformed shapes
- `_mcp_servers_cache_key` determinism + header-hash scoping across auth
  contexts (different headers → different keys; order-independent)
- Per-instance `_ttl_tool_cache` isolation (no cross-instance leakage)
- `_get_tools` TTL cache: hit skips `update_tool_list`, expired entries are
  refetched, FIFO-eviction caps growth at `TOOL_TTL_MAX_ENTRIES`, and TTL=0
  disables the cache
- `_update_tool_list_lock` serialises concurrent calls (peak concurrency 1)
- Shared `servers` cross-request cache evicts oldest entry when the map
  reaches `SHARED_SERVERS_CACHE_MAX_ENTRIES`
- `_build_tool_output` declares `cache=False`; `map_outputs` overrides
  persisted `cache=True` from saved flow JSON

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Mansura <mansura.nw@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-21 14:26:21 +00:00
8db793435e fix(frontend): show backend error detail on project upload failure (#12791)
* fix(frontend): read backend error detail on project upload failure

Backend returns error payload under `detail` (FastAPI convention), but
the upload handler was reading `message`, producing an empty alert list.
Switch to `detail` with `message` and raw-error fallbacks, typed via
AxiosError.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): simplify project upload error handler

Revert defensive AxiosError typing; keep direct `detail` read which is
sufficient for the upload error path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): guard upload error handler against missing response

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:05:43 +00:00
16432b61f2 fix(mcp): reconnect Streamable HTTP after MCP server restart (#12777)
…without bogus SSE fallback

When the remote MCP process restarts, Langflow kept a stale ClientSession:
tool list updates could fail with "Session terminated", the session
health check could re-raise instead of discarding the session, and
any Streamable HTTP failure triggered SSE fallback and could lock
transport preference to SSE—masking the real issue and producing
misleading TaskGroup/dual-transport errors.

Invalidate all sessions for the server URL on list_tools failure,
treat any health-check failure as a dead session, retry transient
Streamable HTTP errors before considering SSE, only fall back to
SSE for clear transport-mismatch signals (e.g. 404/405/406),
reset and re-record transport preference accordingly, and bust
sessions on run_tool using the same termination/connection signals.

Add unit tests with mocked streamablehttp_client and sse_client.
2026-04-20 13:48:31 +00:00
737d2c7a61 fix(frontend): cap MCP server modal height (#12743)
Modal grew past viewport as args/env rows were added, hiding fields
below (including Add Server button) with no scroll.

- add max-h-[75vh] on modal so it stops expanding
- add overflow-y-auto on tab body so inner content scrolls; header,
  tabs, and footer stay fixed
- add shrink-0 on footer so action buttons can't be squashed

Fixes LE-881
2026-04-17 19:22:45 +00:00
919724523c fix: resolve ghosting issues in preload optimization (#12587)
* fix: enable preload_app option in LangflowApplication configuration

* feat: Integrate Sentry and Prometheus support in worker lifespan

- Initialize Sentry SDK in the worker lifespan if a DSN is provided, allowing for better error tracking.
- Start Prometheus HTTP server if enabled in settings, enhancing monitoring capabilities.
- Added logging for both Sentry initialization and Prometheus server startup to aid in debugging and monitoring.

Refactor existing code to defer Sentry initialization to avoid issues with process forking. This change improves the overall observability of the application.

* fix: Correct Prometheus port validation logic in create_app function

- Updated the condition for validating the Prometheus port to use 'and' instead of 'or', ensuring that the port is both greater than 0 and less than MAX_PORT. This change enhances the reliability of the Prometheus server configuration.

* test: Add unit tests for LangflowApplication.pre_fork method

- Introduced a new test suite for the pre_fork method in LangflowApplication, covering various scenarios including warnings for non-main threads and non-LISTEN TCP connections.
- Implemented tests to ensure proper handling of psutil import errors and unexpected exceptions.
- Verified that garbage collection is always executed during the pre_fork process.
- Added a fake server mock to facilitate testing without requiring a real server instance.

* refactor: Move telemetry service initialization to lifespan context

- Removed the initialization of the telemetry service from the beginning of the `get_lifespan` function and added it within the lifespan context. This change ensures that the telemetry service is only initialized when needed, improving resource management and application performance.

* fix: Enhance Sentry integration with error handling and import checks

- Added error handling for Sentry SDK initialization to log warnings if the SDK is not installed or if initialization fails.
- Updated the `setup_sentry` function to conditionally import `SentryAsgiMiddleware`, logging a warning if the import fails.
- This improves the robustness of the application by preventing crashes related to missing Sentry dependencies and providing clearer logging for debugging.

* fix: Improve Prometheus server error handling in lifespan context

- Enhanced error handling for starting the Prometheus server by adding specific checks for ImportError and OSError.
- Added logging for cases where the Prometheus client is not installed or when the port is already in use, improving clarity for debugging and operational monitoring.
- This change aims to provide more informative feedback during server startup, enhancing the overall robustness of the application.

* refactor: Rename and enhance Sentry middleware integration

- Changed the function name from `setup_sentry` to `add_sentry_middleware` for clarity.
- Updated the middleware setup to ensure Sentry is attached at request time, deferring SDK initialization to the worker lifespan to avoid ghost transactions.
- Adjusted unit tests to mock the new middleware function name, ensuring continued test coverage and functionality.

* feat: Enhance pre_fork method to identify benign threads before forking

- Introduced a new class-level constant `_BENIGN_THREAD_PREFIXES` to define known benign thread prefixes.
- Added a class method `_is_benign_thread` to check if a thread is benign based on its name.
- Updated the `pre_fork` method to log warnings for non-benign threads, improving thread management during the forking process.
- Added a unit test to ensure no warnings are emitted for benign threads, enhancing test coverage for the `pre_fork` method.

* fix: Improve logging for psutil import error handling

- Added a debug log statement to indicate when the psutil library is not installed, enhancing visibility into the application's behavior during TCP connection checks.
- This change aims to provide clearer feedback for debugging and operational monitoring when the psutil dependency is missing.

* fix: Enhance garbage collection handling in pre_fork method

- Wrapped the gc.collect() call in a try-except block to prevent crashes if an exception is raised during garbage collection.
- Added logging to capture any warnings when gc.collect() fails, improving error visibility during the pre-fork process.
- Introduced a new unit test to ensure that the pre_fork method handles gc.collect() exceptions gracefully while still calling gc.freeze().

* docs: LANGFLOW_GUNICORN_PRELOAD environment variable introduced

* refactor: Update application setup for Windows and non-Windows environments

- Introduced conditional logic to handle application setup differently based on the operating system.
- Added a factory pattern for creating the FastAPI application, improving flexibility for non-Windows systems.
- Enhanced error handling to provide clear runtime messages when the application cannot be initialized correctly.

* refactor: move test_server.py to correct unit/base location

The test file exercises langflow.server which belongs to the base package
(src/backend/base/). Moving it to src/backend/tests/unit/base/ follows the
project convention and aligns with the path expected by CI ruff checks.

Made-with: Cursor

* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options (#12313)

* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options

* docs: Add Gunicorn configuration details to environment variables documentation

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add unit test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
2026-04-17 16:53:26 +00:00
351e2589c2 feat: allow users to name flow snapshots when saving (LE-874) (#12742)
Wire up the existing backend `description` field so users can optionally
name snapshots. The name appears in the version sidebar, deployment
modal version list, and canvas preview badge.

- Add SaveVersionDialog modal for entering version name on save
- Extract shared VersionLabel component for consistent display
- Thread previewDescription through versionPreviewStore to overlay badge
2026-04-17 14:35:08 +00:00
f748738c74 fix: MCP Auth Error on restart / swapping auth (#12715)
* fix: MCP Auth Error on restart / swapping auth

* [autofix.ci] apply automated fixes

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

* Make this more robust

* fix: Propagate the api key in PATCH

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-17 04:07:24 +00:00
a484c5676d fix(tests): Skip assistant-panel e2e test when OPENAI_API_KEY is missing (#12747)
add openai validation before start test

Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-16 22:35:35 +00:00
abc6bada8d fix: filter out null objects in mergeNodeTemplates function (#12688)
* fix: filter out null objects in mergeNodeTemplates function

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-16 20:25:47 +00:00
712c724979 fix: respect renamed/deleted default folder across logins (#12746)
get_or_create_default_folder only matched the default folder by its
literal name (DEFAULT_FOLDER_NAME, e.g. "Starter Project"), so if a
user renamed the folder from the UI, the next login/server restart
would recreate a new "Starter Project" alongside the renamed one.

Now, if the user already has at least one folder, return one of their
existing folders instead of forcing a new default back into the UI.
Only create the default folder on true first-time setup (when the user
has zero folders). Legacy folder migration behavior is preserved.

Adds regression tests covering the rename case and the case where the
user keeps a non-default folder instead of "Starter Project".
2026-04-16 19:59:36 +00:00
5991ce1b9a fix: Make sdk env flag idempotent (#12716)
* fix: Make sdk env flag idempotent

* [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-04-15 21:36:22 +00:00
9d84ec14d9 chore: version bump 1.10.0
version bump
langflow: 1.10.0
langflow-base: 0.10.0
lfx: 0.5.0
langflow-sdk: 0.2.0
2026-04-15 16:11:11 -04:00
9aae6de814 ci: increase backend test timeout 2026-04-15 08:41:09 -07:00
da516b7045 Merge release branch 2026-04-14 16:44:45 -07:00
14fe53e150 feat: add customOpenUrl utility for secure external link handling (#12705)
* feat: add customOpenUrl utility for secure external link handling

Extract window.open with noopener/noreferrer into a reusable customization
utility and use it in ProviderConfigurationForm. Also converts to type-only
imports where applicable.

* fix: use existing function

* fix: use custom open tab function
2026-04-14 22:07:49 +00:00
a47f2ad17e fix(frontend): add backdrop blur to test deployment modal (#12704)
Match the stepper modal's stronger backdrop overlay style.
2026-04-14 21:32:39 +00:00
38d142a723 fix: Upgrade cuga to 0.2.20 to resolve playwright dependency conflict (#12703)
* fix: upgrade playwright to 1.58.0 to address Chromium CVEs

- Add playwright>=1.58.0 to override-dependencies in pyproject.toml
- Update uv.lock: playwright 1.49.0 -> 1.58.0, pyee 12.0.0 -> 13.0.1
- Fixes CVE-2026-2313, CVE-2026-2314, CVE-2026-2315, CVE-2026-2319,
  CVE-2026-2321, CVE-2026-2441, CVE-2026-2648, CVE-2026-2649
- Ensures Docker builds download updated Chromium with security patches

* fix: update npm to latest version to address brace-expansion CVE-2026-33750

- Add npm update after Node.js installation in Dockerfile
- Fixes CVE-2026-33750 in system npm's brace-expansion dependency
- System npm had brace-expansion 2.0.2, update gets 5.0.5+
- Low risk change: npm is backward compatible, only affects CLI tool

* revert: remove npm update from Dockerfile

- npm update attempts were causing CI build failures
- Bundled npm has issues but updating it is proving problematic
- Focus on playwright CVE fix which is the primary concern
- brace-expansion CVE-2026-33750 is lower priority (DoS only)

* chore: sync uv.lock files

sync uv.lock files

* fix(mcp): dedupe edges in connect_components (#12701)

* fix(mcp): make add_connection idempotent to avoid duplicate edges

connect_components used to append a new edge unconditionally. Because
the edge id is deterministic from source/target/handles, calling it for
a pair the flow already had wired up (UI-then-MCP, batch retry, or just
a repeat call) produced a second edge with the same id, double-wiring
the flow at runtime.

Before appending, scan the existing edges for one with the same id and
return that instead. Different outputs/inputs between the same pair
still produce distinct ids and remain supported.

* test(mcp): cover dedupe against UI-saved edges, broaden match key

Older Langflow UIs saved edges with an `xy-edge__` id prefix instead of
the current `reactflow__edge-`, so an id-based dedup would miss the
UI-then-MCP case for any flow that came from an older version. Switch
the existence check to a structural one (source, target, sourceHandle
name, targetHandle fieldName) so the same logical connection dedupes
regardless of id format.

Add a fixture-driven test that loads MemoryChatbotNoLLM.json (an
xy-edge-prefixed flow) and replays each connection through
add_connection, asserting the edge count does not grow.

* fix(mcp): validate_flow fast-fails and reports partial errors (#12697)

* fix(mcp): validate_flow fast-fails and reports partial errors

validate_flow polled /monitor/builds for up to 30 seconds waiting for
every component to finish before reporting errors. When a component
fails early (for example a missing required field), downstream
components never run, so the loop waited out the full window and
returned just "Build timed out: N/M components completed" with no
actionable context.

- Short-circuit as soon as any completed build reports valid: false;
  return those errors immediately instead of polling on.
- On timeout, include the errors from the builds that did complete
  plus a component_count so the caller can see progress.
- Extract _collect_build_errors so the poll loop and timeout branch
  share the same error shape.

* fix(mcp): stream validate_flow build inline instead of polling

The previous implementation triggered an async build and polled
/monitor/builds, which depended on FastAPI BackgroundTasks firing the
log_vertex_build calls after the trigger request had returned. Under
ASGI test transport these tasks never run, so /monitor/builds stayed
empty and validate_flow timed out with component_count=0.

Switch to event_delivery=direct so the build streams its events back
inside the same request:

- Drive the build via client.stream_post and aggregate per-vertex
  results from end_vertex events.
- Fast-fail on the first vertex with valid=false, since downstream
  vertices depend on it and would not produce useful information.
- Surface top-level error events as a single flow-level error.
- Replace _collect_build_errors with _extract_vertex_error, which
  reads the structured error payload from the end_vertex outputs.

Update the lfx unit tests to use the streaming shape and tighten the
backend integration test to assert real success now that the build
actually runs end to end under ASGI.

* fix(graph): make end_all_traces_in_context Python 3.10 compatible

The implementation called asyncio.create_task(coro, context=context),
but the context= keyword was added to create_task in Python 3.11. On
3.10 it raised TypeError. The bug was latent because nothing in the
test suite previously consumed a streaming build response far enough
for Starlette to dispatch the post-response BackgroundTasks where this
code lives. The validate_flow streaming change exposes it.

On 3.10, route the create_task call through context.run so the new
Task copies the captured context as its current context, matching the
isolation the 3.11 path provides via the context= kwarg.

Add a regression test that asserts end_all_traces sees the value of a
contextvar set before the context was captured, even after the caller
mutates that var.

* fix: failing wxo list llm test (#12700)

patch service layer and update failing test

* [autofix.ci] apply automated fixes

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

* Try to fix the missing typer import

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
Co-authored-by: Adam Aghili <Adam.Aghili@ibm.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-14 21:23:12 +00:00
07cb95a2c5 fix: add pydantic validation on component assistant (#12706) 2026-04-14 18:34:38 -03:00
449c26bb42 fix: failing wxo list llm test (#12700)
patch service layer and update failing test
2026-04-14 20:20:57 +00:00
0b58f1e532 fix(mcp): validate_flow fast-fails and reports partial errors (#12697)
* fix(mcp): validate_flow fast-fails and reports partial errors

validate_flow polled /monitor/builds for up to 30 seconds waiting for
every component to finish before reporting errors. When a component
fails early (for example a missing required field), downstream
components never run, so the loop waited out the full window and
returned just "Build timed out: N/M components completed" with no
actionable context.

- Short-circuit as soon as any completed build reports valid: false;
  return those errors immediately instead of polling on.
- On timeout, include the errors from the builds that did complete
  plus a component_count so the caller can see progress.
- Extract _collect_build_errors so the poll loop and timeout branch
  share the same error shape.

* fix(mcp): stream validate_flow build inline instead of polling

The previous implementation triggered an async build and polled
/monitor/builds, which depended on FastAPI BackgroundTasks firing the
log_vertex_build calls after the trigger request had returned. Under
ASGI test transport these tasks never run, so /monitor/builds stayed
empty and validate_flow timed out with component_count=0.

Switch to event_delivery=direct so the build streams its events back
inside the same request:

- Drive the build via client.stream_post and aggregate per-vertex
  results from end_vertex events.
- Fast-fail on the first vertex with valid=false, since downstream
  vertices depend on it and would not produce useful information.
- Surface top-level error events as a single flow-level error.
- Replace _collect_build_errors with _extract_vertex_error, which
  reads the structured error payload from the end_vertex outputs.

Update the lfx unit tests to use the streaming shape and tighten the
backend integration test to assert real success now that the build
actually runs end to end under ASGI.

* fix(graph): make end_all_traces_in_context Python 3.10 compatible

The implementation called asyncio.create_task(coro, context=context),
but the context= keyword was added to create_task in Python 3.11. On
3.10 it raised TypeError. The bug was latent because nothing in the
test suite previously consumed a streaming build response far enough
for Starlette to dispatch the post-response BackgroundTasks where this
code lives. The validate_flow streaming change exposes it.

On 3.10, route the create_task call through context.run so the new
Task copies the captured context as its current context, matching the
isolation the 3.11 path provides via the context= kwarg.

Add a regression test that asserts end_all_traces sees the value of a
contextvar set before the context was captured, even after the caller
mutates that var.
2026-04-14 19:52:34 +00:00
9a198b6953 fix(mcp): dedupe edges in connect_components (#12701)
* fix(mcp): make add_connection idempotent to avoid duplicate edges

connect_components used to append a new edge unconditionally. Because
the edge id is deterministic from source/target/handles, calling it for
a pair the flow already had wired up (UI-then-MCP, batch retry, or just
a repeat call) produced a second edge with the same id, double-wiring
the flow at runtime.

Before appending, scan the existing edges for one with the same id and
return that instead. Different outputs/inputs between the same pair
still produce distinct ids and remain supported.

* test(mcp): cover dedupe against UI-saved edges, broaden match key

Older Langflow UIs saved edges with an `xy-edge__` id prefix instead of
the current `reactflow__edge-`, so an id-based dedup would miss the
UI-then-MCP case for any flow that came from an older version. Switch
the existence check to a structural one (source, target, sourceHandle
name, targetHandle fieldName) so the same logical connection dedupes
regardless of id format.

Add a fixture-driven test that loads MemoryChatbotNoLLM.json (an
xy-edge-prefixed flow) and replays each connection through
add_connection, asserting the edge count does not grow.
2026-04-14 19:48:58 +00:00
67981c6e14 fix: retry on flow execution failure and surface friendly message for weak models (#12699)
* fix assistant retry

* agent suggestions fix
2026-04-14 19:33:23 +00:00
9f1402ed99 fix: remove fictional gpt-5.3 ids and surface helpful message on model_not_found (#12693)
* fix 5.3 model not working

* [autofix.ci] apply automated fixes

* fix ruff style and checker

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-14 19:29:39 +00:00
cecc4a6c28 fix(mcp): expose layout tool as layout_flow to match batch dispatch (#12698)
The Python function layout_flow_tool was advertised to MCP clients
under its raw function name (layout_flow_tool) while the batch tool
map dispatched it under layout_flow. A caller copying tool names from
the MCP tool list could not drive batch without translating the name.

Pass name="layout_flow" to the decorator so the external MCP name
matches the batch dispatch key. The Python symbol stays layout_flow_tool
to avoid a clash with the layout_flow helper imported from
lfx.graph.flow_builder.layout.

Added a test that asserts every batch tool_map key is an actual MCP
tool name, so future drift between the two surfaces gets caught.
2026-04-14 19:20:05 +00:00
9539996a81 feat(deployments): verify wxO credentials against instance API (#12449)
* feat(deployments): verify wxO credentials against instance API

- Probe GET /v1/orchestrate/models after IAM token to validate URL+key
- Add POST /deployments/providers/verify-credentials for connection tests
- Extend unit tests for models probe and WxO client stub

* fix(api): rename verify endpoint and redact 422 request input
Rename the deployment provider credential verification route to /deployments/providers/verify and sanitize RequestValidationError responses so raw request payloads are not echoed back in 422 errors. Update route/tests accordingly, including regression coverage for input redaction.

* revert(api): remove verification endpoint follow-up changes

Keep this PR focused on wxO adapter tenant validation via model-list fetch and drop API-layer validation endpoint and request-redaction changes.

* fix(wxo): resolve list_llms rebase conflict with adapter seam

Keep release default-model merge behavior while preserving fetch_models_adapter usage, and update list_llms expectations in tests.

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Hamza Rashid <hzarashid@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
2026-04-14 19:11:00 +00:00
b96b17e1d0 fix(frontend): improve deployment stepper visibility in light mode (#12696)
* fix(frontend): improve deployment stepper visibility in light mode

Step labels were using text-muted-foreground which is nearly invisible
in light mode. Changed to text-foreground with font-medium for active
steps. Also added overlayClassName prop to DialogContent to allow
per-dialog backdrop customization, used to lighten the deployment
modal backdrop in light mode.

* fix(frontend): add backdrop blur to deployment stepper modal

---------

Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
2026-04-14 19:10:15 +00:00
b65515f351 fix: add uv sync step to SDK version determination job (#12695)
The determine-sdk-version job was missing the 'uv sync' step that creates
the cache, causing the 'Post Setup Environment' step to fail with:
'cache path does not exist on disk'

This matches the pattern used in determine-lfx-version job (line 247) and
the nightly build workflow (line 104-105).

Fixes the SDK build failure in release workflow dry run.
2026-04-14 18:14:51 +00:00
d93892a214 fix(mcp): preserve category when loading component registry (#12694)
load_registry iterated data.values(), discarding the category
group keys from /api/v1/all. Every component ended up with
category: "", which made the components(category=...) MCP tool
filter return empty results for every category.

Iterate data.items() instead and inject the category name onto
each component's entry so search_registry's category filter works.
2026-04-14 18:13:12 +00:00
acbeaf35ad feat: rename deployment /executions endpoint to /runs (#12685)
* refactor: rename deployment executions to runs at the API boundary
Rename the deployment sub-resource from "executions" to "runs" across
the API layer, frontend hooks, and tests. Internal adapter/service code
retains its own execution terminology — only the public-facing surface
changes.
- URL paths: /{deployment_id}/executions -> /{deployment_id}/runs
- Path param: execution_id -> run_id
- Route functions: create_deployment_execution -> create_deployment_run,
  get_deployment_execution -> get_deployment_run
- Schema classes: ExecutionCreateRequest -> RunCreateRequest,
  ExecutionCreateResponse -> RunCreateResponse,
  ExecutionStatusResponse -> RunStatusResponse
- provider_data field: execution_id -> id
- Frontend hooks: usePostDeploymentExecution -> usePostDeploymentRun,
  useGetDeploymentExecution -> useGetDeploymentRun
- Frontend types: DeploymentExecutionRequest -> DeploymentRunRequest,
  DeploymentExecutionResponse -> DeploymentRunResponse

* fix: rename remaining execution references to runs in FE and schema docs
Complete the executions→runs rename at the API layer by updating
local variables, user-facing error strings, test mock names, and
schema docstrings that still referenced "execution".

* make schema for runs to wxo

* harden validation

* clean up stale refs to old /executions endpoint
2026-04-14 17:53:09 +00:00
5486c9a160 chore: add default models to llm list result from wxo adapter (#12686)
* add default models to llm list response from adapter

* comment

* ensure can't duplicate hardcoded

* remove old commented call

---------

Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
2026-04-14 15:44:42 +00:00
7b856e7d22 fix(loop): iterate when only item is connected and render item as a table (#12669)
* fix(loop): drive iteration from item_output when done is not connected

Extract the loop body execution into an idempotent `_iterate` helper
that both `item_output` and `done_output` invoke. On release-1.9.0,
`_should_process_output` only runs outputs whose name is in the
vertex's outgoing edge source names, so `done_output` never ran when
nothing was connected to `done` and the subgraph iteration never
happened.

`_iterate` caches aggregated results and cached exceptions in ctx so
repeat calls (when both outputs are wired) run the subgraph exactly
once and surface the same failure on re-entry. `item_output` is now
async, calls `_iterate` before stopping the item branch, and returns
a Data payload listing the dispatched items so the inspector shows
real content instead of an empty string.

Add regression tests covering the item-only topology, the classic
both-outputs-connected topology, empty input, cached error re-raise,
and per-run ctx isolation.

* [autofix.ci] apply automated fixes

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

* fix(loop): emit item output as DataFrame so UI renders it as a table

Wrapping the iterated rows in a `Data` payload with `count` / `items`
keys caused the Item inspector to render as JSON instead of as a
tabular view. Return the rows as a DataFrame, mirroring the Mock Data
component pattern, so the inspector uses its table renderer.

* feat(loop): emit summary logs for each run

Add start, complete, skipped, and error log entries from `_iterate` so
the component's Logs tab shows a per-run summary with row counts and
total elapsed time. Failures include the elapsed duration and the
error message.

* [autofix.ci] apply automated fixes

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

* fix(loop): address review comments

- Refresh Research Translation Loop starter flow's saved item-output
  schema (JSON -> Table) to match item_output now returning a DataFrame.
- Clarify the test_ctx_isolation_across_runs docstring: production
  rebuilds the graph per request, so ctx is effectively per-run; reusing
  a single Graph instance across runs is intentionally not exercised.
- Update _iterate's docstring to describe the actual ctx scope rather
  than claiming graph-instance-level reset semantics that the framework
  does not provide.

* [autofix.ci] apply automated fixes

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

* fix(loop): stabilize chat output inspection test

* fix(loop): align starter project with item output contract

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-14 15:35:38 +00:00
9029c4b61e fix: Updates the CI workflow to handle known dependency conflicts. (#12691)
* fix: upgrade playwright to 1.58.0 to address Chromium CVEs

- Add playwright>=1.58.0 to override-dependencies in pyproject.toml
- Update uv.lock: playwright 1.49.0 -> 1.58.0, pyee 12.0.0 -> 13.0.1
- Fixes CVE-2026-2313, CVE-2026-2314, CVE-2026-2315, CVE-2026-2319,
  CVE-2026-2321, CVE-2026-2441, CVE-2026-2648, CVE-2026-2649
- Ensures Docker builds download updated Chromium with security patches

* fix: update npm to latest version to address brace-expansion CVE-2026-33750

- Add npm update after Node.js installation in Dockerfile
- Fixes CVE-2026-33750 in system npm's brace-expansion dependency
- System npm had brace-expansion 2.0.2, update gets 5.0.5+
- Low risk change: npm is backward compatible, only affects CLI tool

* revert: remove npm update from Dockerfile

- npm update attempts were causing CI build failures
- Bundled npm has issues but updating it is proving problematic
- Focus on playwright CVE fix which is the primary concern
- brace-expansion CVE-2026-33750 is lower priority (DoS only)

* CI: Filter cuga/playwright dependency conflict in release workflow

- Filter cuga/playwright conflict (we override playwright>=1.58.0 for CVE fixes)
- Still fails CI if other genuine dependency issues are detected
- Applied to both base and main package build steps

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
2026-04-14 15:29:28 +00:00
68a8990a1a feat: validate duplicate tool names at deployment review step (#12675)
* feat: validate duplicate tool names at deployment review step

Add early detection of duplicate WXO tool names before deployment,
preventing users from hitting a 409 error only at deploy time.

- Add `GET /snapshots/check-names` endpoint using SDK's
  `get_drafts_by_names()` for fast server-side lookup
- Add `check_tool_names_exist()` to WXO service adapter
- Show inline validation error with alert banner on the review step
  card when a tool name already exists in the provider
- Block deploy button while duplicate names exist
- Also detect batch duplicates (same name on multiple flows)
- Use `keepPreviousData` to prevent error flicker on re-validation

* fix: update duplicate tool name error message

* refactor: use snapshot list endpoint with name filter instead of dedicated check-names endpoint

Replace the dedicated `/snapshots/check-names` endpoint with a
`provider_snapshot_names` query parameter on the existing
`GET /deployments/snapshots` endpoint. This keeps the API surface
smaller and follows the existing list-with-filter pattern.

* test: add tests for snapshot name filter and duplicate tool name validation

- Add validation on provider_snapshot_names: min_length=1 on Query param,
  strip whitespace and reject empty entries in SnapshotListParams
- Backend unit tests: 5 validation tests + 3 adapter list_snapshots
  name-filter tests + 1 route handler test
- Frontend unit tests: 4 tests for useCheckToolNames hook
- Frontend E2E tests: 3 Playwright tests for duplicate tool name
  detection, unique name acceptance, and error clearing on edit
- Fix step-review unit test mock to include new context fields

* feat: standardize snapshot name filtering to names query param
Rename deployment snapshot filtering from provider_snapshot_names to names across backend and frontend callers.
Add trimmed non-empty + dedup validation, enforce deployment_id/name-filter mutual exclusivity, and normalize WXO snapshot names with expanded unit coverage.

* chore: restore package-lock.json from release-1.9.0

* fix(frontend): limit deployment creation retry to 1

Reduce usePostDeployment mutation retry from default 3 to 1 to avoid
excessive 409 requests when deploying.

* feat(frontend): check duplicate tool names on deployment update

When editing a deployment, pre-existing flows with renamed tool names
are now validated against the provider for duplicates. Previously only
new flows were checked, allowing renamed tools to collide silently.

Expose initialToolNameByFlow from stepper context so step-review can
detect name changes and include them in the provider duplicate check.

* fix(frontend): replace nested button with div role=button in flow list

Outer button element contained inner detach/undo buttons, causing
"button cannot be a descendant of button" console error.

---------

Co-authored-by: Hamza Rashid <hzarashid@gmail.com>
2026-04-14 14:49:55 +00:00
4ddb55b7e4 refactor: design sweep for 1.9.0 release (#12684)
* fix: agent icon color adjustment

* only show environment selector when more that one, adjust spacing

* adjustments to spacing

* fixed context shift

* added space between connections and scrollbar

* content & small updates

* fix: update test for connection panel

* [autofix.ci] apply automated fixes

* conditional padding change

* fix how visibleDeployments is filtered

* updated test

* fix: update deployments content

* updated content

* queryKey matches the API params

* updated approach to avoid extra API calls

* [autofix.ci] apply automated fixes

* add a small space between available connections

* fix: improve memo deps, simplify empty state, harden formatDate, fix test assertions

- Use providers array instead of providers.length in useMemo dep
- Collapse duplicate DeploymentsEmptyState branches
- Add explicit locale and invalid date guard to formatDate
- Fix add-provider-modal test for split text elements
- Add test asserting providerIdsToQuery is decoupled from selection

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Viktor Avelino <viktor.avelino@gmail.com>
Co-authored-by: Viktor Avelino <64113566+viktoravelino@users.noreply.github.com>
2026-04-14 14:26:45 +00:00
d274b7b32a docs: release note typo (#12690)
release-note-typo
2026-04-14 13:59:22 +00:00
f594412171 fix(frontend): auto-populate global variable key-value pairs (#12687)
fix(frontend): auto-populate global variable key-value pairs when creating a connection
updateDetectedEnvVars expected objects with key/global_variable_name
fields, but the backend detection endpoint returns a flat string array
of variable names. Simplified the function to accept string[] directly,
using each name as both the env var key and the global variable binding.
2026-04-14 11:26:19 +00:00
33cb6643f4 feat: enforce unique snapshot to flow version relationship (#12680)
* Enforce unique tool to flow version relationship

* fix: harden deployment snapshot attachment conflict handling
- add typed attachment conflict exceptions in attachment CRUD and map only those conflicts to HTTP 409 in deployment create/update routes
- update snapshot patch flow to bulk-update all attachment rows by provider_snapshot_id, commit inside the guarded block, and keep compensating provider rollback on DB failure
- document race-condition caveats around app-level snapshot/flow-version guards and add targeted warning/info logs for conflict and post-provider DB anomalies
- add/expand unit tests for attachment bulk update behavior, typed conflict exceptions, and snapshot route commit-failure compensation path
- align watsonx conflict-detail wording and corresponding mapper tests for tool/connection/agent duplicate-resource messages

---------

Co-authored-by: Hamza Rashid <hzarashid@gmail.com>
2026-04-14 10:59:46 +00:00
71fbf5ff00 docs: cut version 1.9.0 (#12681)
* version-1.9.0-docs

* [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-04-14 02:07:01 +00:00
2fa3d1c759 feat: add langflow-sdk support to release workflow (#12679)
* feat: add langflow-sdk build and publish support to release workflow

Add support for building and publishing the langflow-sdk package in the
release workflow, mirroring the nightly build support added in #12491.

Changes:
- Add `release_sdk` workflow input for controlling SDK release
- Add `determine-sdk-version` job with first-release PyPI handling
- Add `build-sdk` job with version verification and import testing
- Add `publish-sdk` job that publishes SDK to PyPI before LFX
- Update `build-lfx` to download SDK artifact and test both wheels together
- Update `build-base` and `build-main` to use SDK wheel as find-links
- Pass SDK artifact to cross-platform tests
- Add validation: releasing LFX requires releasing SDK
- Add pre-release version handling for SDK and LFX's SDK dependency

* Update release.yml

* Update release.yml

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-04-14 01:43:25 +00:00
389b11b884 docs: update wxo signup link (#12683)
Update wxo signup link:
2026-04-13 22:04:24 -04:00
40c5973292 fix: Allow >= specifications in dependencies (#12682)
* fix: Allow >= specifications in dependencies

* [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-04-13 21:35:21 -04:00
19df4606ae chore: update deps (#12657)
* chore: update deps

update deps due to sec vuln

* chore: langchain-core>=1.2.28

langchain-core>=1.2.28

* chore: update pypdf

pypdf 6.10

* chore: pyarrow, openai, litellm, mem0ai, toolguard

picomatch, pyarrow, openai, litellm, mem0ai, toolguard

* chore: override litellm

* chore: match base uv.lock to root

* chore: update tool.uv position

update tool.uv position

* chore: langflow-base[complete]>=0.9.0

langflow-base[complete]>=0.9.0

* chore: revert pyarrow

revert pyarrow

* chore: revert "lfx~=0.4.0",

* chore: lfx white space

* chore: remove allow-direct-references = true

* chore: uv lock --upgrade after merge
2026-04-13 23:23:37 +00:00
26c415056c feat: add SHA-256 hash-based API key lookup (#12597)
* feat: add SHA-256 hash-based API key lookup

Replace the O(n) full table scan in check_key with an indexed
SHA-256 hash column for O(1) lookup. Legacy keys without a hash
fall back to decrypt-and-compare and get their hash backfilled
on first successful match.

- Add api_key_hash column to ApiKey model (nullable, indexed)
- Hash stored at key creation time
- Migration adds column and backfills hashes for existing keys
- Fail closed when multiple keys share the same hash
- Remove unused fernet_obj parameter from decrypt_api_key

* fix(migration): skip hashing duplicate-plaintext API keys

Two-pass backfill: decrypt all rows, group by plaintext, then hash only
unique-plaintext rows. Duplicate-plaintext groups are left with NULL
hash so the runtime fast-path lookup cannot return multiple matches and
fail closed. The runtime slow-path matches and backfills exactly one
row per group on first use; remaining NULL rows become harmless
orphans.

Logs counts only (no key IDs or plaintexts) to avoid leaking
operational info via deployment logs.

Extracts _backfill_hashes helper for testing; adds 9 unit tests.

---------

Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-13 23:06:44 +00:00
83acc4143d docs: mcp client and other changes (#12627)
* tools-component

* tools-partial-and-release-notes

* mcp-astra-changes

* tutorial-changes

* client-page-and-release-notes

* langflow-mcp-client-for-coding-agents

* fix-links

* Apply suggestions from code review

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

* include-explanation-for-step-4

---------

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>
2026-04-13 22:28:05 +00:00
a425540bdb docs: add flow versioning (#12634)
* add-flow-versioning-and-release-note

* add-flow-db-location
2026-04-13 22:20:05 +00:00
17ab88fca7 fix: fix base64 padding bug and empty Fernet error messages (#12595)
Extract shared `add_base64_padding` and `ensure_fernet_key` functions
to eliminate duplicated key derivation logic between AuthService and
auth/utils.py.

Fix `_add_padding` adding 4 `=` characters instead of 0 when key length
is a multiple of 4, which broke Fernet key derivation for 44-char keys.

Change `%s` to `%r` in decryption failure logs so InvalidToken exceptions
(which have no message string) render as `InvalidToken()` instead of
empty strings.
2026-04-13 21:18:29 +00:00
95d4c94ef9 fix: upgrade playwright to 1.58.0 to address Chromium CVEs (#12668)
* fix: upgrade playwright to 1.58.0 to address Chromium CVEs

- Add playwright>=1.58.0 to override-dependencies in pyproject.toml
- Update uv.lock: playwright 1.49.0 -> 1.58.0, pyee 12.0.0 -> 13.0.1
- Fixes CVE-2026-2313, CVE-2026-2314, CVE-2026-2315, CVE-2026-2319,
  CVE-2026-2321, CVE-2026-2441, CVE-2026-2648, CVE-2026-2649
- Ensures Docker builds download updated Chromium with security patches

* fix: update npm to latest version to address brace-expansion CVE-2026-33750

- Add npm update after Node.js installation in Dockerfile
- Fixes CVE-2026-33750 in system npm's brace-expansion dependency
- System npm had brace-expansion 2.0.2, update gets 5.0.5+
- Low risk change: npm is backward compatible, only affects CLI tool

* revert: remove npm update from Dockerfile

- npm update attempts were causing CI build failures
- Bundled npm has issues but updating it is proving problematic
- Focus on playwright CVE fix which is the primary concern
- brace-expansion CVE-2026-33750 is lower priority (DoS only)

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
2026-04-13 21:04:34 +00:00
42e84d0fdf fix: resolve race condition in test_component_logging for Python 3.13 (#12676)
Replace queue.get_nowait() with asyncio.wait_for(queue.get(), timeout=5.0)
to properly wait for async events in the queue, preventing QueueEmpty errors
in Python 3.13.

Fixes flaky test failure in release workflow.
2026-04-13 18:28:04 -04:00