1414 description : " Name of the base package artifact"
1515 required : true
1616 type : string
17- core-artifact-name :
18- description : " Name of the langflow-core package artifact"
19- required : false
20- type : string
2117 main-artifact-name :
2218 description : " Name of the main package artifact"
2319 required : true
@@ -262,13 +258,6 @@ jobs:
262258 name : ${{ inputs.base-artifact-name || needs.build-if-needed.outputs.base-artifact-name || 'adhoc-dist-base' }}
263259 path : ./base-dist
264260
265- - name : Download langflow-core package artifact
266- if : steps.install-method.outputs.method == 'wheel' && inputs.core-artifact-name != ''
267- uses : actions/download-artifact@v7
268- with :
269- name : ${{ inputs.core-artifact-name }}
270- path : ./core-dist
271-
272261 - name : Download main package artifact
273262 if : steps.install-method.outputs.method == 'wheel'
274263 uses : actions/download-artifact@v7
@@ -302,7 +291,7 @@ jobs:
302291
303292 refresh_find_links() {
304293 FIND_LINKS=()
305- for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./core-dist ./ bundles-dist; do
294+ for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./bundles-dist; do
306295 if [ -d "$wheel_dir" ]; then
307296 FIND_LINKS+=(--find-links "$wheel_dir")
308297 fi
@@ -336,11 +325,6 @@ jobs:
336325 BASE_WHEELS=(./base-dist/*.whl)
337326 install_wheels "base" "${BASE_WHEELS[@]}"
338327
339- CORE_WHEELS=(./core-dist/*.whl)
340- if [ ${#CORE_WHEELS[@]} -gt 0 ]; then
341- install_wheels "core" "${CORE_WHEELS[@]}"
342- fi
343-
344328 if [ -d ./bundles-dist ]; then
345329 BUNDLE_WHEELS=(./bundles-dist/*.whl)
346330 install_wheels "bundle" "${BUNDLE_WHEELS[@]}"
@@ -576,13 +560,6 @@ jobs:
576560 name : ${{ inputs.base-artifact-name || needs.build-if-needed.outputs.base-artifact-name || 'adhoc-dist-base' }}
577561 path : ./base-dist
578562
579- - name : Download langflow-core package artifact
580- if : steps.install-method.outputs.method == 'wheel' && inputs.core-artifact-name != ''
581- uses : actions/download-artifact@v7
582- with :
583- name : ${{ inputs.core-artifact-name }}
584- path : ./core-dist
585-
586563 - name : Download main package artifact
587564 if : steps.install-method.outputs.method == 'wheel'
588565 uses : actions/download-artifact@v7
@@ -616,7 +593,7 @@ jobs:
616593
617594 refresh_find_links() {
618595 FIND_LINKS=()
619- for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./core-dist ./ bundles-dist; do
596+ for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./bundles-dist; do
620597 if [ -d "$wheel_dir" ]; then
621598 FIND_LINKS+=(--find-links "$wheel_dir")
622599 fi
@@ -646,11 +623,6 @@ jobs:
646623 BASE_WHEELS=(./base-dist/*.whl)
647624 install_wheels "base" "${BASE_WHEELS[@]}"
648625
649- CORE_WHEELS=(./core-dist/*.whl)
650- if [ ${#CORE_WHEELS[@]} -gt 0 ]; then
651- install_wheels "core" "${CORE_WHEELS[@]}"
652- fi
653-
654626 if [ -d ./bundles-dist ]; then
655627 BUNDLE_WHEELS=(./bundles-dist/*.whl)
656628 install_wheels "bundle" "${BUNDLE_WHEELS[@]}"
@@ -673,12 +645,12 @@ jobs:
673645 run : |
674646 # Expose the local wheel dirs so dependency re-resolution (the
675647 # pre-release path below drops --no-deps) can find the local
676- # pre-release lfx/base/core wheels. Without these, uv re-resolves
648+ # pre-release SDK/LFX/base wheels. Without these, uv re-resolves
677649 # langflow-base's `lfx>=X.Y.Zrc0` constraint against PyPI only, where
678650 # the matching rc/dev wheel is not published yet, and fails with
679651 # "No solution found when resolving dependencies".
680652 FIND_LINKS=()
681- for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./core-dist ./ bundles-dist; do
653+ for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./bundles-dist; do
682654 if [ -d "$wheel_dir" ]; then
683655 FIND_LINKS+=(--find-links "$wheel_dir")
684656 fi
@@ -734,24 +706,19 @@ jobs:
734706 fi
735707 uv pip install --force-reinstall $NO_DEPS --prerelease=if-necessary-or-explicit "${FIND_LINKS[@]}" --python ./test-env/Scripts/python.exe "${INSTALL_WHEELS[@]}"
736708 fi
737-
738- CORE_WHEEL=$(find ./core-dist -name "*.whl" -type f 2>/dev/null | head -1 || true)
739- if [ -n "$CORE_WHEEL" ]; then
740- uv pip install --force-reinstall --no-deps --python ./test-env/Scripts/python.exe "$CORE_WHEEL"
741- fi
742709 shell : bash
743710
744711 - name : Force reinstall local wheels to prevent downgrades (Unix)
745712 if : steps.install-method.outputs.method == 'wheel' && matrix.os != 'windows'
746713 run : |
747714 # Expose the local wheel dirs so dependency re-resolution (the
748715 # pre-release path below drops --no-deps) can find the local
749- # pre-release lfx/base/core wheels. Without these, uv re-resolves
716+ # pre-release SDK/LFX/base wheels. Without these, uv re-resolves
750717 # langflow-base's `lfx>=X.Y.Zrc0` constraint against PyPI only, where
751718 # the matching rc/dev wheel is not published yet, and fails with
752719 # "No solution found when resolving dependencies".
753720 FIND_LINKS=()
754- for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./core-dist ./ bundles-dist; do
721+ for wheel_dir in ./sdk-dist ./lfx-dist ./base-dist ./bundles-dist; do
755722 if [ -d "$wheel_dir" ]; then
756723 FIND_LINKS+=(--find-links "$wheel_dir")
757724 fi
@@ -807,11 +774,6 @@ jobs:
807774 fi
808775 uv pip install --force-reinstall $NO_DEPS --prerelease=if-necessary-or-explicit "${FIND_LINKS[@]}" --python ./test-env/bin/python "${INSTALL_WHEELS[@]}"
809776 fi
810-
811- CORE_WHEEL=$(find ./core-dist -name "*.whl" -type f 2>/dev/null | head -1 || true)
812- if [ -n "$CORE_WHEEL" ]; then
813- uv pip install --force-reinstall --no-deps --python ./test-env/bin/python "$CORE_WHEEL"
814- fi
815777 shell : bash
816778
817779 # PyPI installation steps
@@ -939,14 +901,17 @@ jobs:
939901 "
940902 shell : bash
941903
942- test-core -runtime :
943- name : Core Runtime Wheel - Linux Python 3.14
904+ test-base -runtime :
905+ name : Base Distribution Wheel - Linux Python 3.14
944906 needs : [build-if-needed]
945907 if : |
946908 always() &&
947909 inputs.langflow-version == '' &&
948- (needs.build-if-needed.result == 'success' || needs.build-if-needed.result == 'skipped')
910+ (needs.build-if-needed.result == 'success' || needs.build-if-needed.result == 'skipped') &&
911+ (inputs.base-artifact-name != '' || needs.build-if-needed.outputs.base-artifact-name != '')
949912 runs-on : ubuntu-latest
913+ permissions :
914+ contents : read
950915 steps :
951916 - name : Setup UV
952917 uses : astral-sh/setup-uv@v6
@@ -973,7 +938,7 @@ jobs:
973938 name : ${{ inputs.base-artifact-name || needs.build-if-needed.outputs.base-artifact-name || 'adhoc-dist-base' }}
974939 path : ./base-dist
975940
976- - name : Install only core wheels
941+ - name : Install only the base dependency chain
977942 shell : bash
978943 run : |
979944 set -euo pipefail
@@ -982,49 +947,56 @@ jobs:
982947 SDK_WHEELS=(./sdk-dist/*.whl)
983948 LFX_WHEELS=(./lfx-dist/*.whl)
984949 BASE_WHEELS=(./base-dist/*.whl)
985- [ ${#LFX_WHEELS [@]} -eq 1 ] || { echo "Expected one LFX wheel, found ${#LFX_WHEELS[@]} "; exit 1; }
986- [ ${#BASE_WHEELS [@]} -eq 1 ] || { echo "Expected one base wheel, found ${#BASE_WHEELS[@]} "; exit 1; }
987- [ ${#SDK_WHEELS [@]} -le 1 ] || { echo "Expected at most one SDK wheel, found ${#SDK_WHEELS[@]} "; exit 1; }
950+ [ ${#SDK_WHEELS [@]} -le 1 ] || { echo "Expected at most one SDK wheel "; exit 1; }
951+ [ ${#LFX_WHEELS [@]} -le 1 ] || { echo "Expected at most one LFX wheel "; exit 1; }
952+ [ ${#BASE_WHEELS [@]} -eq 1 ] || { echo "Expected one base wheel"; exit 1; }
988953
989- FIND_LINKS=(--find-links ./lfx-dist --find-links ./base-dist)
990- if [ ${#SDK_WHEELS[@]} -eq 1 ]; then
991- FIND_LINKS+=(--find-links ./sdk-dist)
992- fi
993-
994- uv venv core-test-env --seed --python 3.14
995- uv pip install --python core-test-env/bin/python \
954+ FIND_LINKS=()
955+ for directory in sdk-dist lfx-dist base-dist; do
956+ if [ -d "$directory" ]; then
957+ FIND_LINKS+=(--find-links "./$directory")
958+ fi
959+ done
960+ uv venv base-test-env --seed --python 3.14
961+ uv pip install --python base-test-env/bin/python \
996962 --prerelease=if-necessary-or-explicit \
997963 "${FIND_LINKS[@]}" "${SDK_WHEELS[@]}" "${LFX_WHEELS[@]}" "${BASE_WHEELS[0]}"
998- uv pip check --python core -test-env/bin/python
964+ uv pip check --python base -test-env/bin/python
999965
1000- - name : Verify core boundary and indexed imports
966+ - name : Verify base boundary and indexed imports
1001967 shell : bash
1002968 run : |
1003- core -test-env/bin/python - <<'PY'
969+ base -test-env/bin/python - <<'PY'
1004970 import importlib
1005971 import re
1006- from importlib.metadata import distributions, entry_points
972+ import sys
973+ from importlib.metadata import distributions, entry_points, metadata, requires
974+ from pathlib import Path
1007975
1008976 from lfx.interface.components import _read_component_index
1009977
1010978 normalize = lambda value: re.sub(r"[-_.]+", "-", value).lower()
1011979 installed = {normalize(dist.metadata["Name"]) for dist in distributions() if dist.metadata["Name"]}
1012980 assert {"langflow-base", "langflow-sdk", "lfx"} <= installed, installed
1013- assert "langflow" not in installed, installed
1014- extensions = sorted( name for name in installed if name.startswith("lfx-"))
1015- assert not extensions , f"Extension distributions installed : {extensions }"
981+ forbidden = installed & { "langflow", "langflow-core", "torch", "torchvision"}
982+ forbidden |= { name for name in installed if name.startswith("lfx-")}
983+ assert not forbidden , f"Forbidden base distributions : {sorted(forbidden) }"
1016984 for group in ("lfx.bundles", "langflow.extensions", "langflow.plugins"):
1017985 assert not entry_points(group=group), f"Extension entry points registered in {group}"
1018986
987+ scripts = Path(sys.executable).parent
988+ assert (scripts / "langflow").is_file()
989+ assert not (scripts / "langflow-base").exists()
990+ provided_extras = set(metadata("langflow-base").get_all("Provides-Extra", []))
991+ assert {"complete", "all"} <= provided_extras
992+ compatibility_requirements = [
993+ requirement for requirement in (requires("langflow-base") or [])
994+ if 'extra == "complete"' in requirement or 'extra == "all"' in requirement
995+ ]
996+ assert not compatibility_requirements, compatibility_requirements
997+
1019998 index = _read_component_index()
1020999 assert index is not None, "Installed LFX rejected its bundled component index"
1021- expected_metadata = {
1022- "num_modules": len(index["entries"]),
1023- "num_components": sum(len(components) for _, components in index["entries"]),
1024- }
1025- assert index["metadata"] == expected_metadata, (
1026- f"Unexpected component index metadata: {index['metadata']} != {expected_metadata}"
1027- )
10281000 for category, components in index["entries"]:
10291001 for component_name, component in components.items():
10301002 module_path = component.get("metadata", {}).get("module")
@@ -1033,7 +1005,7 @@ jobs:
10331005 getattr(importlib.import_module(module_name), class_name)
10341006 PY
10351007
1036- - name : Boot installed core server
1008+ - name : Boot installed base server
10371009 shell : bash
10381010 timeout-minutes : 5
10391011 run : |
@@ -1045,7 +1017,7 @@ jobs:
10451017 export DO_NOT_TRACK=true
10461018 LOG_FILE="$RUNTIME_DIR/server.log"
10471019
1048- core -test-env/bin/langflow-base run \
1020+ base -test-env/bin/langflow run \
10491021 --host 127.0.0.1 --port 7861 --backend-only --workers 1 >"$LOG_FILE" 2>&1 &
10501022 SERVER_PID=$!
10511023 cleanup() {
@@ -1071,52 +1043,40 @@ jobs:
10711043 exit 1
10721044 fi
10731045
1074- curl -fsS -c "$RUNTIME_DIR/cookies" http://127.0.0.1:7861/api/v1/auto_login >/dev/null
1075- curl -fsS --compressed -b "$RUNTIME_DIR/cookies" \
1076- http://127.0.0.1:7861/api/v1/all >"$RUNTIME_DIR/catalog.json"
1077- core-test-env/bin/python - "$RUNTIME_DIR/catalog.json" <<'PY'
1078- import json
1079- import sys
1080- from pathlib import Path
1081-
1082- from lfx.interface.components import _read_component_index
1083-
1084- catalog = json.loads(Path(sys.argv[1]).read_text())
1085- catalog.pop("component_display_names", None)
1086- index = _read_component_index()
1087- assert index is not None, "Installed LFX rejected its bundled component index"
1088- actual_metadata = {
1089- "num_modules": len(catalog),
1090- "num_components": sum(len(components) for components in catalog.values()),
1091- }
1092- assert actual_metadata == index["metadata"], (
1093- f"Unexpected API catalog metadata: {actual_metadata} != {index['metadata']}"
1094- )
1095- PY
1096-
10971046 test-summary :
10981047 name : Cross-Platform Test Summary
1099- needs : [test-installation-stable, test-installation-experimental, test-core -runtime]
1048+ needs : [build-if-needed, test-installation-stable, test-installation-experimental, test-base -runtime]
11001049 runs-on : ubuntu-latest
11011050 if : always()
11021051 steps :
11031052 - name : Check test results
11041053 env :
11051054 LANGFLOW_VERSION : ${{ inputs.langflow-version }}
1055+ EXPECT_BASE_RUNTIME : ${{ inputs.base-artifact-name != '' || needs.build-if-needed.outputs.base-artifact-name != '' }}
11061056 run : |
11071057 stable_result="${{ needs.test-installation-stable.result }}"
11081058 experimental_result="${{ needs.test-installation-experimental.result }}"
1109- core_result ="${{ needs.test-core -runtime.result }}"
1059+ base_result ="${{ needs.test-base -runtime.result }}"
11101060
11111061 echo "Stable platforms result: $stable_result"
11121062 echo "Experimental platforms result: $experimental_result"
1113- echo "Core runtime result: $core_result"
1063+ echo "Base runtime result: $base_result"
1064+
1065+ if [ "$stable_result" = "skipped" ] && [ "$base_result" = "skipped" ]; then
1066+ echo "✅ No Langflow application distribution was selected; package build tests passed"
1067+ exit 0
1068+ fi
11141069
1115- if [ "${{ inputs.langflow-version }}" = " " ] && [ "$core_result " != "success" ]; then
1116- echo "❌ Core runtime wheel test failed: $core_result "
1070+ if [ "$EXPECT_BASE_RUNTIME" = "true" ] && [ -z "$LANGFLOW_VERSION " ] && [ "$base_result " != "success" ]; then
1071+ echo "❌ Base runtime wheel test failed: $base_result "
11171072 exit 1
11181073 fi
11191074
1075+ if [ "$stable_result" = "skipped" ] && [ "$base_result" = "success" ]; then
1076+ echo "✅ Base-only wheel installation tests passed"
1077+ exit 0
1078+ fi
1079+
11201080 # Stable platforms must succeed, experimental can fail
11211081 if [ "$stable_result" = "success" ]; then
11221082 if [ "$experimental_result" = "success" ]; then
0 commit comments