Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,56 @@ jobs:

- name: Run tests
run: make -C packages/ai-providers/server-ai-langchain test

server-ai-openai-linux:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/ci
with:
workspace_path: packages/ai-providers/server-ai-openai
python_version: ${{ matrix.python-version }}

- uses: ./.github/actions/build
with:
workspace_path: packages/ai-providers/server-ai-openai

server-ai-openai-windows:
runs-on: windows-latest
defaults:
run:
shell: powershell

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

- name: Configure poetry for local virtualenvs
run: poetry config virtualenvs.in-project true

- name: Install server-ai dependency first
working-directory: packages/sdk/server-ai
run: poetry install

- name: Install requirements
working-directory: packages/ai-providers/server-ai-openai
run: poetry install

- name: Run tests
run: make -C packages/ai-providers/server-ai-openai test
57 changes: 57 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ on:
options:
- packages/sdk/server-ai
- packages/ai-providers/server-ai-langchain
- packages/ai-providers/server-ai-openai
dry_run:
description: 'Is this a dry run. If so no package will be published.'
type: boolean
Expand All @@ -43,6 +44,8 @@ jobs:
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'] }}
package-server-ai-openai-released: ${{ steps.release.outputs['packages/ai-providers/server-ai-openai--release_created'] }}
package-server-ai-openai-tag-name: ${{ steps.release.outputs['packages/ai-providers/server-ai-openai--tag_name'] }}
steps:
- uses: googleapis/release-please-action@v4
id: release
Expand Down Expand Up @@ -193,3 +196,57 @@ jobs:
base64-subjects: "${{ needs.release-server-ai-langchain.outputs.package-hashes }}"
upload-assets: true
upload-tag-name: ${{ needs.release-please.outputs.package-server-ai-langchain-tag-name }}

release-server-ai-openai:
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-openai-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-openai

- uses: ./.github/actions/build
id: build
with:
workspace_path: packages/ai-providers/server-ai-openai

- 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 to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
password: ${{ env.PYPI_AUTH_TOKEN }}
packages-dir: packages/ai-providers/server-ai-openai/dist/

release-server-ai-openai-provenance:
needs: ['release-please', 'release-server-ai-openai']
if: ${{ needs.release-please.outputs.package-server-ai-openai-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-openai.outputs.package-hashes }}"
upload-assets: true
upload-tag-name: ${{ needs.release-please.outputs.package-server-ai-openai-tag-name }}
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"packages/sdk/server-ai": "0.11.0",
"packages/ai-providers/server-ai-langchain": "0.2.0"
"packages/ai-providers/server-ai-langchain": "0.2.0",
"packages/ai-providers/server-ai-openai": "0.1.0"
}
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ BUILDDIR = $(SOURCEDIR)/build
# Package paths
SERVER_AI_PKG = packages/sdk/server-ai
LANGCHAIN_PKG = packages/ai-providers/server-ai-langchain
OPENAI_PKG = packages/ai-providers/server-ai-openai

.PHONY: help
help: #! Show this help message
Expand All @@ -25,6 +26,7 @@ help: #! Show this help message
install: #! Install all packages
$(MAKE) install-server-ai
$(MAKE) install-langchain
$(MAKE) install-openai

.PHONY: install-server-ai
install-server-ai: #! Install server-ai package
Expand All @@ -34,6 +36,10 @@ install-server-ai: #! Install server-ai package
install-langchain: #! Install langchain provider package
$(MAKE) -C $(LANGCHAIN_PKG) install

.PHONY: install-openai
install-openai: #! Install openai provider package
$(MAKE) -C $(OPENAI_PKG) install

#
# Quality control checks
#
Expand All @@ -42,6 +48,7 @@ install-langchain: #! Install langchain provider package
test: #! Run unit tests for all packages
$(MAKE) test-server-ai
$(MAKE) test-langchain
$(MAKE) test-openai

.PHONY: test-server-ai
test-server-ai: #! Run unit tests for server-ai package
Expand All @@ -51,10 +58,15 @@ test-server-ai: #! Run unit tests for server-ai package
test-langchain: #! Run unit tests for langchain provider package
$(MAKE) -C $(LANGCHAIN_PKG) test

.PHONY: test-openai
test-openai: #! Run unit tests for openai provider package
$(MAKE) -C $(OPENAI_PKG) test

.PHONY: lint
lint: #! Run type analysis and linting checks for all packages
$(MAKE) lint-server-ai
$(MAKE) lint-langchain
$(MAKE) lint-openai

.PHONY: lint-server-ai
lint-server-ai: #! Run type analysis and linting checks for server-ai package
Expand All @@ -64,6 +76,10 @@ lint-server-ai: #! Run type analysis and linting checks for server-ai package
lint-langchain: #! Run type analysis and linting checks for langchain provider package
$(MAKE) -C $(LANGCHAIN_PKG) lint

.PHONY: lint-openai
lint-openai: #! Run type analysis and linting checks for openai provider package
$(MAKE) -C $(OPENAI_PKG) lint

#
# Build targets
#
Expand All @@ -72,6 +88,7 @@ lint-langchain: #! Run type analysis and linting checks for langchain provider p
build: #! Build all packages
$(MAKE) build-server-ai
$(MAKE) build-langchain
$(MAKE) build-openai

.PHONY: build-server-ai
build-server-ai: #! Build server-ai package
Expand All @@ -81,6 +98,10 @@ build-server-ai: #! Build server-ai package
build-langchain: #! Build langchain provider package
$(MAKE) -C $(LANGCHAIN_PKG) build

.PHONY: build-openai
build-openai: #! Build openai provider package
$(MAKE) -C $(OPENAI_PKG) build

#
# Documentation generation
#
Expand Down
30 changes: 30 additions & 0 deletions packages/ai-providers/server-ai-openai/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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_openai
poetry run isort --check --atomic src/ldai_openai
poetry run pycodestyle src/ldai_openai

.PHONY: build
build: #! Build distribution files
build: install
poetry build

72 changes: 72 additions & 0 deletions packages/ai-providers/server-ai-openai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# LaunchDarkly AI SDK OpenAI Provider

[![PyPI](https://img.shields.io/pypi/v/launchdarkly-server-sdk-ai-openai-dev.svg?style=flat-square)](https://pypi.org/project/launchdarkly-server-sdk-ai-openai-dev/)

This package provides an OpenAI integration for the LaunchDarkly AI SDK.

## Installation

```bash
pip install launchdarkly-server-sdk-ai-openai-dev
```

## Quick Start

```python
import asyncio
from ldai import AIClient
from ldai_openai import OpenAIProvider

async def main():
# Initialize the AI client
ai_client = AIClient(ld_client)

# Get AI config
ai_config = ai_client.config(
"my-ai-config-key",
context,
default_value
)

# Create an OpenAI provider from the config
provider = await OpenAIProvider.create(ai_config)

# Invoke the model
response = await provider.invoke_model(ai_config.messages)
print(response.message.content)

asyncio.run(main())
```

## Features

- Full integration with OpenAI's chat completions API
- Automatic token usage tracking
- Support for structured output (JSON schema)
- Static utility methods for custom integrations

## API Reference

### OpenAIProvider

#### Constructor

```python
OpenAIProvider(client: OpenAI, model_name: str, parameters: Dict[str, Any], logger: Optional[Any] = None)
```

#### Static Methods

- `create(ai_config: AIConfigKind, logger: Optional[Any] = None) -> OpenAIProvider` - Factory method to create a provider from an AI config
- `get_ai_metrics_from_response(response: Any) -> LDAIMetrics` - Extract metrics from an OpenAI response

#### Instance Methods

- `invoke_model(messages: List[LDMessage]) -> ChatResponse` - Invoke the model with messages
- `invoke_structured_model(messages: List[LDMessage], response_structure: Dict[str, Any]) -> StructuredResponse` - Invoke the model with structured output
- `get_client() -> OpenAI` - Get the underlying OpenAI client

## License

Apache-2.0

59 changes: 59 additions & 0 deletions packages/ai-providers/server-ai-openai/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[tool.poetry]
name = "launchdarkly-server-sdk-ai-openai"
version = "0.0.0"
description = "LaunchDarkly AI SDK OpenAI Provider"
authors = ["LaunchDarkly <dev@launchdarkly.com>"]
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_openai", from = "src" }]

[tool.poetry.dependencies]
python = ">=3.9,<4"
launchdarkly-server-sdk-ai = ">=0.11.0"
openai = ">=1.0.0"

[tool.poetry.group.dev.dependencies]
pytest = ">=2.8"
pytest-cov = ">=2.4.0"
pytest-asyncio = ">=0.21.0,<1.0.0"
mypy = "==1.18.2"
pycodestyle = ">=2.11.0"
isort = ">=5.12.0"

[tool.mypy]
python_version = "3.9"
ignore_missing_imports = true
install_types = true
non_interactive = true

[tool.isort]
profile = "black"
known_third_party = ["openai", "ldai"]
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]


[tool.pytest.ini_options]
addopts = ["-ra"]
testpaths = ["tests"]
asyncio_mode = "auto"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

2 changes: 2 additions & 0 deletions packages/ai-providers/server-ai-openai/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pycodestyle]
max-line-length = 120
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""LaunchDarkly AI SDK OpenAI Provider."""

from ldai_openai.openai_provider import OpenAIProvider

__all__ = ['OpenAIProvider']
Loading
Loading