diff --git a/.github/actions/build-docs/action.yml b/.github/actions/build-docs/action.yml index 84e6a1b..6f2c31a 100644 --- a/.github/actions/build-docs/action.yml +++ b/.github/actions/build-docs/action.yml @@ -1,5 +1,5 @@ name: Build Documentation -description: 'Build Documentation.' +description: 'Build Documentation for a package' runs: using: composite diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 6761d0d..d096f3c 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -1,5 +1,9 @@ name: Build distribution files -description: 'Build distribution files' +description: 'Build distribution files for a package' +inputs: + workspace_path: + description: 'Path to the package to build.' + required: true outputs: package-hashes: description: "base64-encoded sha256 hashes of distribution files" @@ -10,10 +14,11 @@ runs: steps: - name: Build distribution files shell: bash - run: poetry build + run: make -C ${{ inputs.workspace_path }} build + - name: Hash build files for provenance id: package-hashes shell: bash - working-directory: ./dist + working-directory: ${{ inputs.workspace_path }}/dist run: | echo "package-hashes=$(sha256sum * | base64 -w0)" >> "$GITHUB_OUTPUT" diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml new file mode 100644 index 0000000..e744ef6 --- /dev/null +++ b/.github/actions/ci/action.yml @@ -0,0 +1,34 @@ +name: Shared CI Workflow +description: 'Build, lint, and test a package' +inputs: + workspace_path: + description: 'Path to the package to build/test.' + required: true + python_version: + description: 'Python version to use' + required: false + default: '3.11' + +runs: + using: composite + steps: + - name: Set up Python ${{ inputs.python_version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python_version }} + + - name: Install poetry + uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439 + + - name: Install Dependencies + shell: bash + working-directory: ${{ inputs.workspace_path }} + run: poetry install + + - name: Lint + shell: bash + run: make -C ${{ inputs.workspace_path }} lint + + - name: Test + shell: bash + run: make -C ${{ inputs.workspace_path }} test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd8c2dc..d1980dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,35 +11,28 @@ on: - '**.md' jobs: - linux: + server-ai-linux: runs-on: ubuntu-latest - strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Install poetry - uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439 + - uses: ./.github/actions/ci + with: + workspace_path: packages/sdk/server-ai + python_version: ${{ matrix.python-version }} - uses: ./.github/actions/build - - uses: ./.github/actions/build-docs - - - name: Run tests - run: make test + with: + workspace_path: packages/sdk/server-ai - - name: Verify typehints - run: make lint + - uses: ./.github/actions/build-docs - windows: + server-ai-windows: runs-on: windows-latest - defaults: run: shell: powershell @@ -50,6 +43,7 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -59,7 +53,8 @@ jobs: uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439 - name: Install requirements + working-directory: packages/sdk/server-ai run: poetry install - name: Run tests - run: make test + run: make -C packages/sdk/server-ai test diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 7176356..751cdfc 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,67 +1,200 @@ -name: Run Release Please +# This workflow handles both automated and manual package publishing: +# +# AUTOMATED PUBLISHING (on push to main): +# - Triggered automatically when changes are pushed to the main branch +# - Uses release-please to create releases based on conventional commits +# - Publishes packages to PyPI automatically when release PRs are merged +# +# MANUAL PUBLISHING (via workflow_dispatch): +# - Can be triggered manually from the Actions tab +# - Allows publishing a specific package to PyPI +# - Supports dry-run mode +# +name: release-please on: push: - branches: [ main ] + branches: + - main + workflow_dispatch: + inputs: + workspace_path: + description: 'The workspace to publish' + required: true + default: 'packages/sdk/server-ai' + type: choice + options: + - packages/sdk/server-ai + - packages/ai-providers/server-ai-langchain + dry_run: + description: 'Is this a dry run. If so no package will be published.' + type: boolean + required: true jobs: - release-package: + release-please: runs-on: ubuntu-latest permissions: - id-token: write # Needed if using OIDC to get release secrets. - contents: write # Contents and pull-requests are for release-please to make releases. - pull-requests: write + contents: write # Needed for release-please to create releases. + pull-requests: write # Needed for release-please to create/update PRs. + if: github.event_name == 'push' outputs: - release-created: ${{ steps.release.outputs.release_created }} - upload-tag-name: ${{ steps.release.outputs.tag_name }} - package-hashes: ${{ steps.build.outputs.package-hashes}} + package-server-ai-released: ${{ steps.release.outputs['packages/sdk/server-ai--release_created'] }} + package-server-ai-tag-name: ${{ steps.release.outputs['packages/sdk/server-ai--tag_name'] }} + package-server-ai-langchain-released: ${{ steps.release.outputs['packages/ai-providers/server-ai-langchain--release_created'] }} + package-server-ai-langchain-tag-name: ${{ steps.release.outputs['packages/ai-providers/server-ai-langchain--tag_name'] }} steps: - uses: googleapis/release-please-action@v4 id: release + release-server-ai: + runs-on: ubuntu-latest + needs: ['release-please'] + permissions: + id-token: write # Needed for OIDC to get release secrets from AWS. + if: ${{ needs.release-please.outputs.package-server-ai-released == 'true' }} + outputs: + package-hashes: ${{ steps.build.outputs.package-hashes }} + steps: - uses: actions/checkout@v4 - if: ${{ steps.release.outputs.releases_created == 'true' }} with: - fetch-depth: 0 # If you only need the current version keep this. + fetch-depth: 0 - uses: actions/setup-python@v5 - if: ${{ steps.release.outputs.releases_created == 'true' }} with: - python-version: 3.9 + python-version: '3.11' - name: Install poetry - if: ${{ steps.release.outputs.releases_created == 'true' }} uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439 + - uses: ./.github/actions/ci + with: + workspace_path: packages/sdk/server-ai + + - uses: ./.github/actions/build + id: build + with: + workspace_path: packages/sdk/server-ai + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0 - if: ${{ steps.release.outputs.releases_created == 'true' }} name: 'Get PyPI token' with: aws_assume_role: ${{ vars.AWS_ROLE_ARN }} ssm_parameter_pairs: '/production/common/releasing/pypi/token = PYPI_AUTH_TOKEN' + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ env.PYPI_AUTH_TOKEN }} + packages-dir: packages/sdk/server-ai/dist/ + + release-server-ai-langchain: + runs-on: ubuntu-latest + needs: ['release-please'] + permissions: + id-token: write # Needed for OIDC to get release secrets from AWS. + if: ${{ needs.release-please.outputs.package-server-ai-langchain-released == 'true' }} + outputs: + package-hashes: ${{ steps.build.outputs.package-hashes }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install poetry + uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439 + + - uses: ./.github/actions/ci + with: + workspace_path: packages/ai-providers/server-ai-langchain + - uses: ./.github/actions/build id: build - if: ${{ steps.release.outputs.releases_created == 'true' }} + with: + workspace_path: packages/ai-providers/server-ai-langchain - - uses: ./.github/actions/build-docs - if: ${{ steps.release.outputs.releases_created == 'true' }} + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0 + name: 'Get PyPI token' + with: + aws_assume_role: ${{ vars.AWS_ROLE_ARN }} + ssm_parameter_pairs: '/production/common/releasing/pypi/token = PYPI_AUTH_TOKEN' - - name: Publish package distributions to PyPI - if: ${{ steps.release.outputs.releases_created == 'true' }} - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Publish to PyPI + # Pin the action to a full 40-character commit SHA for security. + # Release v1 commit SHA as of 2024-06-14: + # https://github.com/pypa/gh-action-pypi-publish/releases/tag/v1.8.13 + # Commit SHA: 19af04270e8d898ea07a523bb392fa7fe98df87c + uses: pypa/gh-action-pypi-publish@19af04270e8d898ea07a523bb392fa7fe98df87c + with: + password: ${{ env.PYPI_AUTH_TOKEN }} + packages-dir: packages/ai-providers/server-ai-langchain/dist/ + + manual-publish: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + permissions: + id-token: write # Needed for OIDC to get release secrets from AWS. + contents: read # Needed for actions/checkout. + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install poetry + uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439 + + - uses: ./.github/actions/ci + with: + workspace_path: ${{ inputs.workspace_path }} + + - uses: ./.github/actions/build + id: build + with: + workspace_path: ${{ inputs.workspace_path }} + + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0 + if: ${{ inputs.dry_run != true }} + name: 'Get PyPI token' + with: + aws_assume_role: ${{ vars.AWS_ROLE_ARN }} + ssm_parameter_pairs: '/production/common/releasing/pypi/token = PYPI_AUTH_TOKEN' + + - name: Publish to PyPI + if: ${{ inputs.dry_run != true }} + # https://github.com/pypa/gh-action-pypi-publish/releases/tag/v1.8.13 - pinned to commit on 2024-04-13 + uses: pypa/gh-action-pypi-publish@3cc2c35166dfc1e5ea3bb0491ffdeedcaa50d7c with: - password: ${{env.PYPI_AUTH_TOKEN}} + password: ${{ env.PYPI_AUTH_TOKEN }} + packages-dir: ${{ inputs.workspace_path }}/dist/ + + release-server-ai-provenance: + needs: ['release-please', 'release-server-ai'] + if: ${{ needs.release-please.outputs.package-server-ai-released == 'true' }} + permissions: + actions: read # Needed for detecting the GitHub Actions environment. + id-token: write # Needed for provenance signing. + contents: write # Needed for uploading assets to the release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0 + with: + base64-subjects: "${{ needs.release-server-ai.outputs.package-hashes }}" + upload-assets: true + upload-tag-name: ${{ needs.release-please.outputs.package-server-ai-tag-name }} - release-provenance: - needs: [ 'release-package' ] - if: ${{ needs.release-package.outputs.release-created == 'true' }} + release-server-ai-langchain-provenance: + needs: ['release-please', 'release-server-ai-langchain'] + if: ${{ needs.release-please.outputs.package-server-ai-langchain-released == 'true' }} permissions: - actions: read - id-token: write - contents: write + actions: read # Needed for detecting the GitHub Actions environment. + id-token: write # Needed for provenance signing. + contents: write # Needed for uploading assets to the release. uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0 with: - base64-subjects: "${{ needs.release-package.outputs.package-hashes }}" + base64-subjects: "${{ needs.release-server-ai-langchain.outputs.package-hashes }}" upload-assets: true - upload-tag-name: ${{ needs.release-package.outputs.upload-tag-name }} + upload-tag-name: ${{ needs.release-please.outputs.package-server-ai-langchain-tag-name }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 30b6d45..7c26fcf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,4 @@ { - ".": "0.10.1" + "packages/sdk/server-ai": "0.10.1", + "packages/ai-providers/server-ai-langchain": "0.1.0" } diff --git a/Makefile b/Makefile index 791925c..31de23e 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ SPHINXPROJ = launchdarkly-server-sdk SOURCEDIR = docs BUILDDIR = $(SOURCEDIR)/build +# Package paths +SERVER_AI_PKG = packages/sdk/server-ai +LANGCHAIN_PKG = packages/ai-providers/server-ai-langchain + .PHONY: help help: #! Show this help message @echo 'Usage: make [target] ... ' @@ -13,25 +17,66 @@ help: #! Show this help message @echo 'Targets:' @grep -h -F '#!' $(MAKEFILE_LIST) | grep -v grep | sed 's/:.*#!/:/' | column -t -s":" +# +# Installation targets +# + .PHONY: install -install: - @poetry install +install: #! Install all packages + $(MAKE) -C $(SERVER_AI_PKG) install + $(MAKE) -C $(LANGCHAIN_PKG) install + +.PHONY: install-server-ai +install-server-ai: #! Install server-ai package + $(MAKE) -C $(SERVER_AI_PKG) install + +.PHONY: install-langchain +install-langchain: #! Install langchain provider package + $(MAKE) -C $(LANGCHAIN_PKG) install # # Quality control checks # .PHONY: test -test: #! Run unit tests -test: install - @poetry run pytest $(PYTEST_FLAGS) +test: #! Run unit tests for all packages + $(MAKE) -C $(SERVER_AI_PKG) test + +.PHONY: test-server-ai +test-server-ai: #! Run unit tests for server-ai package + $(MAKE) -C $(SERVER_AI_PKG) test + +.PHONY: test-langchain +test-langchain: #! Run unit tests for langchain provider package + $(MAKE) -C $(LANGCHAIN_PKG) test .PHONY: lint -lint: #! Run type analysis and linting checks -lint: install - @poetry run mypy ldai - @poetry run isort --check --atomic ldai - @poetry run pycodestyle ldai +lint: #! Run type analysis and linting checks for all packages + $(MAKE) -C $(SERVER_AI_PKG) lint + +.PHONY: lint-server-ai +lint-server-ai: #! Run type analysis and linting checks for server-ai package + $(MAKE) -C $(SERVER_AI_PKG) lint + +.PHONY: lint-langchain +lint-langchain: #! Run type analysis and linting checks for langchain provider package + $(MAKE) -C $(LANGCHAIN_PKG) lint + +# +# Build targets +# + +.PHONY: build +build: #! Build all packages + $(MAKE) -C $(SERVER_AI_PKG) build + +.PHONY: build-server-ai +build-server-ai: #! Build server-ai package + $(MAKE) -C $(SERVER_AI_PKG) build + +.PHONY: build-langchain +build-langchain: #! Build langchain provider package + $(MAKE) -C $(LANGCHAIN_PKG) build # # Documentation generation @@ -39,6 +84,4 @@ lint: install .PHONY: docs docs: #! Generate sphinx-based documentation - @poetry install --with docs - @cd docs - @poetry run $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + $(MAKE) -C $(SERVER_AI_PKG) docs DOCS_DIR=../../../$(SOURCEDIR) DOCS_BUILD_DIR=../../../$(BUILDDIR) diff --git a/docs/conf.py b/docs/conf.py index 20ca389..47061f1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,8 @@ import os import sys -sys.path.insert(0, os.path.abspath('..')) +# Add the server-ai package source to the path +sys.path.insert(0, os.path.abspath('../packages/sdk/server-ai/src')) import ldai diff --git a/ldai/testing/__init__.py b/ldai/testing/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packages/ai-providers/server-ai-langchain/Makefile b/packages/ai-providers/server-ai-langchain/Makefile new file mode 100644 index 0000000..2486d17 --- /dev/null +++ b/packages/ai-providers/server-ai-langchain/Makefile @@ -0,0 +1,28 @@ +PYTEST_FLAGS=-W error::SyntaxWarning + +.PHONY: help +help: #! Show this help message + @echo 'Usage: make [target] ... ' + @echo '' + @echo 'Targets:' + @grep -h -F '#!' $(MAKEFILE_LIST) | grep -v grep | sed 's/:.*#!/:/' | column -t -s":" + +.PHONY: install +install: #! Install package dependencies + poetry install + +.PHONY: test +test: #! Run unit tests +test: install + poetry run pytest $(PYTEST_FLAGS) + +.PHONY: lint +lint: #! Run type analysis and linting checks +lint: install + poetry run mypy src/ldai_langchain + poetry run pycodestyle src/ldai_langchain + +.PHONY: build +build: #! Build distribution files +build: install + poetry build diff --git a/packages/ai-providers/server-ai-langchain/README.md b/packages/ai-providers/server-ai-langchain/README.md new file mode 100644 index 0000000..f612c63 --- /dev/null +++ b/packages/ai-providers/server-ai-langchain/README.md @@ -0,0 +1,31 @@ +# LaunchDarkly AI SDK - LangChain Provider + +This package provides LangChain integration for the LaunchDarkly Server-Side AI SDK. + +## Status + +🚧 **Coming Soon** - This package is a placeholder for future LangChain integration. + +## Installation + +```bash +pip install launchdarkly-server-sdk-ai-langchain +``` + +## Usage + +```python +# Coming soon +``` + +## Documentation + +For full documentation, please refer to the [LaunchDarkly AI SDK documentation](https://docs.launchdarkly.com/sdk/ai/python). + +## Contributing + +See [CONTRIBUTING.md](../../../CONTRIBUTING.md) in the repository root. + +## License + +Apache-2.0 diff --git a/packages/ai-providers/server-ai-langchain/pyproject.toml b/packages/ai-providers/server-ai-langchain/pyproject.toml new file mode 100644 index 0000000..db792e1 --- /dev/null +++ b/packages/ai-providers/server-ai-langchain/pyproject.toml @@ -0,0 +1,51 @@ +[tool.poetry] +name = "launchdarkly-server-sdk-ai-langchain" +version = "0.1.0" +description = "LaunchDarkly AI SDK LangChain Provider" +authors = ["LaunchDarkly "] +license = "Apache-2.0" +readme = "README.md" +homepage = "https://docs.launchdarkly.com/sdk/ai/python" +repository = "https://github.com/launchdarkly/python-server-sdk-ai" +classifiers = [ + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", +] +packages = [{ include = "ldai_langchain", from = "src" }] + +[tool.poetry.dependencies] +python = ">=3.9,<4" +launchdarkly-server-sdk-ai = ">=0.10.1" +# langchain-core = ">=0.1.0" # Uncomment when implementing + + +[tool.poetry.group.dev.dependencies] +pytest = ">=2.8" +pytest-cov = ">=2.4.0" +pytest-asyncio = ">=0.21.0" +mypy = "==1.18.2" + +[tool.mypy] +python_version = "3.9" +ignore_missing_imports = true +install_types = true +non_interactive = true + + +[tool.pytest.ini_options] +addopts = ["-ra"] +testpaths = ["tests"] + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/packages/ai-providers/server-ai-langchain/src/ldai_langchain/__init__.py b/packages/ai-providers/server-ai-langchain/src/ldai_langchain/__init__.py new file mode 100644 index 0000000..bf15780 --- /dev/null +++ b/packages/ai-providers/server-ai-langchain/src/ldai_langchain/__init__.py @@ -0,0 +1,14 @@ +"""LaunchDarkly AI SDK - LangChain Provider. + +This package provides LangChain integration for the LaunchDarkly Server-Side AI SDK. +""" + +__version__ = "0.1.0" + +# Placeholder for future LangChain provider implementation +# from ldai_langchain.langchain_provider import LangChainProvider + +__all__ = [ + '__version__', + # 'LangChainProvider', # Uncomment when implemented +] diff --git a/packages/ai-providers/server-ai-langchain/tests/__init__.py b/packages/ai-providers/server-ai-langchain/tests/__init__.py new file mode 100644 index 0000000..c76e4c7 --- /dev/null +++ b/packages/ai-providers/server-ai-langchain/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for LangChain provider.""" diff --git a/packages/sdk/server-ai/Makefile b/packages/sdk/server-ai/Makefile new file mode 100644 index 0000000..95f037c --- /dev/null +++ b/packages/sdk/server-ai/Makefile @@ -0,0 +1,40 @@ +PYTEST_FLAGS=-W error::SyntaxWarning +SPHINXOPTS = -W --keep-going +SPHINXBUILD = sphinx-build + +# Configurable docs paths (can be overridden from root Makefile) +DOCS_DIR ?= docs +DOCS_BUILD_DIR ?= docs/build + +.PHONY: help +help: #! Show this help message + @echo 'Usage: make [target] ... ' + @echo '' + @echo 'Targets:' + @grep -h -F '#!' $(MAKEFILE_LIST) | grep -v grep | sed 's/:.*#!/:/' | column -t -s":" + +.PHONY: install +install: #! Install package dependencies + poetry install + +.PHONY: test +test: #! Run unit tests +test: install + poetry run pytest $(PYTEST_FLAGS) + +.PHONY: lint +lint: #! Run type analysis and linting checks +lint: install + poetry run mypy src/ldai + poetry run isort --check --atomic src/ldai + poetry run pycodestyle src/ldai + +.PHONY: build +build: #! Build distribution files +build: install + poetry build + +.PHONY: docs +docs: #! Generate sphinx-based documentation + poetry install --with docs + poetry run $(SPHINXBUILD) -M html "$(DOCS_DIR)" "$(DOCS_BUILD_DIR)" $(SPHINXOPTS) diff --git a/packages/sdk/server-ai/README.md b/packages/sdk/server-ai/README.md new file mode 100644 index 0000000..acd05d2 --- /dev/null +++ b/packages/sdk/server-ai/README.md @@ -0,0 +1,50 @@ +# LaunchDarkly Server-Side AI SDK for Python + +This package contains the LaunchDarkly Server-Side AI SDK for Python (`launchdarkly-server-sdk-ai`). + +## Installation + +```bash +pip install launchdarkly-server-sdk-ai +``` + +## Quick Start + +```python +from ldclient import LDClient, Config, Context +from ldai import LDAIClient, AICompletionConfigDefault, ModelConfig + +# Initialize LaunchDarkly client +ld_client = LDClient(Config("your-sdk-key")) + +# Create AI client +ai_client = LDAIClient(ld_client) + +# Get AI configuration +context = Context.create("user-123") +config = ai_client.completion_config( + "my-ai-config", + context, + AICompletionConfigDefault( + enabled=True, + model=ModelConfig("gpt-4") + ) +) + +# Use the configuration with your AI provider +if config.enabled: + # Your AI implementation here + pass +``` + +## Documentation + +For full documentation, please refer to the [LaunchDarkly AI SDK documentation](https://docs.launchdarkly.com/sdk/ai/python). + +## Contributing + +See [CONTRIBUTING.md](../../../CONTRIBUTING.md) in the repository root. + +## License + +Apache-2.0 diff --git a/packages/sdk/server-ai/pyproject.toml b/packages/sdk/server-ai/pyproject.toml new file mode 100644 index 0000000..426d93b --- /dev/null +++ b/packages/sdk/server-ai/pyproject.toml @@ -0,0 +1,70 @@ +[tool.poetry] +name = "launchdarkly-server-sdk-ai" +version = "0.10.1" +description = "LaunchDarkly SDK for AI" +authors = ["LaunchDarkly "] +license = "Apache-2.0" +readme = "README.md" +homepage = "https://docs.launchdarkly.com/sdk/ai/python" +repository = "https://github.com/launchdarkly/python-server-sdk-ai" +documentation = "https://launchdarkly-python-sdk-ai.readthedocs.io/en/latest/" +classifiers = [ + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries", +] +packages = [{ include = "ldai", from = "src" }] + +[tool.poetry.dependencies] +python = ">=3.9,<4" +launchdarkly-server-sdk = ">=9.4.0" +chevron = "=0.14.0" + + +[tool.poetry.group.dev.dependencies] +pytest = ">=2.8" +pytest-cov = ">=2.4.0" +pytest-mypy = "==1.0.1" +pytest-asyncio = ">=0.21.0" +mypy = "==1.18.2" +pycodestyle = "^2.12.1" +isort = ">=5.13.2,<7.0.0" + + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +sphinx = ">=6,<8" +sphinx-rtd-theme = ">=1.3,<4.0" +certifi = ">=2018.4.16" +expiringdict = ">=1.1.4" +pyrfc3339 = ">=1.0" +jsonpickle = ">1.4.1" +semver = ">=2.7.9" +urllib3 = ">=1.26.0" +jinja2 = "3.1.6" + +[tool.mypy] +python_version = "3.9" +ignore_missing_imports = true +install_types = true +non_interactive = true + + +[tool.pytest.ini_options] +addopts = ["-ra"] +testpaths = ["tests"] + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/packages/sdk/server-ai/setup.cfg b/packages/sdk/server-ai/setup.cfg new file mode 100644 index 0000000..6224f31 --- /dev/null +++ b/packages/sdk/server-ai/setup.cfg @@ -0,0 +1,2 @@ +[pycodestyle] +max-line-length = 120 diff --git a/ldai/__init__.py b/packages/sdk/server-ai/src/ldai/__init__.py similarity index 100% rename from ldai/__init__.py rename to packages/sdk/server-ai/src/ldai/__init__.py diff --git a/ldai/chat/__init__.py b/packages/sdk/server-ai/src/ldai/chat/__init__.py similarity index 100% rename from ldai/chat/__init__.py rename to packages/sdk/server-ai/src/ldai/chat/__init__.py diff --git a/ldai/client.py b/packages/sdk/server-ai/src/ldai/client.py similarity index 99% rename from ldai/client.py rename to packages/sdk/server-ai/src/ldai/client.py index ea07915..986be3d 100644 --- a/ldai/client.py +++ b/packages/sdk/server-ai/src/ldai/client.py @@ -444,7 +444,10 @@ def __evaluate( context: Context, default_dict: Dict[str, Any], variables: Optional[Dict[str, Any]] = None, - ) -> Tuple[Optional[ModelConfig], Optional[ProviderConfig], Optional[List[LDMessage]], Optional[str], LDAIConfigTracker, bool, Optional[Any]]: + ) -> Tuple[ + Optional[ModelConfig], Optional[ProviderConfig], Optional[List[LDMessage]], + Optional[str], LDAIConfigTracker, bool, Optional[Any] + ]: """ Internal method to evaluate a configuration and extract components. diff --git a/ldai/judge/__init__.py b/packages/sdk/server-ai/src/ldai/judge/__init__.py similarity index 100% rename from ldai/judge/__init__.py rename to packages/sdk/server-ai/src/ldai/judge/__init__.py diff --git a/ldai/judge/evaluation_schema_builder.py b/packages/sdk/server-ai/src/ldai/judge/evaluation_schema_builder.py similarity index 92% rename from ldai/judge/evaluation_schema_builder.py rename to packages/sdk/server-ai/src/ldai/judge/evaluation_schema_builder.py index c996f08..29b885d 100644 --- a/ldai/judge/evaluation_schema_builder.py +++ b/packages/sdk/server-ai/src/ldai/judge/evaluation_schema_builder.py @@ -24,7 +24,10 @@ def build(evaluation_metric_keys: list[str]) -> Dict[str, Any]: 'properties': { 'evaluations': { 'type': 'object', - 'description': f"Object containing evaluation results for {', '.join(evaluation_metric_keys)} metrics", + 'description': ( + f"Object containing evaluation results for " + f"{', '.join(evaluation_metric_keys)} metrics" + ), 'properties': EvaluationSchemaBuilder._build_key_properties(evaluation_metric_keys), 'required': evaluation_metric_keys, 'additionalProperties': False, diff --git a/ldai/models.py b/packages/sdk/server-ai/src/ldai/models.py similarity index 100% rename from ldai/models.py rename to packages/sdk/server-ai/src/ldai/models.py diff --git a/ldai/providers/__init__.py b/packages/sdk/server-ai/src/ldai/providers/__init__.py similarity index 100% rename from ldai/providers/__init__.py rename to packages/sdk/server-ai/src/ldai/providers/__init__.py diff --git a/ldai/providers/ai_provider.py b/packages/sdk/server-ai/src/ldai/providers/ai_provider.py similarity index 100% rename from ldai/providers/ai_provider.py rename to packages/sdk/server-ai/src/ldai/providers/ai_provider.py diff --git a/ldai/providers/ai_provider_factory.py b/packages/sdk/server-ai/src/ldai/providers/ai_provider_factory.py similarity index 97% rename from ldai/providers/ai_provider_factory.py rename to packages/sdk/server-ai/src/ldai/providers/ai_provider_factory.py index 3fd0f50..7419176 100644 --- a/ldai/providers/ai_provider_factory.py +++ b/packages/sdk/server-ai/src/ldai/providers/ai_provider_factory.py @@ -156,8 +156,9 @@ async def _create_provider( provider = await provider_class.create(ai_config, logger) if logger: + provider_name = ai_config.provider.name if ai_config.provider else 'unknown' logger.debug( - f"Successfully created AIProvider for: {ai_config.provider.name if ai_config.provider else 'unknown'} " + f"Successfully created AIProvider for: {provider_name} " f"with package {package_name}" ) return provider diff --git a/ldai/providers/types.py b/packages/sdk/server-ai/src/ldai/providers/types.py similarity index 100% rename from ldai/providers/types.py rename to packages/sdk/server-ai/src/ldai/providers/types.py diff --git a/ldai/tracker.py b/packages/sdk/server-ai/src/ldai/tracker.py similarity index 100% rename from ldai/tracker.py rename to packages/sdk/server-ai/src/ldai/tracker.py diff --git a/packages/sdk/server-ai/tests/__init__.py b/packages/sdk/server-ai/tests/__init__.py new file mode 100644 index 0000000..1ea3607 --- /dev/null +++ b/packages/sdk/server-ai/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for the LaunchDarkly Server-Side AI SDK.""" diff --git a/ldai/testing/test_agents.py b/packages/sdk/server-ai/tests/test_agents.py similarity index 100% rename from ldai/testing/test_agents.py rename to packages/sdk/server-ai/tests/test_agents.py diff --git a/ldai/testing/test_model_config.py b/packages/sdk/server-ai/tests/test_model_config.py similarity index 100% rename from ldai/testing/test_model_config.py rename to packages/sdk/server-ai/tests/test_model_config.py diff --git a/ldai/testing/test_tracker.py b/packages/sdk/server-ai/tests/test_tracker.py similarity index 100% rename from ldai/testing/test_tracker.py rename to packages/sdk/server-ai/tests/test_tracker.py diff --git a/pyproject.toml b/pyproject.toml index 9c1f44a..a38cffc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,71 +1,22 @@ +# Root pyproject.toml for the LaunchDarkly Python AI SDK monorepo +# +# This monorepo contains: +# - packages/sdk/server-ai: The main LaunchDarkly Server-Side AI SDK +# - packages/ai-providers/server-ai-langchain: LangChain integration (placeholder) +# +# For development, use the package-specific pyproject.toml files in: +# - packages/sdk/server-ai/pyproject.toml +# - packages/ai-providers/server-ai-langchain/pyproject.toml + [tool.poetry] -name = "launchdarkly-server-sdk-ai" -version = "0.10.1" -description = "LaunchDarkly SDK for AI" +name = "launchdarkly-python-sdk-ai-monorepo" +version = "0.0.0" +description = "LaunchDarkly Python AI SDK Monorepo" authors = ["LaunchDarkly "] license = "Apache-2.0" -readme = "README.md" -homepage = "https://docs.launchdarkly.com/sdk/ai/python" -repository = "https://github.com/launchdarkly/python-server-sdk-ai" -documentation = "https://launchdarkly-python-sdk-ai.readthedocs.io/en/latest/" -classifiers = [ - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Software Development", - "Topic :: Software Development :: Libraries", -] -packages = [ { include = "ldai" } ] -exclude = [ - { path = "ldai/testing", format = "wheel" } -] [tool.poetry.dependencies] python = ">=3.9,<4" -launchdarkly-server-sdk = ">=9.4.0" -chevron = "=0.14.0" - - -[tool.poetry.group.dev.dependencies] -pytest = ">=2.8" -pytest-cov = ">=2.4.0" -pytest-mypy = "==1.0.1" -pytest-asyncio = ">=0.21.0" -mypy = "==1.18.2" -pycodestyle = "^2.12.1" -isort = ">=5.13.2,<7.0.0" - - -[tool.poetry.group.docs] -optional = true - -[tool.poetry.group.docs.dependencies] -sphinx = ">=6,<8" -sphinx-rtd-theme = ">=1.3,<4.0" -certifi = ">=2018.4.16" -expiringdict = ">=1.1.4" -pyrfc3339 = ">=1.0" -jsonpickle = ">1.4.1" -semver = ">=2.7.9" -urllib3 = ">=1.26.0" -jinja2 = "3.1.6" - -[tool.mypy] -python_version = "3.9" -ignore_missing_imports = true -install_types = true -non_interactive = true - - -[tool.pytest.ini_options] -addopts = ["-ra"] - [build-system] requires = ["poetry-core"] diff --git a/release-please-config.json b/release-please-config.json index 78df6d7..9c556a9 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -1,12 +1,21 @@ { + "include-component-in-tag": true, "packages": { - ".": { + "packages/sdk/server-ai": { "release-type": "python", "versioning": "default", "bump-minor-pre-major": true, "include-v-in-tag": false, - "extra-files": ["ldai/__init__.py", "PROVENANCE.md"], - "include-component-in-tag": false + "extra-files": ["src/ldai/__init__.py", "../../../PROVENANCE.md"], + "component": "launchdarkly-server-sdk-ai" + }, + "packages/ai-providers/server-ai-langchain": { + "release-type": "python", + "versioning": "default", + "bump-minor-pre-major": true, + "include-v-in-tag": false, + "extra-files": ["src/ldai_langchain/__init__.py"], + "component": "launchdarkly-server-sdk-ai-langchain" } } } diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 1fb1827..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[pycodestyle] -ignore = E501,W503