Skip to content

feat: ship langflow-core for 1.11#14099

Closed
erichare wants to merge 6 commits into
langflow-ai:release-1.11.0from
erichare:fix/langflow-core-distribution-1.11
Closed

feat: ship langflow-core for 1.11#14099
erichare wants to merge 6 commits into
langflow-ai:release-1.11.0from
erichare:fix/langflow-core-distribution-1.11

Conversation

@erichare

@erichare erichare commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Depends on

Summary

  • add langflow-core as a sibling PyPI distribution for Langflow 1.11, with the langflow and langflow-core CLIs
  • depend only on langflow-base; do not install lfx-openai, lfx-bundles, lfx-ibm, or any other lfx-* extension distribution
  • expose explicit audio and postgresql extras
  • add a lockfile-resolved core container published under core-* tags
  • wire core build, clean-install testing, PyPI publication, Docker publication, and GitHub release artifacts into the 1.11 release workflow
  • require the matching base release before core publication and prevent GitHub releases from attaching built-but-unpublished artifacts
  • document the core package/image and the intentionally smaller 17-module / 130-component catalog

This branch is stacked on the prerequisite fix set, so those shared changes remain visible until #14098 merges.

Validation

  • 31 passed on Python 3.10 and 31 passed on Python 3.14 — langflow-core distribution contract and version-patch tests
  • 30 passed — release CI-script tests
  • 41 passed — backend version-reporting tests
  • built wheel metadata contains only langflow-base requirements, with audio and postgresql extras
  • clean Python 3.14 wheel install: uv pip check, no langflow or lfx-* distributions, 130/130 indexed imports, and a live 17-module / 130-component API catalog
  • real Podman image build from the repository lock: 308 compatible distributions, no extension entry points or lfx-* distributions, both CLIs, 130/130 imports, and a live /health_check
  • uv lock --check, actionlint, git diff --check, and the full pre-commit suite

Release note

The existing v1.11.0 tag predates both PRs. Publish from a post-merge 1.11.x tag (for example, v1.11.1) unless the release owner deliberately recreates v1.11.0 at the merged release commit.

Summary by CodeRabbit

  • New Features

    • Added Langflow Core, a lightweight distribution with built-in components and no provider bundles.
    • Added the core-latest Docker image and supported Core packaging and release options.
    • Added optional audio and PostgreSQL installation extras for Langflow Core.
  • Bug Fixes

    • Starter projects requiring unavailable components are now skipped automatically.
    • Langflow can load without optional OpenAI or LiteLLM dependencies when those features are unused.
    • Voice mode now accurately reports availability based on its optional dependencies.
  • Documentation

    • Added installation, Docker, custom dependency, and component bundle guidance for Langflow Core.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The PR introduces langflow-core as a bundle-free distribution, adds local and CI packaging workflows, creates a core Docker image, extends release automation, synchronizes LFX metadata, and documents core-only installation and deployment.

Changes

Langflow Core distribution

Layer / File(s) Summary
Core package contract and local packaging
pyproject.toml, src/langflow-core/*, Makefile, src/lfx/tests/unit/test_langflow_core_distribution.py, src/lfx/tests/unit/test_patch_regexes.py
Defines the core package metadata, dependencies, entry points, workspace membership, build/publish targets, version patching, and contract tests.
Core runtime boundaries and optional dependencies
src/backend/base/langflow/..., src/lfx/..., src/backend/tests/unit/*, src/lfx/tests/unit/*
Filters starter projects by available components, resolves core version information, defers optional OpenAI imports, adjusts LFX dependency handling, and validates import and voice-mode boundaries.
LFX version and dependency synchronization
scripts/ci/*, .github/workflows/nightly_build.yml, .github/workflows/release.yml
Adds scripts and tests for pre-release LFX dependency rewriting and component-index version/checksum updates.
Core Docker image build and validation
docker/build_and_push*.Dockerfile, .github/workflows/docker_test.yml
Adds the core multi-stage image, supplies workspace metadata to existing builds, and validates image contents, commands, health, and cleanup.
Core wheel CI artifacts and runtime testing
.github/workflows/cross-platform-test.yml, .github/workflows/ci-scripts-test.yml
Builds and propagates core wheel artifacts, tightens package-test gating, and adds a core-only runtime test.
Core package and Docker release orchestration
.github/workflows/release.yml, .github/workflows/docker-build-v2.yml
Adds core version determination, wheel publication, Docker builds, release ordering, manifest tags, and release assets.
Documentation and workflow support
README.md, docs/docs/..., .github/changes-filter.yaml, .github/workflows/py_autofix.yml, .secrets.baseline
Documents core-only installation and deployment and updates CI path detection, base-branch merging, dependency installation, and baseline metadata.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: fix-index

Suggested reviewers: ogabrielluiz, jordanrfrazier, cristhianzl

🚥 Pre-merge checks | ✅ 6 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Quality And Coverage ⚠️ Warning Most new code is covered, but test_core_component_imports and test_voice_mode_client_scoping leave mutated module state behind, making the suite flaky. Wrap the reimport/reload in try/finally and restore/clear the affected module state (sys.modules.pop, monkeypatch.undo()), so later tests reload cleanly.
Test File Naming And Structure ⚠️ Warning Two new unit tests mutate import state without teardown, so the suite lacks proper isolation/structure despite correct naming. Add cleanup in the OpenAI/module-import tests (restore sys.modules/reload state in finally) so later tests run against a clean module cache.
✅ Passed checks (6 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Test Coverage For New Implementations ✅ Passed PASS: The PR adds real, convention-named tests covering new code paths: core distribution contracts, regex/version patching, component filtering, voice-mode/OpenAI, and CI scripts.
Excessive Mock Usage Warning ✅ Passed Mocks are limited and targeted to external dependency absence and temp-file isolation; most new tests validate real files and contracts, not core logic.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: shipping the new langflow-core distribution for the 1.11 release.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jul 15, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jul 15, 2026
@erichare

Copy link
Copy Markdown
Collaborator Author

Reopened as #14100 from the upstream langflow-ai/langflow branch so maintainers can update it directly. The upstream branch also includes the post-#14098 merge-conflict resolution.

@erichare erichare closed this Jul 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (2)
docker/build_and_push_core.Dockerfile (1)

9-9: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Pin the core image toolchain. In docker/build_and_push_core.Dockerfile:9,89-104, ghcr.io/astral-sh/uv:latest, pip install --upgrade pip, latest-v22.x, and npm@latest make this image non-reproducible and can break builds without a source change; pin the uv image digest, version Node/npm, and verify the Node tarball checksum.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker/build_and_push_core.Dockerfile` at line 9, Pin the toolchain versions
in the core image build: replace ghcr.io/astral-sh/uv:latest with a fixed
digest, use explicit Node and npm versions instead of latest-v22.x and
npm@latest, and verify the downloaded Node tarball against a pinned checksum
before extraction. Update the relevant Dockerfile commands while preserving the
existing installation flow.
src/lfx/tests/unit/services/settings/test_settings_composition.py (1)

27-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer mocking sys.modules instead of overriding builtins.__import__.

Monkeypatching builtins.__import__ can be fragile and is generally considered an excessive mocking technique. Python's import machinery officially supports simulating a missing module by injecting None into sys.modules.

You can significantly simplify this test by using monkeypatch.setitem(sys.modules, ...) for the mocked and missing modules.

♻️ Proposed refactor to use `sys.modules`
-import builtins
+import sys
+import types
 def test_voice_mode_requires_openai_sdk(monkeypatch: pytest.MonkeyPatch) -> None:
     """Installing only the VAD library must not advertise unusable voice mode."""
-    real_import = builtins.__import__
-
-    def import_without_openai(name, *args, **kwargs):
-        if name == "webrtcvad":
-            return object()
-        if name == "openai" or name.startswith("openai."):
-            error_message = "No module named 'openai'"
-            raise ModuleNotFoundError(error_message, name="openai")
-        return real_import(name, *args, **kwargs)
-
-    monkeypatch.setattr(builtins, "__import__", import_without_openai)
+    # Simulate webrtcvad being present
+    monkeypatch.setitem(sys.modules, "webrtcvad", types.ModuleType("webrtcvad"))
+    
+    # Simulate openai being absent
+    monkeypatch.setitem(sys.modules, "openai", None)
 
     assert Settings().voice_mode_available is False
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lfx/tests/unit/services/settings/test_settings_composition.py` around
lines 27 - 44, Refactor test_voice_mode_requires_openai_sdk to stop overriding
builtins.__import__. Use monkeypatch.setitem on sys.modules to simulate the
available webrtcvad module and missing openai module, while preserving the
assertion that Settings().voice_mode_available is False.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/cross-platform-test.yml:
- Line 912: Update the core distribution wheel jobs identified by their names
and corresponding Python-version configuration to use stable Python 3.13 for the
blocking release checks instead of experimental 3.14. Preserve separate Python
3.14 jobs as non-blocking experimental coverage, including the additional
affected job sections.
- Around line 1042-1048: Update the CLI startup logic in the
EXPECT_LANGFLOW_CORE branch to launch and validate the langflow-core entry
point, while separately checking the compatibility langflow alias. Ensure CI
fails when either entry point is missing, and preserve the existing
langflow-base path for non-core runs.
- Around line 1085-1100: Update the “Check test results” step to pass the
langflow-version input through the step’s env configuration, then compare the
resulting LANGFLOW_VERSION variable in the shell condition instead of expanding
inputs.langflow-version directly. Preserve the existing empty-value and
core-result logic.
- Around line 913-914: Add an explicit least-privilege permissions block to the
job using needs: [build-if-needed], granting only actions: read and contents:
read for artifact downloads and local validation.

In @.github/workflows/docker-build-v2.yml:
- Around line 151-163: Update the Docker tag lookups in the release-version
logic, including the `last_released_version` and `released_versions` branches,
to inspect all Docker Hub pagination pages (or query the exact candidate tag)
before deciding whether to publish. Ensure tags beyond the first 100 are
included so existing stable and prerelease `core-$version` tags are detected
before setting the skip state.
- Line 10: Validate the reusable workflow inputs release_type and
version_override before they are interpolated into any version or tag run
blocks, restricting them to the expected release-type values and a safe version
format. Apply the validation in the workflow-call setup before shell execution,
while leaving ref handling limited to checkout logic.

In @.github/workflows/py_autofix.yml:
- Around line 73-75: Quote the merge ref in the workflow’s git merge command,
updating the command adjacent to the existing quoted git fetch so
github.base_ref is passed as one shell argument and unusual branch names cannot
be misparsed.

In @.github/workflows/release.yml:
- Around line 466-486: Update the release-version check around
last_released_version to query the PyPI JSON .releases[$version] entry directly
for every release type, including prereleases. Set skipped=true when that exact
version exists and has published files, while preserving the existing
release-package and skipped=false conditions when it does not.
- Around line 1837-1838: Update the release tag and name expressions to gate
version selection on the successful publish job rather than merely preferring
the nonempty determine-main-version output. Use the published package’s
version—Main for a successful Main publish and Core for a successful Core-only
publish—while preserving inputs.release_tag for non-pre-release releases.
- Line 1709: Update the reusable workflow invocation in release.yml to declare
and pass only DOCKERHUB_USERNAME, DOCKERHUB_TOKEN, and TEMP_GHCR_TOKEN,
replacing secrets: inherit. Ensure docker-build-v2.yml exposes these required
secrets and the caller maps each secret explicitly.

In `@docs/docs/Develop/install-custom-dependencies.mdx`:
- Line 10: Update the sentence describing the `pyproject.toml` location to use
the hyphenated compound adjective “root-level,” leaving the rest of the
documentation unchanged.

In `@src/backend/tests/unit/test_voice_mode_client_scoping.py`:
- Around line 17-31: Update
test_voice_mode_module_import_does_not_require_openai to wrap the mocked-import
reload in try...finally, manually restore the original builtins.__import__ in
the cleanup block, and reload vm again after restoration so its OpenAI symbol
and module state are available to subsequent tests.

In `@src/lfx/tests/unit/components/test_core_component_imports.py`:
- Around line 22-41: Update
test_core_modules_do_not_import_optional_or_test_dependencies to wrap
importlib.import_module(module_name) in a try...finally block, and remove
module_name from sys.modules in the finally cleanup. Preserve the
blocked-dependency import guard while ensuring the partially initialized module
cannot leak into subsequent tests.

---

Nitpick comments:
In `@docker/build_and_push_core.Dockerfile`:
- Line 9: Pin the toolchain versions in the core image build: replace
ghcr.io/astral-sh/uv:latest with a fixed digest, use explicit Node and npm
versions instead of latest-v22.x and npm@latest, and verify the downloaded Node
tarball against a pinned checksum before extraction. Update the relevant
Dockerfile commands while preserving the existing installation flow.

In `@src/lfx/tests/unit/services/settings/test_settings_composition.py`:
- Around line 27-44: Refactor test_voice_mode_requires_openai_sdk to stop
overriding builtins.__import__. Use monkeypatch.setitem on sys.modules to
simulate the available webrtcvad module and missing openai module, while
preserving the assertion that Settings().voice_mode_available is False.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 856a9341-5985-47a7-9f36-195fc4bc892a

📥 Commits

Reviewing files that changed from the base of the PR and between 43f52df and 036549b.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (47)
  • .github/changes-filter.yaml
  • .github/workflows/ci-scripts-test.yml
  • .github/workflows/cross-platform-test.yml
  • .github/workflows/docker-build-v2.yml
  • .github/workflows/docker_test.yml
  • .github/workflows/nightly_build.yml
  • .github/workflows/py_autofix.yml
  • .github/workflows/release.yml
  • .secrets.baseline
  • Makefile
  • README.md
  • docker/build_and_push.Dockerfile
  • docker/build_and_push_base.Dockerfile
  • docker/build_and_push_core.Dockerfile
  • docker/build_and_push_ep.Dockerfile
  • docker/build_and_push_with_extras.Dockerfile
  • docs/docs/Components/components-bundles.mdx
  • docs/docs/Deployment/deployment-docker.mdx
  • docs/docs/Develop/install-custom-dependencies.mdx
  • docs/docs/Get-Started/get-started-installation.mdx
  • pyproject.toml
  • scripts/ci/test_update_lfx_pre_release_dependency.py
  • scripts/ci/test_update_lfx_version.py
  • scripts/ci/update_component_index_version.py
  • scripts/ci/update_lfx_pre_release_dependency.py
  • scripts/ci/update_lfx_version.py
  • src/backend/base/langflow/api/v1/voice_mode.py
  • src/backend/base/langflow/initial_setup/setup.py
  • src/backend/base/langflow/utils/version.py
  • src/backend/base/pyproject.toml
  • src/backend/tests/unit/test_initial_setup.py
  • src/backend/tests/unit/test_voice_mode_client_scoping.py
  • src/backend/tests/unit/utils/test_version.py
  • src/langflow-core/README.md
  • src/langflow-core/pyproject.toml
  • src/langflow-core/src/langflow_core/__init__.py
  • src/lfx/pyproject.toml
  • src/lfx/src/lfx/_assets/component_index.json
  • src/lfx/src/lfx/base/agents/crewai/crew.py
  • src/lfx/src/lfx/components/tools/calculator.py
  • src/lfx/src/lfx/interface/components.py
  • src/lfx/src/lfx/services/settings/base.py
  • src/lfx/tests/unit/components/test_core_component_imports.py
  • src/lfx/tests/unit/services/settings/test_settings_composition.py
  • src/lfx/tests/unit/test_component_index.py
  • src/lfx/tests/unit/test_langflow_core_distribution.py
  • src/lfx/tests/unit/test_patch_regexes.py
👮 Files not reviewed due to content moderation or server errors (7)
  • src/backend/base/langflow/api/v1/voice_mode.py
  • src/lfx/src/lfx/services/settings/base.py
  • src/lfx/src/lfx/base/agents/crewai/crew.py
  • src/lfx/src/lfx/components/tools/calculator.py
  • src/lfx/pyproject.toml
  • src/lfx/src/lfx/_assets/component_index.json
  • src/lfx/src/lfx/interface/components.py

shell: bash

test-core-runtime:
name: Core Distribution Wheel - Linux Python 3.14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not make experimental Python 3.14 the blocking core release gate.

The workflow explicitly treats Python 3.14 as non-blocking elsewhere, but this job makes its dependency compatibility mandatory for release. Run the blocking core check on a stable version such as 3.13; retain 3.14 separately as experimental coverage.

Proposed fix
-    name: Core Distribution Wheel - Linux Python 3.14
+    name: Core Distribution Wheel - Linux Python 3.13
...
-          python-version: "3.14"
+          python-version: "3.13"
...
-          uv venv core-test-env --seed --python 3.14
+          uv venv core-test-env --seed --python 3.13

Also applies to: 923-927, 989-993, 1100-1103

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cross-platform-test.yml at line 912, Update the core
distribution wheel jobs identified by their names and corresponding
Python-version configuration to use stable Python 3.13 for the blocking release
checks instead of experimental 3.14. Preserve separate Python 3.14 jobs as
non-blocking experimental coverage, including the additional affected job
sections.

Comment on lines +913 to +914
needs: [build-if-needed]
if: |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Top-level workflow permissions and job context\n'
rg -n --no-heading '^(permissions:|  test-core-runtime:|    permissions:|    needs:|    if:|    steps:|    uses:|    runs-on:)' .github/workflows/cross-platform-test.yml

printf '\n## Nearby lines around the cited job\n'
sed -n '900,960p' .github/workflows/cross-platform-test.yml

printf '\n## Any explicit permissions blocks in this workflow\n'
rg -n --no-heading '^(\s*)permissions:' .github/workflows/cross-platform-test.yml

Repository: langflow-ai/langflow

Length of output: 3471


🌐 Web query:

GitHub Actions download-artifact permissions actions:read contents:read official documentation

💡 Result:

When using the actions/download-artifact action, the required permissions depend on whether you are downloading artifacts from the current workflow run or from a different context [1][2]. 1. Current Workflow Run: By default, the action does not require a specific token input to download artifacts from the same repository and current workflow run [1][3]. In this scenario, it uses the job's default permissions [4]. 2. Different Workflow Run or Repository: When downloading artifacts from a different repository or a different workflow run, you must provide a github-token input [2][3]. This token must have actions:read permissions on the target repository [2][5]. A Personal Access Token (PAT) with these permissions is typically used for this purpose [4]. Configuration Example: To download an artifact from a different repository or run, include the github-token and relevant identifiers in your workflow YAML: steps: - uses: actions/download-artifact@v4 with: name: my-other-artifact github-token: ${{ secrets.GH_PAT }} # Token with actions:read permissions on the target repo [2][3] repository: owner/repo-name run-id: 123456789 Note that if you are using the GITHUB_TOKEN for tasks within the same repository, ensure your workflow job has the appropriate permissions defined if you are using granular permissions [6]: permissions: actions: read contents: read

Citations:


Restrict this job’s token scope. It only downloads artifacts and runs local validation, so add an explicit least-privilege block; for actions/download-artifact with granular GITHUB_TOKEN permissions, include both actions: read and contents: read.

Proposed fix
  test-core-runtime:
    name: Core Distribution Wheel - Linux Python 3.14
    needs: [build-if-needed]
+   permissions:
+     actions: read
+     contents: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
needs: [build-if-needed]
if: |
needs: [build-if-needed]
permissions:
actions: read
contents: read
if: |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cross-platform-test.yml around lines 913 - 914, Add an
explicit least-privilege permissions block to the job using needs:
[build-if-needed], granting only actions: read and contents: read for artifact
downloads and local validation.

Source: Linters/SAST tools

Comment on lines +1042 to +1048
if [ "$EXPECT_LANGFLOW_CORE" = "true" ]; then
LANGFLOW_CLI=core-test-env/bin/langflow
else
LANGFLOW_CLI=core-test-env/bin/langflow-base
fi
"$LANGFLOW_CLI" run \
--host 127.0.0.1 --port 7861 --backend-only --workers 1 >"$LOG_FILE" 2>&1 &

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Exercise the new langflow-core CLI.

The test currently validates only the compatibility langflow entry point. Boot through langflow-core and separately check the alias so either missing entry point fails CI.

Proposed fix
           if [ "$EXPECT_LANGFLOW_CORE" = "true" ]; then
-            LANGFLOW_CLI=core-test-env/bin/langflow
+            core-test-env/bin/langflow --help >/dev/null
+            LANGFLOW_CLI=core-test-env/bin/langflow-core
           else
             LANGFLOW_CLI=core-test-env/bin/langflow-base
           fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ "$EXPECT_LANGFLOW_CORE" = "true" ]; then
LANGFLOW_CLI=core-test-env/bin/langflow
else
LANGFLOW_CLI=core-test-env/bin/langflow-base
fi
"$LANGFLOW_CLI" run \
--host 127.0.0.1 --port 7861 --backend-only --workers 1 >"$LOG_FILE" 2>&1 &
if [ "$EXPECT_LANGFLOW_CORE" = "true" ]; then
core-test-env/bin/langflow --help >/dev/null
LANGFLOW_CLI=core-test-env/bin/langflow-core
else
LANGFLOW_CLI=core-test-env/bin/langflow-base
fi
"$LANGFLOW_CLI" run \
--host 127.0.0.1 --port 7861 --backend-only --workers 1 >"$LOG_FILE" 2>&1 &
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cross-platform-test.yml around lines 1042 - 1048, Update
the CLI startup logic in the EXPECT_LANGFLOW_CORE branch to launch and validate
the langflow-core entry point, while separately checking the compatibility
langflow alias. Ensure CI fails when either entry point is missing, and preserve
the existing langflow-base path for non-core runs.

Comment on lines 1085 to +1100
- name: Check test results
run: |
stable_result="${{ needs.test-installation-stable.result }}"
experimental_result="${{ needs.test-installation-experimental.result }}"
core_result="${{ needs.test-core-runtime.result }}"

echo "Stable platforms result: $stable_result"
echo "Experimental platforms result: $experimental_result"
echo "Core runtime result: $core_result"

if [ "$stable_result" = "skipped" ] && [ "$core_result" = "skipped" ]; then
echo "✅ No Langflow application distribution was selected; package build tests passed"
exit 0
fi

if [ "${{ inputs.langflow-version }}" = "" ] && [ "$core_result" != "success" ]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant workflow section with line numbers
sed -n '1040,1120p' .github/workflows/cross-platform-test.yml | cat -n

printf '\n---\n'

# Inspect the workflow triggers and input declarations
rg -n "workflow_call|inputs:|langflow-version|on:" .github/workflows/cross-platform-test.yml

printf '\n---\n'

# Show the top of the file for reusable-workflow context
sed -n '1,120p' .github/workflows/cross-platform-test.yml | cat -n

Repository: langflow-ai/langflow

Length of output: 12233


Avoid expanding the workflow input directly in the shell. ${{ inputs.langflow-version }} is user-controlled and can break out of the quoted test; pass it through env and compare "$LANGFLOW_VERSION" instead.

🧰 Tools
🪛 zizmor (1.26.1)

[error] 1100-1100: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cross-platform-test.yml around lines 1085 - 1100, Update
the “Check test results” step to pass the langflow-version input through the
step’s env configuration, then compare the resulting LANGFLOW_VERSION variable
in the shell condition instead of expanding inputs.langflow-version directly.
Preserve the existing empty-value and core-result logic.

Source: Linters/SAST tools

required: true
type: string
description: "Release type. One of 'main', 'main-backend', 'main-frontend', 'main-ep', 'base', 'main-all'."
description: "Release type. One of 'main', 'main-backend', 'main-frontend', 'main-ep', 'base', 'core', 'main-all'."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Identify callers and determine whether inputs can originate from untrusted contexts.
rg -n -C4 'docker-build-v2\.yml|version_override:|release_type:|ref:' .github/workflows

Repository: langflow-ai/langflow

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/docker-build-v2.yml"

printf '\n== Relevant line ranges ==\n'
sed -n '130,170p' "$file"
printf '\n---\n'
sed -n '730,860p' "$file"
printf '\n---\n'
sed -n '1,120p' "$file"

printf '\n== version_override occurrences ==\n'
rg -n -C3 'version_override|release_type|inputs\.ref|inputs\.release_type|inputs\.version_override' "$file"

Repository: langflow-ai/langflow

Length of output: 24575


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/docker-build-v2.yml"

printf '\n== AST outline ==\n'
ast-grep outline "$file" --view expanded

Repository: langflow-ai/langflow

Length of output: 229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/docker-build-v2.yml"

echo "== lines 140-160 =="
sed -n '140,160p' "$file"

echo
echo "== lines 748-850 =="
sed -n '748,850p' "$file"

echo
echo "== workflow_call/workflow_dispatch inputs =="
sed -n '1,60p' "$file"

Repository: langflow-ai/langflow

Length of output: 10326


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'uses:\s+\./\.github/workflows/docker-build-v2\.yml' .github/workflows

Repository: langflow-ai/langflow

Length of output: 4420


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== version_override references ==\n'
rg -n -C2 'version_override' .github/workflows

printf '\n== docker-build-v2 callers with with: blocks ==\n'
rg -n -C4 'uses:\s+\./\.github/workflows/docker-build-v2\.yml|release_type:|version_override:|ref:' .github/workflows/release.yml .github/workflows/release_nightly.yml .github/workflows/*.yml

Repository: langflow-ai/langflow

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' .github/workflows/release.yml

Repository: langflow-ai/langflow

Length of output: 4423


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'release_tag|workflow_dispatch|workflow_call|inputs\.' .github/workflows/release.yml

Repository: langflow-ai/langflow

Length of output: 34638


Validate the reusable workflow inputs before shell interpolation.

release_type and version_override are free-form on workflow_call and are expanded directly inside the version/tag run: blocks. A crafted value can trigger command substitution on release runners; ref only needs checkout handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-build-v2.yml at line 10, Validate the reusable
workflow inputs release_type and version_override before they are interpolated
into any version or tag run blocks, restricting them to the expected
release-type values and a safe version format. Apply the validation in the
workflow-call setup before shell execution, while leaving ref handling limited
to checkout logic.

Sources: Learnings, Linters/SAST tools

pre_release: ${{ inputs.pre_release }}
version_override: ${{ needs.determine-core-version.outputs.version }}
push_to_registry: ${{ !inputs.dry_run }}
secrets: inherit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'workflow_call:|secrets:|secrets\.' .github/workflows/docker-build-v2.yml

Repository: langflow-ai/langflow

Length of output: 4812


🏁 Script executed:

#!/bin/bash
sed -n '1,80p' .github/workflows/docker-build-v2.yml
printf '\n---\n'
sed -n '1685,1725p' .github/workflows/release.yml

Repository: langflow-ai/langflow

Length of output: 4321


🏁 Script executed:

#!/bin/bash
rg -n '\$\{\{ *secrets\.[A-Z0-9_]+ *\}\}' .github/workflows/docker-build-v2.yml
printf '\n---\n'
sed -n '1,45p' .github/workflows/docker-build-v2.yml

Repository: langflow-ai/langflow

Length of output: 3038


Pass only the Docker workflow’s required secrets. docker-build-v2.yml only uses DOCKERHUB_USERNAME, DOCKERHUB_TOKEN, and TEMP_GHCR_TOKEN, so declare those in the reusable workflow and pass them explicitly here instead of secrets: inherit.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 1698-1709: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 1709, Update the reusable workflow
invocation in release.yml to declare and pass only DOCKERHUB_USERNAME,
DOCKERHUB_TOKEN, and TEMP_GHCR_TOKEN, replacing secrets: inherit. Ensure
docker-build-v2.yml exposes these required secrets and the caller maps each
secret explicitly.

Source: Linters/SAST tools

Comment on lines +1837 to +1838
tag: ${{ inputs.pre_release && (needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version) || inputs.release_tag }}
name: ${{ needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Select the release version from the package actually published.

determine-main-version runs even during a Core-only release, so its nonempty output wins both expressions and can create/name the release using the Main version. Gate the choice on the successful publish job instead.

Proposed fix
-          tag: ${{ inputs.pre_release && (needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version) || inputs.release_tag }}
-          name: ${{ needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version }}
+          tag: ${{ inputs.pre_release && (needs.publish-main.result == 'success' && needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version) || inputs.release_tag }}
+          name: ${{ needs.publish-main.result == 'success' && needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tag: ${{ inputs.pre_release && (needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version) || inputs.release_tag }}
name: ${{ needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version }}
tag: ${{ inputs.pre_release && (needs.publish-main.result == 'success' && needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version) || inputs.release_tag }}
name: ${{ needs.publish-main.result == 'success' && needs.determine-main-version.outputs.version || needs.determine-core-version.outputs.version }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 1837 - 1838, Update the release
tag and name expressions to gate version selection on the successful publish job
rather than merely preferring the nonempty determine-main-version output. Use
the published package’s version—Main for a successful Main publish and Core for
a successful Core-only publish—while preserving inputs.release_tag for
non-pre-release releases.

The Langflow codebase uses three packages, each with its own `pyproject.toml` file:
The Langflow codebase uses four primary distributions, each with its own `pyproject.toml` file:

* The `main` package (`langflow`) is managed by the root level `pyproject.toml`, and it includes end-user features and main application code, such as Langchain and OpenAI. The `main` package depends on the `base` package.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use “root-level” here.

The compound adjective should be hyphenated in this user-facing sentence.

🧰 Tools
🪛 LanguageTool

[grammar] ~10-~10: Use a hyphen to join words.
Context: ...kage (langflow) is managed by the root level pyproject.toml, and it includes ...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/Develop/install-custom-dependencies.mdx` at line 10, Update the
sentence describing the `pyproject.toml` location to use the hyphenated compound
adjective “root-level,” leaving the rest of the documentation unchanged.

Source: Linters/SAST tools

Comment on lines +17 to +31
def test_voice_mode_module_import_does_not_require_openai(monkeypatch):
"""The server must boot when the optional OpenAI voice SDK is absent."""
real_import = builtins.__import__

def import_without_openai(name, *args, **kwargs):
if name == "openai" or name.startswith("openai."):
error_message = "No module named 'openai'"
raise ModuleNotFoundError(error_message, name="openai")
return real_import(name, *args, **kwargs)

monkeypatch.setattr(builtins, "__import__", import_without_openai)
vm.__dict__.pop("OpenAI", None)

importlib.reload(vm)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore module state to prevent test isolation failures.

Reloading the vm module while openai is mocked as absent mutates the module in-place within sys.modules. While monkeypatch will restore the __import__ builtin when the test finishes, it will not undo the importlib.reload(vm) mutation. The vm module will remain in a crippled state for the rest of the test session, which can cause downstream tests (like test_get_tts_config_scoped_by_user) to fail if they run subsequently and expect the OpenAI client to be present in the module namespace.

🛠️ Proposed fix

Wrap the reload in a try...finally block. By manually undoing the monkeypatch in finally, you can reload the module one last time to restore it to its original state before the next test runs:

     monkeypatch.setattr(builtins, "__import__", import_without_openai)
     vm.__dict__.pop("OpenAI", None)
 
-    importlib.reload(vm)
+    try:
+        importlib.reload(vm)
+    finally:
+        monkeypatch.undo()
+        importlib.reload(vm)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_voice_mode_module_import_does_not_require_openai(monkeypatch):
"""The server must boot when the optional OpenAI voice SDK is absent."""
real_import = builtins.__import__
def import_without_openai(name, *args, **kwargs):
if name == "openai" or name.startswith("openai."):
error_message = "No module named 'openai'"
raise ModuleNotFoundError(error_message, name="openai")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", import_without_openai)
vm.__dict__.pop("OpenAI", None)
importlib.reload(vm)
def test_voice_mode_module_import_does_not_require_openai(monkeypatch):
"""The server must boot when the optional OpenAI voice SDK is absent."""
real_import = builtins.__import__
def import_without_openai(name, *args, **kwargs):
if name == "openai" or name.startswith("openai."):
error_message = "No module named 'openai'"
raise ModuleNotFoundError(error_message, name="openai")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", import_without_openai)
vm.__dict__.pop("OpenAI", None)
try:
importlib.reload(vm)
finally:
monkeypatch.undo()
importlib.reload(vm)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/tests/unit/test_voice_mode_client_scoping.py` around lines 17 -
31, Update test_voice_mode_module_import_does_not_require_openai to wrap the
mocked-import reload in try...finally, manually restore the original
builtins.__import__ in the cleanup block, and reload vm again after restoration
so its OpenAI symbol and module state are available to subsequent tests.

Comment on lines +22 to +41
def test_core_modules_do_not_import_optional_or_test_dependencies(
monkeypatch: pytest.MonkeyPatch,
module_name: str,
blocked_dependency: str,
) -> None:
"""Core component modules must remain importable in a clean runtime install."""
real_import = builtins.__import__

def guarded_import(name, *args, **kwargs):
if name == blocked_dependency or name.startswith(f"{blocked_dependency}."):
error_message = f"No module named '{blocked_dependency}'"
raise ModuleNotFoundError(error_message, name=blocked_dependency)
return real_import(name, *args, **kwargs)

monkeypatch.setattr(builtins, "__import__", guarded_import)
sys.modules.pop(module_name, None)

importlib.import_module(module_name)


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clean up sys.modules to prevent test isolation failures.

Removing the module from sys.modules and re-importing it while its dependencies are mocked leaves a crippled version of the module cached in sys.modules. Since this mutation isn't tracked or cleaned up by monkeypatch, any subsequent tests in the same process that import this module will receive this crippled version, potentially causing them to crash.

🛠️ Proposed fix

Wrap the import in a try...finally block and pop the crippled module from sys.modules afterward. This forces any subsequent tests to cleanly reload the module from disk using the normal (unmocked) import mechanism:

     monkeypatch.setattr(builtins, "__import__", guarded_import)
     sys.modules.pop(module_name, None)
 
-    importlib.import_module(module_name)
+    try:
+        importlib.import_module(module_name)
+    finally:
+        sys.modules.pop(module_name, None)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_core_modules_do_not_import_optional_or_test_dependencies(
monkeypatch: pytest.MonkeyPatch,
module_name: str,
blocked_dependency: str,
) -> None:
"""Core component modules must remain importable in a clean runtime install."""
real_import = builtins.__import__
def guarded_import(name, *args, **kwargs):
if name == blocked_dependency or name.startswith(f"{blocked_dependency}."):
error_message = f"No module named '{blocked_dependency}'"
raise ModuleNotFoundError(error_message, name=blocked_dependency)
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", guarded_import)
sys.modules.pop(module_name, None)
importlib.import_module(module_name)
def test_core_modules_do_not_import_optional_or_test_dependencies(
monkeypatch: pytest.MonkeyPatch,
module_name: str,
blocked_dependency: str,
) -> None:
"""Core component modules must remain importable in a clean runtime install."""
real_import = builtins.__import__
def guarded_import(name, *args, **kwargs):
if name == blocked_dependency or name.startswith(f"{blocked_dependency}."):
error_message = f"No module named '{blocked_dependency}'"
raise ModuleNotFoundError(error_message, name=blocked_dependency)
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", guarded_import)
sys.modules.pop(module_name, None)
try:
importlib.import_module(module_name)
finally:
sys.modules.pop(module_name, None)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lfx/tests/unit/components/test_core_component_imports.py` around lines 22
- 41, Update test_core_modules_do_not_import_optional_or_test_dependencies to
wrap importlib.import_module(module_name) in a try...finally block, and remove
module_name from sys.modules in the finally cleanup. Preserve the
blocked-dependency import guard while ensuring the partially initialized module
cannot leak into subsequent tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant