From abd772f78048a54d703674b75e629feecf4d73df Mon Sep 17 00:00:00 2001 From: Eric Hare Date: Thu, 2 Apr 2026 22:59:09 -0700 Subject: [PATCH] fix: Build and install the langflow-sdk for lfx (fixes nightly) (#12481) * fix: Build and install the langflow-sdk for lfx * Publish sdk as a nightly * Update ci.yml * Update python_test.yml * Update ci.yml --- .github/workflows/ci.yml | 2 +- .github/workflows/cross-platform-test.yml | 108 +++++++++++++++++++++- .github/workflows/nightly_build.yml | 17 +++- .github/workflows/python_test.yml | 2 +- .github/workflows/release_nightly.yml | 66 ++++++++++++- .secrets.baseline | 6 +- Makefile | 42 +++++++++ scripts/ci/sdk_nightly_tag.py | 63 +++++++++++++ scripts/ci/update_lfx_version.py | 31 ++++++- scripts/ci/update_pyproject_name.py | 3 + scripts/ci/update_sdk_version.py | 40 ++++++++ src/sdk/Makefile | 68 ++++++++++++++ 12 files changed, 429 insertions(+), 19 deletions(-) create mode 100644 scripts/ci/sdk_nightly_tag.py create mode 100644 scripts/ci/update_sdk_version.py create mode 100644 src/sdk/Makefile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e894cd5f4..a15c6b8308 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -426,7 +426,7 @@ jobs: DOCS_ONLY: ${{ needs.path-filter.outputs.docs-only }} EXIT_CODE: ${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || - (needs.check-nightly-status.outputs.should-proceed != 'true' && github.event_name != 'workflow_dispatch' && needs.path-filter.outputs.docs-only != 'true')) + (needs.check-nightly-status.outputs.should-proceed != 'true' && github.event_name != 'workflow_dispatch' && github.event_name != 'merge_group' && needs.path-filter.outputs.docs-only != 'true' && !contains(github.event.pull_request.labels.*.name, 'skip-nightly-check'))) && '1' || '0' }} steps: - name: "CI Success" diff --git a/.github/workflows/cross-platform-test.yml b/.github/workflows/cross-platform-test.yml index e76d77f789..8352f82675 100644 --- a/.github/workflows/cross-platform-test.yml +++ b/.github/workflows/cross-platform-test.yml @@ -22,6 +22,10 @@ on: description: "Name of the LFX package artifact" required: false type: string + sdk-artifact-name: + description: "Name of the langflow-sdk package artifact" + required: false + type: string pre_release: description: "Whether this is a pre-release build" required: false @@ -161,6 +165,13 @@ jobs: shell: bash # Download artifacts for wheel installation + - name: Download langflow-sdk package artifact + if: steps.install-method.outputs.method == 'wheel' && inputs.sdk-artifact-name != '' + uses: actions/download-artifact@v7 + with: + name: ${{ inputs.sdk-artifact-name }} + path: ./sdk-dist + - name: Download LFX package artifact if: steps.install-method.outputs.method == 'wheel' && inputs.lfx-artifact-name != '' uses: actions/download-artifact@v7 @@ -187,9 +198,38 @@ jobs: uv venv test-env --seed shell: bash - # Wheel installation steps + # Wheel installation steps — install SDK and LFX together when both are present. + - name: Install SDK and LFX packages from wheel (Windows) + if: steps.install-method.outputs.method == 'wheel' && matrix.os == 'windows' && inputs.sdk-artifact-name != '' && inputs.lfx-artifact-name != '' + run: | + ls -la ./sdk-dist/ + find ./sdk-dist -name "*.whl" -type f + ls -la ./lfx-dist/ + find ./lfx-dist -name "*.whl" -type f + SDK_WHEEL_FILE=$(find ./sdk-dist -name "*.whl" -type f | head -1) + LFX_WHEEL_FILE=$(find ./lfx-dist -name "*.whl" -type f | head -1) + if [ -n "$SDK_WHEEL_FILE" ] && [ -n "$LFX_WHEEL_FILE" ]; then + uv pip install --prerelease=allow --python ./test-env/Scripts/python.exe "$SDK_WHEEL_FILE" "$LFX_WHEEL_FILE" + else + echo "Missing wheel file in ./sdk-dist/ or ./lfx-dist/" + exit 1 + fi + shell: bash + + - name: Install SDK package from wheel (Windows) + if: steps.install-method.outputs.method == 'wheel' && matrix.os == 'windows' && inputs.sdk-artifact-name != '' && inputs.lfx-artifact-name == '' + run: | + WHEEL_FILE=$(find ./sdk-dist -name "*.whl" -type f | head -1) + if [ -n "$WHEEL_FILE" ]; then + uv pip install --prerelease=allow --python ./test-env/Scripts/python.exe "$WHEEL_FILE" + else + echo "No wheel file found in ./sdk-dist/" + exit 1 + fi + shell: bash + - name: Install LFX package from wheel (Windows) - if: steps.install-method.outputs.method == 'wheel' && matrix.os == 'windows' && inputs.lfx-artifact-name != '' + if: steps.install-method.outputs.method == 'wheel' && matrix.os == 'windows' && inputs.lfx-artifact-name != '' && inputs.sdk-artifact-name == '' run: | ls -la ./lfx-dist/ find ./lfx-dist -name "*.whl" -type f @@ -230,8 +270,37 @@ jobs: fi shell: bash + - name: Install SDK and LFX packages from wheel (Unix) + if: steps.install-method.outputs.method == 'wheel' && matrix.os != 'windows' && inputs.sdk-artifact-name != '' && inputs.lfx-artifact-name != '' + run: | + ls -la ./sdk-dist/ + find ./sdk-dist -name "*.whl" -type f + ls -la ./lfx-dist/ + find ./lfx-dist -name "*.whl" -type f + SDK_WHEEL_FILE=$(find ./sdk-dist -name "*.whl" -type f | head -1) + LFX_WHEEL_FILE=$(find ./lfx-dist -name "*.whl" -type f | head -1) + if [ -n "$SDK_WHEEL_FILE" ] && [ -n "$LFX_WHEEL_FILE" ]; then + uv pip install --prerelease=allow --python ./test-env/bin/python "$SDK_WHEEL_FILE" "$LFX_WHEEL_FILE" + else + echo "Missing wheel file in ./sdk-dist/ or ./lfx-dist/" + exit 1 + fi + shell: bash + + - name: Install SDK package from wheel (Unix) + if: steps.install-method.outputs.method == 'wheel' && matrix.os != 'windows' && inputs.sdk-artifact-name != '' && inputs.lfx-artifact-name == '' + run: | + WHEEL_FILE=$(find ./sdk-dist -name "*.whl" -type f | head -1) + if [ -n "$WHEEL_FILE" ]; then + uv pip install --prerelease=allow --python ./test-env/bin/python "$WHEEL_FILE" + else + echo "No wheel file found in ./sdk-dist/" + exit 1 + fi + shell: bash + - name: Install LFX package from wheel (Unix) - if: steps.install-method.outputs.method == 'wheel' && matrix.os != 'windows' && inputs.lfx-artifact-name != '' + if: steps.install-method.outputs.method == 'wheel' && matrix.os != 'windows' && inputs.lfx-artifact-name != '' && inputs.sdk-artifact-name == '' run: | ls -la ./lfx-dist/ find ./lfx-dist -name "*.whl" -type f @@ -460,6 +529,13 @@ jobs: shell: bash # Download artifacts for wheel installation + - name: Download langflow-sdk package artifact + if: steps.install-method.outputs.method == 'wheel' && inputs.sdk-artifact-name != '' + uses: actions/download-artifact@v7 + with: + name: ${{ inputs.sdk-artifact-name }} + path: ./sdk-dist + - name: Download LFX package artifact if: steps.install-method.outputs.method == 'wheel' && inputs.lfx-artifact-name != '' uses: actions/download-artifact@v7 @@ -486,7 +562,31 @@ jobs: uv venv test-env --seed shell: bash - # Wheel installation steps + # Wheel installation steps — install langflow-sdk before LFX (sdk is not yet on PyPI) + - name: Install langflow-sdk from wheel (Windows) + if: steps.install-method.outputs.method == 'wheel' && matrix.os == 'windows' && inputs.sdk-artifact-name != '' + run: | + WHEEL_FILE=$(find ./sdk-dist -name "*.whl" -type f | head -1) + if [ -n "$WHEEL_FILE" ]; then + uv pip install --prerelease=allow --python ./test-env/Scripts/python.exe "$WHEEL_FILE" + else + echo "No wheel file found in ./sdk-dist/" + exit 1 + fi + shell: bash + + - name: Install langflow-sdk from wheel (Unix) + if: steps.install-method.outputs.method == 'wheel' && matrix.os != 'windows' && inputs.sdk-artifact-name != '' + run: | + WHEEL_FILE=$(find ./sdk-dist -name "*.whl" -type f | head -1) + if [ -n "$WHEEL_FILE" ]; then + uv pip install --prerelease=allow --python ./test-env/bin/python "$WHEEL_FILE" + else + echo "No wheel file found in ./sdk-dist/" + exit 1 + fi + shell: bash + - name: Install LFX package from wheel (Windows) if: steps.install-method.outputs.method == 'wheel' && matrix.os == 'windows' && inputs.lfx-artifact-name != '' run: | diff --git a/.github/workflows/nightly_build.yml b/.github/workflows/nightly_build.yml index d40d0dc935..cb1939a92b 100644 --- a/.github/workflows/nightly_build.yml +++ b/.github/workflows/nightly_build.yml @@ -85,6 +85,7 @@ jobs: release_tag: ${{ steps.generate_release_tag.outputs.release_tag }} base_tag: ${{ steps.set_base_tag.outputs.base_tag }} lfx_tag: ${{ steps.generate_lfx_tag.outputs.lfx_tag }} + sdk_tag: ${{ steps.generate_sdk_tag.outputs.sdk_tag }} steps: - name: Checkout code uses: actions/checkout@v6 @@ -138,6 +139,14 @@ jobs: echo "lfx_tag=$LFX_TAG" >> $GITHUB_OUTPUT echo "lfx_tag=$LFX_TAG" + - name: Generate SDK nightly tag + id: generate_sdk_tag + run: | + # NOTE: This outputs the tag with the `v` prefix. + SDK_TAG="$(uv run ./scripts/ci/sdk_nightly_tag.py)" + echo "sdk_tag=$SDK_TAG" >> $GITHUB_OUTPUT + echo "sdk_tag=$SDK_TAG" + - name: Commit tag id: commit_tag run: | @@ -149,8 +158,11 @@ jobs: RELEASE_TAG="${{ steps.generate_release_tag.outputs.release_tag }}" BASE_TAG="${{ steps.generate_base_tag.outputs.base_tag }}" LFX_TAG="${{ steps.generate_lfx_tag.outputs.lfx_tag }}" + SDK_TAG="${{ steps.generate_sdk_tag.outputs.sdk_tag }}" + echo "Updating SDK project version to $SDK_TAG" + uv run --no-sync ./scripts/ci/update_sdk_version.py $SDK_TAG echo "Updating LFX project version to $LFX_TAG" - uv run ./scripts/ci/update_lfx_version.py $LFX_TAG + uv run --no-sync ./scripts/ci/update_lfx_version.py $LFX_TAG $SDK_TAG echo "Updating base project version to $BASE_TAG and updating main project version to $RELEASE_TAG" uv run --no-sync ./scripts/ci/update_pyproject_combined.py main $RELEASE_TAG $BASE_TAG $LFX_TAG @@ -158,7 +170,7 @@ jobs: cd src/backend/base && uv lock && cd ../../.. cd src/lfx && uv lock && cd ../.. - git add pyproject.toml src/backend/base/pyproject.toml src/lfx/pyproject.toml uv.lock src/backend/base/uv.lock + git add pyproject.toml src/backend/base/pyproject.toml src/lfx/pyproject.toml src/sdk/pyproject.toml uv.lock src/backend/base/uv.lock git commit -m "Update version and project name" echo "Tagging main with $RELEASE_TAG" @@ -278,6 +290,7 @@ jobs: nightly_tag_release: ${{ needs.create-nightly-tag.outputs.release_tag }} nightly_tag_base: ${{ needs.create-nightly-tag.outputs.base_tag }} nightly_tag_lfx: ${{ needs.create-nightly-tag.outputs.lfx_tag }} + nightly_tag_sdk: ${{ needs.create-nightly-tag.outputs.sdk_tag }} # When triggered by schedule, inputs.push_to_registry is not set, so default to true # When triggered manually, use the provided value (default is also true) push_to_registry: ${{ github.event_name == 'schedule' || inputs.push_to_registry != false }} diff --git a/.github/workflows/python_test.yml b/.github/workflows/python_test.yml index bab24fe61f..e222bbde1d 100644 --- a/.github/workflows/python_test.yml +++ b/.github/workflows/python_test.yml @@ -93,7 +93,7 @@ jobs: - name: Run unit tests uses: nick-fields/retry@v3 with: - timeout_minutes: 12 + timeout_minutes: 15 max_attempts: 2 command: make unit_tests args="-x -vv --splits ${{ matrix.splitCount }} --group ${{ matrix.group }} --reruns 5 --cov --cov-config=src/backend/.coveragerc --cov-report=xml --cov-report=html" diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml index 43dfa3c460..7d6c8bafab 100644 --- a/.github/workflows/release_nightly.yml +++ b/.github/workflows/release_nightly.yml @@ -36,6 +36,10 @@ on: description: "Tag for the nightly LFX build" required: false type: string + nightly_tag_sdk: + description: "Tag for the nightly SDK build" + required: false + type: string push_to_registry: description: "Whether to push images to registries. Set to false for testing builds without publishing." required: true @@ -72,6 +76,20 @@ jobs: - name: Install LFX dependencies run: cd src/lfx && uv sync + - name: Verify SDK Nightly Name and Version + if: ${{ inputs.build_lfx }} + run: | + name=$(grep '^name = "' src/sdk/pyproject.toml | head -n 1 | sed 's/.*"\(.*\)".*/\1/') + version=$(grep '^version = "' src/sdk/pyproject.toml | head -n 1 | sed 's/.*"\(.*\)".*/\1/') + if [ "$name" != "langflow-sdk-nightly" ]; then + echo "Name $name does not match langflow-sdk-nightly. Exiting the workflow." + exit 1 + fi + if [ "v$version" != "${{ inputs.nightly_tag_sdk }}" ]; then + echo "Version v$version does not match nightly tag ${{ inputs.nightly_tag_sdk }}. Exiting the workflow." + exit 1 + fi + - name: Verify Nightly Name and Version id: verify run: | @@ -90,6 +108,10 @@ jobs: version=$(echo $version | sed 's/^v//') echo "version=$version" >> $GITHUB_OUTPUT + - name: Build langflow-sdk for distribution + run: | + make sdk_build args="--wheel" + - name: Build LFX for distribution run: | cd src/lfx @@ -99,12 +121,21 @@ jobs: - name: Test LFX CLI run: | cd src/lfx - uv pip install dist/*.whl --force-reinstall - uv run lfx --help + # Use a clean venv and install both wheels together so uv can resolve + # the local SDK dependency without falling back to PyPI. + uv venv test-env --seed + uv pip install --python ./test-env/bin/python --force-reinstall ../sdk/dist/*.whl dist/*.whl + ./test-env/bin/lfx --help echo "LFX CLI test completed successfully" # PyPI publishing moved to after cross-platform testing + - name: Upload langflow-sdk Artifact + uses: actions/upload-artifact@v6 + with: + name: dist-nightly-sdk + path: src/sdk/dist + - name: Upload LFX Artifact uses: actions/upload-artifact@v6 with: @@ -316,10 +347,11 @@ jobs: base-artifact-name: "dist-nightly-base" main-artifact-name: "dist-nightly-main" lfx-artifact-name: "dist-nightly-lfx" + sdk-artifact-name: "dist-nightly-sdk" publish-nightly-lfx: name: Publish LFX Nightly to PyPI - needs: [build-nightly-lfx, test-cross-platform] + needs: [build-nightly-lfx, test-cross-platform, publish-nightly-sdk] if: ${{ inputs.build_lfx }} runs-on: ubuntu-latest steps: @@ -345,6 +377,34 @@ jobs: run: | make lfx_publish + publish-nightly-sdk: + name: Publish SDK Nightly to PyPI + needs: [build-nightly-lfx, test-cross-platform] + if: ${{ inputs.build_lfx }} + runs-on: ubuntu-latest + steps: + - name: Check out the code + uses: actions/checkout@v6 + with: + ref: ${{ inputs.nightly_tag_release }} + persist-credentials: true + - name: Download langflow-sdk artifact + uses: actions/download-artifact@v7 + with: + name: dist-nightly-sdk + path: src/sdk/dist + - name: Setup Environment + uses: astral-sh/setup-uv@v6 + with: + enable-cache: false + python-version: "3.13" + - name: Publish SDK to PyPI + env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + run: | + make sdk_publish + publish-nightly-base: name: Publish Langflow Base Nightly to PyPI needs: [build-nightly-base, test-cross-platform, publish-nightly-lfx] diff --git a/.secrets.baseline b/.secrets.baseline index 1f51e981c2..f071e8eb4a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -187,7 +187,7 @@ "filename": ".github/workflows/nightly_build.yml", "hashed_secret": "3e26d6750975d678acb8fa35a0f69237881576b0", "is_verified": false, - "line_number": 284, + "line_number": 297, "is_secret": false } ], @@ -207,7 +207,7 @@ "filename": ".github/workflows/release_nightly.yml", "hashed_secret": "3e26d6750975d678acb8fa35a0f69237881576b0", "is_verified": false, - "line_number": 413, + "line_number": 473, "is_secret": false } ], @@ -7315,5 +7315,5 @@ } ] }, - "generated_at": "2026-04-02T17:27:24Z" + "generated_at": "2026-04-03T04:00:10Z" } diff --git a/Makefile b/Makefile index 59a6a40a7d..b5273b18d1 100644 --- a/Makefile +++ b/Makefile @@ -490,6 +490,38 @@ lfx_docker_test: ## run LFX tests in Docker @echo 'Running LFX tests in Docker' @cd src/lfx && make docker_test +###################### +# SDK PACKAGE +###################### + +sdk_build: ## build the SDK package + @echo 'Building SDK package' + @cd src/sdk && make build + +sdk_publish: ## publish SDK package to PyPI + @echo 'Publishing SDK package' + @cd src/sdk && make publish + +sdk_publish_testpypi: ## publish SDK package to test PyPI + @echo 'Publishing SDK package to test PyPI' + @cd src/sdk && make publish_test + +sdk_test: ## run SDK tests + @echo 'Running SDK tests' + @cd src/sdk && make test + +sdk_format: ## format SDK code + @echo 'Formatting SDK code' + @cd src/sdk && make format + +sdk_lint: ## lint SDK code + @echo 'Linting SDK code' + @cd src/sdk && make lint + +sdk_clean: ## clean SDK build artifacts + @echo 'Cleaning SDK build artifacts' + @cd src/sdk && make clean + # example make alembic-revision message="Add user table" alembic-revision: ## generate a new migration @echo 'Generating a new Alembic revision' @@ -877,6 +909,14 @@ help_backend: ## show backend-specific commands @echo " $(GREEN)make lfx_docker_dev$(NC) - Start LFX development environment" @echo " $(GREEN)make lfx_docker_test$(NC) - Run LFX tests in Docker" @echo '' + @echo "$(GREEN)SDK Package Commands:$(NC)" + @echo " $(GREEN)make sdk_build$(NC) - Build SDK package" + @echo " $(GREEN)make sdk_test$(NC) - Run SDK tests" + @echo " $(GREEN)make sdk_format$(NC) - Format SDK code" + @echo " $(GREEN)make sdk_lint$(NC) - Lint SDK code" + @echo " $(GREEN)make sdk_clean$(NC) - Clean SDK build artifacts" + @echo " $(GREEN)make sdk_publish$(NC) - Publish SDK to PyPI" + @echo '' @echo "$(GREEN)═══════════════════════════════════════════════════════════════════$(NC)" @echo '' @@ -983,6 +1023,8 @@ help_advanced: ## show advanced and miscellaneous commands @echo " $(GREEN)make publish_langflow$(NC) - Publish langflow to PyPI" @echo " $(GREEN)make lfx_publish$(NC) - Publish LFX package to PyPI" @echo " $(GREEN)make lfx_publish_testpypi$(NC) - Publish LFX to test PyPI" + @echo " $(GREEN)make sdk_publish$(NC) - Publish SDK package to PyPI" + @echo " $(GREEN)make sdk_publish_testpypi$(NC) - Publish SDK to test PyPI" @echo '' @echo "$(GREEN)Lock Files:$(NC)" @echo " $(GREEN)make lock$(NC) - Lock all dependencies" diff --git a/scripts/ci/sdk_nightly_tag.py b/scripts/ci/sdk_nightly_tag.py new file mode 100644 index 0000000000..b3f0ae83f9 --- /dev/null +++ b/scripts/ci/sdk_nightly_tag.py @@ -0,0 +1,63 @@ +"""Script to generate nightly tags for the SDK package.""" + +import packaging.version +import requests +from packaging.version import Version + +PYPI_SDK_URL = "https://pypi.org/pypi/langflow-sdk/json" +PYPI_SDK_NIGHTLY_URL = "https://pypi.org/pypi/langflow-sdk-nightly/json" + + +def get_latest_published_version(*, is_nightly: bool) -> Version: + url = PYPI_SDK_NIGHTLY_URL if is_nightly else PYPI_SDK_URL + + res = requests.get(url, timeout=10) + if res.status_code == requests.codes.not_found: + msg = "Package not found on PyPI" + raise requests.RequestException(msg) + + try: + version_str = res.json()["info"]["version"] + except (KeyError, ValueError) as e: + msg = "Got unexpected response from PyPI" + raise requests.RequestException(msg) from e + return Version(version_str) + + +def create_sdk_tag(): + from pathlib import Path + + import tomllib + + sdk_pyproject_path = Path(__file__).parent.parent.parent / "src" / "sdk" / "pyproject.toml" + pyproject_data = tomllib.loads(sdk_pyproject_path.read_text()) + + current_version_str = pyproject_data["project"]["version"] + current_version = Version(current_version_str) + + try: + current_nightly_version = get_latest_published_version(is_nightly=True) + nightly_base_version = current_nightly_version.base_version + except (requests.RequestException, KeyError, ValueError): + current_nightly_version = None + nightly_base_version = None + + build_number = "0" + latest_base_version = current_version.base_version + + if current_nightly_version and latest_base_version == nightly_base_version: + build_number = str(current_nightly_version.dev + 1) + + new_nightly_version = latest_base_version + ".dev" + build_number + + if not new_nightly_version.startswith("v"): + new_nightly_version = "v" + new_nightly_version + + packaging.version.Version(new_nightly_version) + + return new_nightly_version + + +if __name__ == "__main__": + tag = create_sdk_tag() + print(tag) diff --git a/scripts/ci/update_lfx_version.py b/scripts/ci/update_lfx_version.py index 8c7292b6ca..b264b03eb6 100644 --- a/scripts/ci/update_lfx_version.py +++ b/scripts/ci/update_lfx_version.py @@ -34,11 +34,28 @@ def update_lfx_workspace_dep(pyproject_path: str, new_project_name: str) -> None filepath.write_text(content, encoding="utf-8") -def update_lfx_for_nightly(lfx_tag: str): +def update_sdk_dependency_in_lfx(pyproject_path: str, sdk_version: str) -> None: + """Update the SDK dependency in the LFX pyproject for nightly builds.""" + filepath = BASE_DIR / pyproject_path + content = filepath.read_text(encoding="utf-8") + + pattern = re.compile(r'"langflow-sdk(?:-nightly)?(?:==|~=|>=)[\d.]+(?:\.(?:post|dev|a|b|rc)\d+)*"') + replacement = f'"langflow-sdk-nightly=={sdk_version}"' + + if not pattern.search(content): + msg = f"SDK dependency not found in {filepath}" + raise ValueError(msg) + + content = pattern.sub(replacement, content) + filepath.write_text(content, encoding="utf-8") + + +def update_lfx_for_nightly(lfx_tag: str, sdk_tag: str): """Update LFX package for nightly build. Args: lfx_tag: The nightly tag for LFX (e.g., "v0.1.0.dev0") + sdk_tag: The nightly tag for the SDK (e.g., "v0.1.0.dev0") """ lfx_pyproject_path = "src/lfx/pyproject.toml" @@ -52,6 +69,9 @@ def update_lfx_for_nightly(lfx_tag: str): # Update workspace dependency in root pyproject.toml update_lfx_workspace_dep("pyproject.toml", "lfx-nightly") + sdk_version = sdk_tag.lstrip("v") + update_sdk_dependency_in_lfx(lfx_pyproject_path, sdk_version) + print(f"Updated LFX package to lfx-nightly version {version}") @@ -59,15 +79,16 @@ def main(): """Update LFX for nightly builds. Usage: - update_lfx_version.py + update_lfx_version.py """ - expected_args = 2 + expected_args = 3 if len(sys.argv) != expected_args: - print("Usage: update_lfx_version.py ") + print("Usage: update_lfx_version.py ") sys.exit(1) lfx_tag = sys.argv[1] - update_lfx_for_nightly(lfx_tag) + sdk_tag = sys.argv[2] + update_lfx_for_nightly(lfx_tag, sdk_tag) if __name__ == "__main__": diff --git a/scripts/ci/update_pyproject_name.py b/scripts/ci/update_pyproject_name.py index 444c0c5844..703bd1574b 100755 --- a/scripts/ci/update_pyproject_name.py +++ b/scripts/ci/update_pyproject_name.py @@ -45,6 +45,9 @@ def update_uv_dep(pyproject_path: str, new_project_name: str) -> None: elif new_project_name == "langflow-base-nightly": pattern = re.compile(r"langflow-base = \{ workspace = true \}") replacement = "langflow-base-nightly = { workspace = true }" + elif new_project_name == "langflow-sdk-nightly": + pattern = re.compile(r"langflow-sdk = \{ workspace = true \}") + replacement = "langflow-sdk-nightly = { workspace = true }" else: msg = f"Invalid project name: {new_project_name}" raise ValueError(msg) diff --git a/scripts/ci/update_sdk_version.py b/scripts/ci/update_sdk_version.py new file mode 100644 index 0000000000..92a2d75ae6 --- /dev/null +++ b/scripts/ci/update_sdk_version.py @@ -0,0 +1,40 @@ +"""Script to update SDK version for nightly builds.""" + +import sys +from pathlib import Path + +from update_pyproject_name import update_pyproject_name +from update_pyproject_name import update_uv_dep as update_workspace_dep +from update_pyproject_version import update_pyproject_version + +# Add the current directory to the path so we can import the other scripts +current_dir = Path(__file__).resolve().parent +sys.path.append(str(current_dir)) + + +def update_sdk_for_nightly(sdk_tag: str): + """Update SDK package for nightly build.""" + sdk_pyproject_path = "src/sdk/pyproject.toml" + + update_pyproject_name(sdk_pyproject_path, "langflow-sdk-nightly") + + version = sdk_tag.lstrip("v") + update_pyproject_version(sdk_pyproject_path, version) + + update_workspace_dep("pyproject.toml", "langflow-sdk-nightly") + + print(f"Updated SDK package to langflow-sdk-nightly version {version}") + + +def main(): + expected_args = 2 + if len(sys.argv) != expected_args: + print("Usage: update_sdk_version.py ") + sys.exit(1) + + sdk_tag = sys.argv[1] + update_sdk_for_nightly(sdk_tag) + + +if __name__ == "__main__": + main() diff --git a/src/sdk/Makefile b/src/sdk/Makefile new file mode 100644 index 0000000000..9bbd7d5f8b --- /dev/null +++ b/src/sdk/Makefile @@ -0,0 +1,68 @@ +.PHONY: all help init install dev format lint test build build_wheel build_sdist publish publish_test clean + +# Configurations +VERSION=$(shell grep "^version" pyproject.toml | sed 's/.*\"\(.*\)\"$$/\1/') +GREEN=\033[0;32m +NC=\033[0m # No Color + +all: help + +help: ## show this help message + @echo '----' + @grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | \ + awk -F ':.*##' '{printf "\033[36mmake %s\033[0m: %s\n", $$1, $$2}' | \ + column -c2 -t -s : + @echo '----' + +init: ## initialize the project + @echo "$(GREEN)Installing SDK dependencies...$(NC)" + @uv sync --dev + @echo "$(GREEN)SDK project initialized.$(NC)" + +install: ## install the project dependencies + @echo "$(GREEN)Installing SDK dependencies...$(NC)" + @uv sync + +dev: ## install development dependencies + @echo "$(GREEN)Installing SDK development dependencies...$(NC)" + @uv sync --dev + +format: dev ## format the code + @echo "$(GREEN)Formatting SDK code...$(NC)" + @uv run ruff check . --fix + @uv run ruff format . + +lint: dev ## run linters + @echo "$(GREEN)Running SDK linters...$(NC)" + @uv run ruff check . + +test: dev ## run tests + @echo "$(GREEN)Running SDK tests...$(NC)" + @uv run pytest tests -v $(args) + +build: ## build the project + @echo "$(GREEN)Building SDK...$(NC)" + @rm -rf dist/ + @uv build --out-dir dist $(args) + @echo "$(GREEN)SDK build completed. Artifacts in dist/$(NC)" + +build_wheel: ## build wheel only + @make build args="--wheel" + +build_sdist: ## build source distribution only + @make build args="--sdist" + +publish: ## publish to PyPI + @echo "$(GREEN)Publishing SDK to PyPI...$(NC)" + @uv publish + +publish_test: ## publish to test PyPI + @echo "$(GREEN)Publishing SDK to test PyPI...$(NC)" + @uv publish --repository testpypi + +clean: ## clean build artifacts + @echo "$(GREEN)Cleaning SDK build artifacts...$(NC)" + @rm -rf dist/ + @find . -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true + @find . -type f -name '*.pyc' -delete + @echo "$(GREEN)Cleanup completed.$(NC)"