feat: ship langflow-core for 1.11#14099
Conversation
WalkthroughThe PR introduces ChangesLangflow Core distribution
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 6 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (6 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (2)
docker/build_and_push_core.Dockerfile (1)
9-9: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winPin 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, andnpm@latestmake this image non-reproducible and can break builds without a source change; pin theuvimage 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 winPrefer mocking
sys.modulesinstead of overridingbuiltins.__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 injectingNoneintosys.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 typesdef 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
⛔ Files ignored due to path filters (1)
uv.lockis 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.baselineMakefileREADME.mddocker/build_and_push.Dockerfiledocker/build_and_push_base.Dockerfiledocker/build_and_push_core.Dockerfiledocker/build_and_push_ep.Dockerfiledocker/build_and_push_with_extras.Dockerfiledocs/docs/Components/components-bundles.mdxdocs/docs/Deployment/deployment-docker.mdxdocs/docs/Develop/install-custom-dependencies.mdxdocs/docs/Get-Started/get-started-installation.mdxpyproject.tomlscripts/ci/test_update_lfx_pre_release_dependency.pyscripts/ci/test_update_lfx_version.pyscripts/ci/update_component_index_version.pyscripts/ci/update_lfx_pre_release_dependency.pyscripts/ci/update_lfx_version.pysrc/backend/base/langflow/api/v1/voice_mode.pysrc/backend/base/langflow/initial_setup/setup.pysrc/backend/base/langflow/utils/version.pysrc/backend/base/pyproject.tomlsrc/backend/tests/unit/test_initial_setup.pysrc/backend/tests/unit/test_voice_mode_client_scoping.pysrc/backend/tests/unit/utils/test_version.pysrc/langflow-core/README.mdsrc/langflow-core/pyproject.tomlsrc/langflow-core/src/langflow_core/__init__.pysrc/lfx/pyproject.tomlsrc/lfx/src/lfx/_assets/component_index.jsonsrc/lfx/src/lfx/base/agents/crewai/crew.pysrc/lfx/src/lfx/components/tools/calculator.pysrc/lfx/src/lfx/interface/components.pysrc/lfx/src/lfx/services/settings/base.pysrc/lfx/tests/unit/components/test_core_component_imports.pysrc/lfx/tests/unit/services/settings/test_settings_composition.pysrc/lfx/tests/unit/test_component_index.pysrc/lfx/tests/unit/test_langflow_core_distribution.pysrc/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 |
There was a problem hiding this comment.
🩺 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.13Also 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.
| needs: [build-if-needed] | ||
| if: | |
There was a problem hiding this comment.
🔒 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.ymlRepository: 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:
- 1: https://github.com/actions/download-artifact
- 2: https://github.com/actions/download-artifact/blob/v4.1.0/README.md
- 3: https://github.com/actions/download-artifact/blob/ada9446619b84dd8a557aaaec3b79b58c4986cdf/README.md
- 4: https://deepwiki.com/actions/download-artifact/2.2-artifact-lifecycle
- 5: https://github.com/actions/toolkit/blob/main/packages/artifact/README.md
- 6: https://docs.github.com/actions/reference/authentication-in-a-workflow
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.
| 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
| 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 & |
There was a problem hiding this comment.
🎯 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.
| 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.
| - 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 |
There was a problem hiding this comment.
🔒 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 -nRepository: 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'." |
There was a problem hiding this comment.
🔒 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/workflowsRepository: 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 expandedRepository: 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/workflowsRepository: 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/*.ymlRepository: langflow-ai/langflow
Length of output: 50377
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,120p' .github/workflows/release.ymlRepository: 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.ymlRepository: 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 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C3 'workflow_call:|secrets:|secrets\.' .github/workflows/docker-build-v2.ymlRepository: 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.ymlRepository: 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.ymlRepository: 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
| 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 }} |
There was a problem hiding this comment.
🗄️ 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.
| 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. |
There was a problem hiding this comment.
📐 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
| 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) | ||
|
|
There was a problem hiding this comment.
🎯 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.
| 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.
| 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) | ||
|
|
||
|
|
There was a problem hiding this comment.
🎯 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.
| 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.
Depends on
Summary
langflow-coreas a sibling PyPI distribution for Langflow 1.11, with thelangflowandlangflow-coreCLIslangflow-base; do not installlfx-openai,lfx-bundles,lfx-ibm, or any otherlfx-*extension distributionaudioandpostgresqlextrascore-*tagsThis branch is stacked on the prerequisite fix set, so those shared changes remain visible until #14098 merges.
Validation
31 passedon Python 3.10 and31 passedon Python 3.14 — langflow-core distribution contract and version-patch tests30 passed— release CI-script tests41 passed— backend version-reporting testslangflow-baserequirements, withaudioandpostgresqlextrasuv pip check, nolangfloworlfx-*distributions, 130/130 indexed imports, and a live 17-module / 130-component API cataloglfx-*distributions, both CLIs, 130/130 imports, and a live/health_checkuv lock --check, actionlint,git diff --check, and the full pre-commit suiteRelease note
The existing
v1.11.0tag predates both PRs. Publish from a post-merge 1.11.x tag (for example,v1.11.1) unless the release owner deliberately recreatesv1.11.0at the merged release commit.Summary by CodeRabbit
New Features
core-latestDocker image and supported Core packaging and release options.Bug Fixes
Documentation