Skip to content

Commit ec2272e

Browse files
authored
feat: Add OpenAI provider package (#78)
fix!: Bump requirement of launchdarkly-server-sdk-ai to 0.12.0
1 parent 8e5c5f6 commit ec2272e

File tree

13 files changed

+850
-1
lines changed

13 files changed

+850
-1
lines changed

.github/workflows/ci.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,56 @@ jobs:
111111

112112
- name: Run tests
113113
run: make -C packages/ai-providers/server-ai-langchain test
114+
115+
server-ai-openai-linux:
116+
runs-on: ubuntu-latest
117+
strategy:
118+
matrix:
119+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
120+
121+
steps:
122+
- uses: actions/checkout@v4
123+
124+
- uses: ./.github/actions/ci
125+
with:
126+
workspace_path: packages/ai-providers/server-ai-openai
127+
python_version: ${{ matrix.python-version }}
128+
129+
- uses: ./.github/actions/build
130+
with:
131+
workspace_path: packages/ai-providers/server-ai-openai
132+
133+
server-ai-openai-windows:
134+
runs-on: windows-latest
135+
defaults:
136+
run:
137+
shell: powershell
138+
139+
strategy:
140+
matrix:
141+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
142+
143+
steps:
144+
- uses: actions/checkout@v4
145+
146+
- name: Set up Python ${{ matrix.python-version }}
147+
uses: actions/setup-python@v5
148+
with:
149+
python-version: ${{ matrix.python-version }}
150+
151+
- name: Install poetry
152+
uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439
153+
154+
- name: Configure poetry for local virtualenvs
155+
run: poetry config virtualenvs.in-project true
156+
157+
- name: Install server-ai dependency first
158+
working-directory: packages/sdk/server-ai
159+
run: poetry install
160+
161+
- name: Install requirements
162+
working-directory: packages/ai-providers/server-ai-openai
163+
run: poetry install
164+
165+
- name: Run tests
166+
run: make -C packages/ai-providers/server-ai-openai test

.github/workflows/release-please.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ on:
2626
options:
2727
- packages/sdk/server-ai
2828
- packages/ai-providers/server-ai-langchain
29+
- packages/ai-providers/server-ai-openai
2930
dry_run:
3031
description: 'Is this a dry run. If so no package will be published.'
3132
type: boolean
@@ -43,6 +44,8 @@ jobs:
4344
package-server-ai-tag-name: ${{ steps.release.outputs['packages/sdk/server-ai--tag_name'] }}
4445
package-server-ai-langchain-released: ${{ steps.release.outputs['packages/ai-providers/server-ai-langchain--release_created'] }}
4546
package-server-ai-langchain-tag-name: ${{ steps.release.outputs['packages/ai-providers/server-ai-langchain--tag_name'] }}
47+
package-server-ai-openai-released: ${{ steps.release.outputs['packages/ai-providers/server-ai-openai--release_created'] }}
48+
package-server-ai-openai-tag-name: ${{ steps.release.outputs['packages/ai-providers/server-ai-openai--tag_name'] }}
4649
steps:
4750
- uses: googleapis/release-please-action@v4
4851
id: release
@@ -193,3 +196,57 @@ jobs:
193196
base64-subjects: "${{ needs.release-server-ai-langchain.outputs.package-hashes }}"
194197
upload-assets: true
195198
upload-tag-name: ${{ needs.release-please.outputs.package-server-ai-langchain-tag-name }}
199+
200+
release-server-ai-openai:
201+
runs-on: ubuntu-latest
202+
needs: ['release-please']
203+
permissions:
204+
id-token: write # Needed for OIDC to get release secrets from AWS.
205+
if: ${{ needs.release-please.outputs.package-server-ai-openai-released == 'true' }}
206+
outputs:
207+
package-hashes: ${{ steps.build.outputs.package-hashes }}
208+
steps:
209+
- uses: actions/checkout@v4
210+
with:
211+
fetch-depth: 0
212+
213+
- uses: actions/setup-python@v5
214+
with:
215+
python-version: '3.11'
216+
217+
- name: Install poetry
218+
uses: abatilo/actions-poetry@7b6d33e44b4f08d7021a1dee3c044e9c253d6439
219+
220+
- uses: ./.github/actions/ci
221+
with:
222+
workspace_path: packages/ai-providers/server-ai-openai
223+
224+
- uses: ./.github/actions/build
225+
id: build
226+
with:
227+
workspace_path: packages/ai-providers/server-ai-openai
228+
229+
- uses: launchdarkly/gh-actions/actions/[email protected]
230+
name: 'Get PyPI token'
231+
with:
232+
aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
233+
ssm_parameter_pairs: '/production/common/releasing/pypi/token = PYPI_AUTH_TOKEN'
234+
235+
- name: Publish to PyPI
236+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
237+
with:
238+
password: ${{ env.PYPI_AUTH_TOKEN }}
239+
packages-dir: packages/ai-providers/server-ai-openai/dist/
240+
241+
release-server-ai-openai-provenance:
242+
needs: ['release-please', 'release-server-ai-openai']
243+
if: ${{ needs.release-please.outputs.package-server-ai-openai-released == 'true' }}
244+
permissions:
245+
actions: read # Needed for detecting the GitHub Actions environment.
246+
id-token: write # Needed for provenance signing.
247+
contents: write # Needed for uploading assets to the release.
248+
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
249+
with:
250+
base64-subjects: "${{ needs.release-server-ai-openai.outputs.package-hashes }}"
251+
upload-assets: true
252+
upload-tag-name: ${{ needs.release-please.outputs.package-server-ai-openai-tag-name }}

.release-please-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"packages/sdk/server-ai": "0.12.0",
3-
"packages/ai-providers/server-ai-langchain": "0.3.0"
3+
"packages/ai-providers/server-ai-langchain": "0.3.0",
4+
"packages/ai-providers/server-ai-openai": "0.0.0"
45
}

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ BUILDDIR = $(SOURCEDIR)/build
99
# Package paths
1010
SERVER_AI_PKG = packages/sdk/server-ai
1111
LANGCHAIN_PKG = packages/ai-providers/server-ai-langchain
12+
OPENAI_PKG = packages/ai-providers/server-ai-openai
1213

1314
.PHONY: help
1415
help: #! Show this help message
@@ -25,6 +26,7 @@ help: #! Show this help message
2526
install: #! Install all packages
2627
$(MAKE) install-server-ai
2728
$(MAKE) install-langchain
29+
$(MAKE) install-openai
2830

2931
.PHONY: install-server-ai
3032
install-server-ai: #! Install server-ai package
@@ -34,6 +36,10 @@ install-server-ai: #! Install server-ai package
3436
install-langchain: #! Install langchain provider package
3537
$(MAKE) -C $(LANGCHAIN_PKG) install
3638

39+
.PHONY: install-openai
40+
install-openai: #! Install openai provider package
41+
$(MAKE) -C $(OPENAI_PKG) install
42+
3743
#
3844
# Quality control checks
3945
#
@@ -42,6 +48,7 @@ install-langchain: #! Install langchain provider package
4248
test: #! Run unit tests for all packages
4349
$(MAKE) test-server-ai
4450
$(MAKE) test-langchain
51+
$(MAKE) test-openai
4552

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

61+
.PHONY: test-openai
62+
test-openai: #! Run unit tests for openai provider package
63+
$(MAKE) -C $(OPENAI_PKG) test
64+
5465
.PHONY: lint
5566
lint: #! Run type analysis and linting checks for all packages
5667
$(MAKE) lint-server-ai
5768
$(MAKE) lint-langchain
69+
$(MAKE) lint-openai
5870

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

79+
.PHONY: lint-openai
80+
lint-openai: #! Run type analysis and linting checks for openai provider package
81+
$(MAKE) -C $(OPENAI_PKG) lint
82+
6783
#
6884
# Build targets
6985
#
@@ -72,6 +88,7 @@ lint-langchain: #! Run type analysis and linting checks for langchain provider p
7288
build: #! Build all packages
7389
$(MAKE) build-server-ai
7490
$(MAKE) build-langchain
91+
$(MAKE) build-openai
7592

7693
.PHONY: build-server-ai
7794
build-server-ai: #! Build server-ai package
@@ -81,6 +98,10 @@ build-server-ai: #! Build server-ai package
8198
build-langchain: #! Build langchain provider package
8299
$(MAKE) -C $(LANGCHAIN_PKG) build
83100

101+
.PHONY: build-openai
102+
build-openai: #! Build openai provider package
103+
$(MAKE) -C $(OPENAI_PKG) build
104+
84105
#
85106
# Documentation generation
86107
#
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
PYTEST_FLAGS=-W error::SyntaxWarning
2+
3+
.PHONY: help
4+
help: #! Show this help message
5+
@echo 'Usage: make [target] ... '
6+
@echo ''
7+
@echo 'Targets:'
8+
@grep -h -F '#!' $(MAKEFILE_LIST) | grep -v grep | sed 's/:.*#!/:/' | column -t -s":"
9+
10+
.PHONY: install
11+
install: #! Install package dependencies
12+
poetry install
13+
14+
.PHONY: test
15+
test: #! Run unit tests
16+
test: install
17+
poetry run pytest $(PYTEST_FLAGS)
18+
19+
.PHONY: lint
20+
lint: #! Run type analysis and linting checks
21+
lint: install
22+
poetry run mypy src/ldai_openai
23+
poetry run isort --check --atomic src/ldai_openai
24+
poetry run pycodestyle src/ldai_openai
25+
26+
.PHONY: build
27+
build: #! Build distribution files
28+
build: install
29+
poetry build
30+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# LaunchDarkly AI SDK OpenAI Provider
2+
3+
[![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/)
4+
5+
This package provides an OpenAI integration for the LaunchDarkly AI SDK.
6+
7+
## Installation
8+
9+
```bash
10+
pip install launchdarkly-server-sdk-ai-openai-dev
11+
```
12+
13+
## Quick Start
14+
15+
```python
16+
import asyncio
17+
from ldai import AIClient
18+
from ldai_openai import OpenAIProvider
19+
20+
async def main():
21+
# Initialize the AI client
22+
ai_client = AIClient(ld_client)
23+
24+
# Get AI config
25+
ai_config = ai_client.config(
26+
"my-ai-config-key",
27+
context,
28+
default_value
29+
)
30+
31+
# Create an OpenAI provider from the config
32+
provider = await OpenAIProvider.create(ai_config)
33+
34+
# Invoke the model
35+
response = await provider.invoke_model(ai_config.messages)
36+
print(response.message.content)
37+
38+
asyncio.run(main())
39+
```
40+
41+
## Features
42+
43+
- Full integration with OpenAI's chat completions API
44+
- Automatic token usage tracking
45+
- Support for structured output (JSON schema)
46+
- Static utility methods for custom integrations
47+
48+
## API Reference
49+
50+
### OpenAIProvider
51+
52+
#### Constructor
53+
54+
```python
55+
OpenAIProvider(client: OpenAI, model_name: str, parameters: Dict[str, Any], logger: Optional[Any] = None)
56+
```
57+
58+
#### Static Methods
59+
60+
- `create(ai_config: AIConfigKind, logger: Optional[Any] = None) -> OpenAIProvider` - Factory method to create a provider from an AI config
61+
- `get_ai_metrics_from_response(response: Any) -> LDAIMetrics` - Extract metrics from an OpenAI response
62+
63+
#### Instance Methods
64+
65+
- `invoke_model(messages: List[LDMessage]) -> ChatResponse` - Invoke the model with messages
66+
- `invoke_structured_model(messages: List[LDMessage], response_structure: Dict[str, Any]) -> StructuredResponse` - Invoke the model with structured output
67+
- `get_client() -> OpenAI` - Get the underlying OpenAI client
68+
69+
## License
70+
71+
Apache-2.0
72+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
[tool.poetry]
2+
name = "launchdarkly-server-sdk-ai-openai"
3+
version = "0.0.0"
4+
description = "LaunchDarkly AI SDK OpenAI Provider"
5+
authors = ["LaunchDarkly <[email protected]>"]
6+
license = "Apache-2.0"
7+
readme = "README.md"
8+
homepage = "https://docs.launchdarkly.com/sdk/ai/python"
9+
repository = "https://github.com/launchdarkly/python-server-sdk-ai"
10+
classifiers = [
11+
"Intended Audience :: Developers",
12+
"License :: OSI Approved :: Apache Software License",
13+
"Operating System :: OS Independent",
14+
"Programming Language :: Python :: 3",
15+
"Programming Language :: Python :: 3.9",
16+
"Programming Language :: Python :: 3.10",
17+
"Programming Language :: Python :: 3.11",
18+
"Programming Language :: Python :: 3.12",
19+
"Programming Language :: Python :: 3.13",
20+
"Topic :: Software Development",
21+
"Topic :: Software Development :: Libraries",
22+
]
23+
packages = [{ include = "ldai_openai", from = "src" }]
24+
25+
[tool.poetry.dependencies]
26+
python = ">=3.9,<4"
27+
launchdarkly-server-sdk-ai = ">=0.12.0"
28+
openai = ">=1.0.0"
29+
30+
[tool.poetry.group.dev.dependencies]
31+
pytest = ">=2.8"
32+
pytest-cov = ">=2.4.0"
33+
pytest-asyncio = ">=0.21.0,<1.0.0"
34+
mypy = "==1.18.2"
35+
pycodestyle = ">=2.11.0"
36+
isort = ">=5.12.0"
37+
38+
[tool.mypy]
39+
python_version = "3.9"
40+
ignore_missing_imports = true
41+
install_types = true
42+
non_interactive = true
43+
44+
[tool.isort]
45+
profile = "black"
46+
known_third_party = ["openai", "ldai"]
47+
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
48+
49+
50+
[tool.pytest.ini_options]
51+
addopts = ["-ra"]
52+
testpaths = ["tests"]
53+
asyncio_mode = "auto"
54+
55+
56+
[build-system]
57+
requires = ["poetry-core"]
58+
build-backend = "poetry.core.masonry.api"
59+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[pycodestyle]
2+
max-line-length = 120
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""LaunchDarkly AI SDK OpenAI Provider."""
2+
3+
from ldai_openai.openai_provider import OpenAIProvider
4+
5+
__all__ = ['OpenAIProvider']

0 commit comments

Comments
 (0)