diff --git a/.github/actions/setup-runner/action.yml b/.github/actions/setup-runner/action.yml index 3998e4d934..848e218f7b 100644 --- a/.github/actions/setup-runner/action.yml +++ b/.github/actions/setup-runner/action.yml @@ -46,7 +46,7 @@ runs: # Install specific client version after sync if needed if [ "${{ steps.client-config.outputs.install-after-sync }}" = "true" ]; then echo "Installing llama-stack-client from: ${{ steps.client-config.outputs.install-source }}" - uv pip install ${{ steps.client-config.outputs.install-source }} + uv pip install --upgrade ${{ steps.client-config.outputs.install-source }} fi echo "Installed llama packages" diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a0d8b23a40..a76102f8f6 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -23,6 +23,7 @@ Llama Stack uses GitHub Actions for Continuous Integration (CI). Below is a tabl | Prepare release | [prepare-release.yml](prepare-release.yml) | Prepare release | | Test Llama Stack Build | [providers-build.yml](providers-build.yml) | Test llama stack build | | Test llama stack list-deps | [providers-list-deps.yml](providers-list-deps.yml) | Test llama stack list-deps | +| Publish OpenAPI SDK to PyPI | [publish-openapi-sdk.yml](publish-openapi-sdk.yml) | Publish llama-stack-open-client to PyPI | | Build, test, and publish packages | [pypi.yml](pypi.yml) | Build, test, and publish packages | | Integration Tests (Record) | [record-integration-tests.yml](record-integration-tests.yml) | Auto-record missing test recordings for PR | | Release Branch Scheduled CI | [release-branch-scheduled-ci.yml](release-branch-scheduled-ci.yml) | Scheduled CI checks for active release branches | diff --git a/.github/workflows/openapi-generator-validation.yml b/.github/workflows/openapi-generator-validation.yml index 5f41f8e25b..6bff45b623 100644 --- a/.github/workflows/openapi-generator-validation.yml +++ b/.github/workflows/openapi-generator-validation.yml @@ -185,9 +185,9 @@ jobs: echo "OpenAPI spec generated successfully" ls -lh openapi.yml - - name: Generate Python SDK + - name: Generate Python SDK (for CI testing with llama_stack_client name) working-directory: client-sdks/openapi - run: make sdk + run: make sdk OPEN=0 - name: Validate generated SDK working-directory: client-sdks/openapi @@ -214,3 +214,46 @@ jobs: # Show directory structure for debugging echo "=== Generated SDK structure ===" find sdks/python -type f -name "*.py" | head -20 + + - name: Install generated SDK + run: | + echo "Installing OpenAPI-generated SDK (llama_stack_client)..." + # Uninstall existing client (note: uv pip uninstall doesn't support -y flag) + uv pip uninstall llama-stack-client || true + + # Install SDK using uv pip - this ensures proper editable install + uv pip install -e client-sdks/openapi/sdks/python + + echo "Verifying installation..." + # Use 'uv run python' to ensure we use the correct Python in the venv + uv run python -c "import llama_stack_client; print(f'Installed: {llama_stack_client.__name__}')" + + - name: Setup Ollama (for integration tests) + if: runner.os == 'Linux' + uses: ./.github/actions/setup-ollama + + - name: Run integration tests with OpenAPI SDK + if: runner.os == 'Linux' + run: | + echo "Running integration tests with OpenAPI-generated SDK..." + + # Run a smoke test suite - basic functionality tests + uv run pytest tests/integration/inspect/test_inspect.py \ + --stack-config="inference=ollama" \ + -v \ + || echo "::warning::Some integration tests failed - this may indicate SDK compatibility issues" + + # Show which SDK is actually being used + uv run python -c "import llama_stack_client; import inspect; print(f'SDK location: {inspect.getfile(llama_stack_client)}')" + + - name: Summary + if: runner.os == 'Linux' + run: | + echo "## OpenAPI SDK Validation Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ SDK generation completed successfully" >> $GITHUB_STEP_SUMMARY + echo "✅ SDK installed and importable" >> $GITHUB_STEP_SUMMARY + echo "✅ Integration tests executed (check logs for results)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Platform**: ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY + echo "**Package**: llama_stack_client (OpenAPI-generated)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 8416cfbab8..22883a457b 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -152,7 +152,7 @@ jobs: # Install specific client version after sync if needed if [ "${{ steps.client-config.outputs.install-after-sync }}" = "true" ]; then echo "Installing llama-stack-client from: ${{ steps.client-config.outputs.install-source }}" - uv pip install ${{ steps.client-config.outputs.install-source }} + uv pip install --upgrade ${{ steps.client-config.outputs.install-source }} fi - name: Run mypy (full type_checking) diff --git a/.github/workflows/publish-openapi-sdk.yml b/.github/workflows/publish-openapi-sdk.yml new file mode 100644 index 0000000000..a3a6c3852d --- /dev/null +++ b/.github/workflows/publish-openapi-sdk.yml @@ -0,0 +1,151 @@ +name: Publish OpenAPI SDK to PyPI + +run-name: Publish llama-stack-open-client to PyPI + +on: + workflow_dispatch: + inputs: + publish_to: + description: 'Publish to PyPI or TestPyPI' + required: true + default: 'testpypi' + type: choice + options: + - testpypi + - pypi + dry_run: + description: 'Dry run (build only, no publish)' + type: boolean + default: false + push: + tags: + - 'openapi-sdk-v*' # Tags like openapi-sdk-v0.5.0 + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + id-token: write # Required for trusted publishing to PyPI + +jobs: + publish-sdk: + runs-on: ubuntu-latest + environment: + name: ${{ github.event.inputs.publish_to || 'testpypi' }} + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Python environment + uses: ./.github/actions/setup-runner + with: + python-version: '3.12' + + - name: Set up Java + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: 'temurin' + java-version: '11' + + - name: Install openapi-generator-cli + run: npm install -g @openapitools/openapi-generator-cli + + - name: Verify installations + working-directory: client-sdks/openapi + run: | + set -e + echo "=== Java version ===" + java -version + + echo "=== OpenAPI Generator version ===" + openapi-generator-cli version + + echo "=== SDK version from pyproject.toml ===" + make version + + - name: Generate OpenAPI SDK + working-directory: client-sdks/openapi + run: | + echo "Generating SDK with OPEN=1 (llama_stack_open_client)..." + make sdk OPEN=1 + + - name: Verify SDK generation + working-directory: client-sdks/openapi + run: | + if [ ! -d sdks/python ]; then + echo "Error: SDK directory was not generated" + exit 1 + fi + + PY_FILE_COUNT=$(find sdks/python -name "*.py" | wc -l) + echo "Generated Python files: $PY_FILE_COUNT" + + if [ "$PY_FILE_COUNT" -le 10 ]; then + echo "Error: Too few Python files generated" + exit 1 + fi + + echo "SDK generated successfully" + ls -lh sdks/python/ + + - name: Build package + working-directory: client-sdks/openapi/sdks/python + run: | + echo "Building Python package..." + uv build + + echo "Built distribution files:" + ls -lh dist/ + + - name: Publish to TestPyPI (dry-run or manual) + if: | + (github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi' && inputs.dry_run == false) || + (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/openapi-sdk-v') && !contains(github.ref, '-')) + working-directory: client-sdks/openapi/sdks/python + run: | + echo "Publishing to TestPyPI..." + uv publish --publish-url https://test.pypi.org/legacy/ dist/* + env: + UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }} + + - name: Publish to PyPI (production) + if: | + github.event_name == 'workflow_dispatch' && + inputs.publish_to == 'pypi' && + inputs.dry_run == false + working-directory: client-sdks/openapi/sdks/python + run: | + echo "Publishing to PyPI..." + uv publish dist/* + env: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + + - name: Upload build artifacts + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4.6.0 + with: + name: python-sdk-dist + path: client-sdks/openapi/sdks/python/dist/ + retention-days: 30 + + - name: Summary + run: | + echo "## SDK Publishing Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Event**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY + echo "- **Dry Run**: ${{ inputs.dry_run || 'false' }}" >> $GITHUB_STEP_SUMMARY + echo "- **Target**: ${{ inputs.publish_to || 'testpypi (default)' }}" >> $GITHUB_STEP_SUMMARY + echo "- **Package**: llama-stack-open-client" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ inputs.dry_run }}" = "true" ]; then + echo "✅ Package built successfully (dry-run, not published)" >> $GITHUB_STEP_SUMMARY + elif [ "${{ inputs.publish_to }}" = "testpypi" ] || [ "${{ github.event_name }}" = "push" ]; then + echo "✅ Package published to TestPyPI" >> $GITHUB_STEP_SUMMARY + echo "Install with: \`pip install --index-url https://test.pypi.org/simple/ llama-stack-open-client\`" >> $GITHUB_STEP_SUMMARY + else + echo "✅ Package published to PyPI" >> $GITHUB_STEP_SUMMARY + echo "Install with: \`pip install llama-stack-open-client\`" >> $GITHUB_STEP_SUMMARY + fi diff --git a/client-sdks/openapi/Makefile b/client-sdks/openapi/Makefile index f2cd3e0412..0619109cbb 100644 --- a/client-sdks/openapi/Makefile +++ b/client-sdks/openapi/Makefile @@ -118,7 +118,7 @@ sdk: check-generator $(PROCESSED_SPEC) generate-config @echo "Generating Python SDK (version: $(VERSION), package: $(PACKAGE_NAME))..." @mkdir -p $(SDK_OUTPUT_DIR) $(OPENAPI_GENERATOR) generate -i $(PROCESSED_SPEC) -g python -c $(OPENAPI_CONFIG) -o $(SDK_OUTPUT_DIR) \ - --additional-properties=packageVersion=$(VERSION) -t $(TEMPLATE_DIR) + --additional-properties=packageVersion=$(VERSION),httpx=true -t $(TEMPLATE_DIR) @cp -R $(TEMPLATE_DIR)/lib $(SDK_OUTPUT_DIR)/$(PACKAGE_NAME) @echo "Patching SDK with hierarchical API structure..." $(PYTHON) $(PATCH_SCRIPT) --hierarchy $(HIERARCHY_FILE) --sdk-dir $(SDK_OUTPUT_DIR) --package $(PACKAGE_NAME) diff --git a/client-sdks/openapi/README.md b/client-sdks/openapi/README.md index 3b118d5667..6e09ed73f2 100644 --- a/client-sdks/openapi/README.md +++ b/client-sdks/openapi/README.md @@ -70,6 +70,48 @@ merge_stainless_config.py -> build_hierarchy.py -> openapi-generator -> pa - `sdks/python/` - Generated Python SDK - `.openapi-generator/` - Generator metadata +## Publishing to PyPI + +The SDK can be published to PyPI using the GitHub Actions workflow at `.github/workflows/publish-openapi-sdk.yml`. + +### Manual Publishing (via GitHub UI) + +1. Go to Actions → "Publish OpenAPI SDK to PyPI" +2. Click "Run workflow" +3. Select options: + - **publish_to**: `testpypi` (for testing) or `pypi` (production) + - **dry_run**: `true` to build only without publishing + +### Automatic Publishing (via Git Tags) + +Push a tag matching `openapi-sdk-v*` to trigger automatic publishing to TestPyPI: + +```bash +git tag openapi-sdk-v0.5.0 +git push origin openapi-sdk-v0.5.0 +``` + +### Required Secrets + +Configure these GitHub secrets for the repository: + +- `TEST_PYPI_API_TOKEN` - TestPyPI API token +- `PYPI_API_TOKEN` - Production PyPI API token + +### Testing the Published Package + +After publishing to TestPyPI: + +```bash +pip install --index-url https://test.pypi.org/simple/ llama-stack-open-client +``` + +After publishing to PyPI: + +```bash +pip install llama-stack-open-client +``` + ## Files - `Makefile` - Build orchestration diff --git a/client-sdks/openapi/templates/python/pyproject.mustache b/client-sdks/openapi/templates/python/pyproject.mustache index 39d5abfea9..990b656046 100644 --- a/client-sdks/openapi/templates/python/pyproject.mustache +++ b/client-sdks/openapi/templates/python/pyproject.mustache @@ -6,28 +6,32 @@ {{/poetry1}} name = "{{{packageName}}}" version = "{{{packageVersion}}}" -description = "{{{appName}}}" +description = "Python client SDK for Llama Stack - AI development framework" {{#poetry1}} -authors = ["{{infoName}}{{^infoName}}OpenAPI Generator Community{{/infoName}} <{{infoEmail}}{{^infoEmail}}team@openapitools.org{{/infoEmail}}>"] +authors = ["Meta Llama Team "] {{/poetry1}} {{^poetry1}} authors = [ - {name = "{{infoName}}{{^infoName}}OpenAPI Generator Community{{/infoName}}",email = "{{infoEmail}}{{^infoEmail}}team@openapitools.org{{/infoEmail}}"}, + {name = "Meta Llama Team", email = "llama-oss@meta.com"}, ] {{/poetry1}} -{{#licenseInfo}} -{{#poetry1}} -license = "{{{licenseInfo}}}" -{{/poetry1}} -{{^poetry1}} -license = { text = "{{{licenseInfo}}}" } -{{/poetry1}} -{{/licenseInfo}} +license = { text = "MIT" } readme = "README.md" {{#poetry1}} -repository = "https://{{{gitHost}}}/{{{gitUserId}}}/{{{gitRepoId}}}" +repository = "https://github.com/llamastack/llama-stack" {{/poetry1}} -keywords = ["OpenAPI", "OpenAPI-Generator", "{{{appName}}}"] +keywords = ["llama", "llama-stack", "ai", "ml", "openai", "inference", "agents"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Scientific/Engineering :: Artificial Intelligence", +] {{#poetry1}} include = ["{{packageName}}/py.typed"] @@ -81,7 +85,10 @@ dependencies = [ ] [project.urls] -Repository = "https://{{{gitHost}}}/{{{gitUserId}}}/{{{gitRepoId}}}" +Homepage = "https://llamastack.github.io" +Documentation = "https://llamastack.github.io/docs" +Repository = "https://github.com/llamastack/llama-stack" +"Bug Tracker" = "https://github.com/llamastack/llama-stack/issues" {{/poetry1}} {{^poetry1}}