Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
9859d08
feat: implements optimize method in SDK, code moved
andrewklatzke Mar 26, 2026
1712e4f
feat: implementation of agent optimization + tests
andrewklatzke Mar 31, 2026
ea596a7
feat: implement ability to use completions or agents for judge calls
andrewklatzke Mar 31, 2026
2fd55e2
feat: all logs -> debug
andrewklatzke Mar 31, 2026
8481690
fix: lints + structured output tool rename
andrewklatzke Mar 31, 2026
f8e5509
fix: lint + missed variable rename
andrewklatzke Mar 31, 2026
c032aaf
fix: sort imports
andrewklatzke Mar 31, 2026
aee6aa7
fix: lint
andrewklatzke Mar 31, 2026
59c7ac7
chore: break up long lines, add spaces where necessary
andrewklatzke Mar 31, 2026
59f03f2
chore: break up another long line
andrewklatzke Mar 31, 2026
e2ff561
chore: fix on_turn path
andrewklatzke Mar 31, 2026
af2dd03
chore: move prompts to own file, better debug info
andrewklatzke Mar 31, 2026
ea43575
chore: update tests, fix cursor feedback
andrewklatzke Mar 31, 2026
2fecd54
feat: implements LD API client, optimize_from_config path
andrewklatzke Apr 1, 2026
d3e1f96
feat: partially implement optimize_from_config
andrewklatzke Apr 1, 2026
44c8c59
feat: ground truth optimization path
andrewklatzke Apr 3, 2026
8f9f1e2
feat: prevent overfitting via prompt changes and post-processing
andrewklatzke Apr 7, 2026
a17fd6e
chore: remove some dead code
andrewklatzke Apr 7, 2026
67fdbf1
chore: remove provided_tool_handlers code
andrewklatzke Apr 7, 2026
3042984
fix: adjust iteration logic so validation doesn't consume them
andrewklatzke Apr 8, 2026
288336e
feat: implement latency & token tracking for optimizations
andrewklatzke Apr 8, 2026
5d76276
feat: add optimization for duration
andrewklatzke Apr 8, 2026
4cb8859
feat: add auto-commit option
andrewklatzke Apr 9, 2026
ba369a2
chore: add tests
andrewklatzke Apr 9, 2026
149aa76
chore: various fixes, improvements for optimization package
andrewklatzke Apr 15, 2026
31c8385
feat: add shared dataclass for calls so they can be handled by same h…
andrewklatzke Apr 15, 2026
55674ae
chore: improve call config, context so they're passable as a single t…
andrewklatzke Apr 16, 2026
8f3468f
fix: success path + add test, cursor feedback
andrewklatzke Apr 16, 2026
7074cfa
feat: dx improvements for optimization package
andrewklatzke Apr 16, 2026
a386a27
chore: update types for lint
andrewklatzke Apr 17, 2026
8d1a868
lint, cursor feedback
andrewklatzke Apr 17, 2026
937542a
chore: additional lint
andrewklatzke Apr 17, 2026
8b3c69f
chore: rename package, imports, and address security review
andrewklatzke Apr 22, 2026
f7ce6d3
merge + conflicts
andrewklatzke Apr 22, 2026
7468372
chore: cursor feedback, remove unnecessary log of response
andrewklatzke Apr 22, 2026
a387f83
chore: fix readme inconsistency
andrewklatzke Apr 22, 2026
312161f
chore: add retry logic and structural validation for variations
andrewklatzke Apr 22, 2026
542c135
chore: remove coolnames, implement generate_slug() cleanup unused files
andrewklatzke Apr 22, 2026
3bce893
chore: removes cleanup unused tool function, remove additionalProperties
andrewklatzke Apr 22, 2026
e8c6692
feat: add token limit handling
andrewklatzke Apr 23, 2026
572a2aa
fix: better handling of params and custom params for optimization
andrewklatzke May 4, 2026
0ed243e
feat: adds ability to use inverted judges
andrewklatzke May 5, 2026
a8f14de
feat: use judge_passed for all calcs
andrewklatzke May 5, 2026
23baeb4
feat: adds ability to use inverted judges (#168)
andrewklatzke May 6, 2026
94de596
feat: adds ability to optimize for cost
andrewklatzke May 6, 2026
dc82818
fix: remove unnecessary token path
andrewklatzke May 6, 2026
92f51fa
fix: better handling of params and custom params for optimization (#163)
andrewklatzke May 7, 2026
365fa94
feat: adds reporting for cost and latency optimization failures
andrewklatzke May 7, 2026
d267832
feat: adds reporting for cost and latency optimization failures (#180)
andrewklatzke May 12, 2026
9bedf9e
fix: don't only evaluate final input in GT results
andrewklatzke May 13, 2026
53f455f
fix: don't only evaluate final input in GT results
andrewklatzke May 13, 2026
66bc1f0
fix: only strip known provider prefixes
andrewklatzke May 13, 2026
f2f0894
fix: address cursor feedback
andrewklatzke May 14, 2026
9c1d8d7
chore: adjust package name
andrewklatzke May 18, 2026
4fc1ecf
fix: pull model configs if available in options path
andrewklatzke May 18, 2026
32dc4d0
chore: (optimization) add provenance file + register with release please
andrewklatzke May 18, 2026
3b4baa3
feat: adds ability to optimize for cost (#172)
andrewklatzke May 18, 2026
d15c6bc
chore: (optimization) add provenance file + register with release ple…
andrewklatzke May 18, 2026
99dd7b8
Merge branch 'main' into aklatzke/AIC-2263/sdk-dx-improvements
andrewklatzke May 20, 2026
849d41f
chore: lint
andrewklatzke May 20, 2026
c51e235
Merge branch 'aklatzke/AIC-2263/sdk-dx-improvements' of github.com:la…
andrewklatzke May 20, 2026
f481ed3
chore: isort
andrewklatzke May 20, 2026
27b248e
chore: line lengths
andrewklatzke May 20, 2026
4545203
chore: fix agent init typing
andrewklatzke May 20, 2026
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
6 changes: 3 additions & 3 deletions packages/optimization/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ test: install
.PHONY: lint
lint: #! Run type analysis and linting checks
lint: install
uv run mypy src/ldai_optimization
uv run isort --check --atomic src/ldai_optimization
uv run pycodestyle src/ldai_optimization
uv run mypy src/ldai_optimizer
uv run isort --check --atomic src/ldai_optimizer
uv run pycodestyle src/ldai_optimizer

.PHONY: build
build: #! Build distribution files
Expand Down
49 changes: 49 additions & 0 deletions packages/optimization/PROVENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Verifying SDK build provenance with GitHub artifact attestations

LaunchDarkly uses [GitHub artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages.

LaunchDarkly publishes provenance about our SDK package builds using [GitHub's `actions/attest` action](https://github.com/actions/attest). These attestations are stored in GitHub's attestation API and can be verified using the [GitHub CLI](https://cli.github.com/).

To verify build provenance attestations, we recommend using the [GitHub CLI `attestation verify` command](https://cli.github.com/manual/gh_attestation_verify). Example usage for verifying SDK packages is included below:

<!-- x-release-please-start-version -->
```
# Set the version of the library to verify
VERSION=0.1.0
```
<!-- x-release-please-end -->

```
# Download package from PyPI
$ pip download --only-binary=:all: launchdarkly-ai-optimizer==${VERSION}

# Verify provenance using the GitHub CLI
$ gh attestation verify launchdarkly_ai_optimizer-${VERSION}-py3-none-any.whl --owner launchdarkly
```

Below is a sample of expected output.

```
Loaded digest sha256:... for file://launchdarkly_ai_optimizer-0.1.0-py3-none-any.whl
Loaded 1 attestation from GitHub API

The following policy criteria will be enforced:
- Predicate type must match:................ https://slsa.dev/provenance/v1
- Source Repository Owner URI must match:... https://github.com/launchdarkly
- Subject Alternative Name must match regex: (?i)^https://github.com/launchdarkly/
- OIDC Issuer must match:................... https://token.actions.githubusercontent.com

✓ Verification succeeded!

The following 1 attestation matched the policy criteria

- Attestation #1
- Build repo:..... launchdarkly/python-server-sdk-ai
- Build workflow:. .github/workflows/release-please.yml
- Signer repo:.... launchdarkly/python-server-sdk-ai
- Signer workflow: .github/workflows/release-please.yml
```

For more information, see [GitHub's documentation on verifying artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds#verifying-artifact-attestations-with-the-github-cli).

**Note:** These instructions do not apply when building our libraries from source.
118 changes: 110 additions & 8 deletions packages/optimization/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,129 @@
# LaunchDarkly AI SDK — optimization

[![Actions Status](https://github.com/launchdarkly/python-server-sdk-ai/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/launchdarkly/python-server-sdk-ai/actions/workflows/ci.yml)

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

> [!CAUTION]
> This package is in pre-release and not subject to backwards compatibility
> guarantees. The API may change based on feedback.
>
> Pin to a specific minor version and review the [changelog](CHANGELOG.md) before upgrading.

This package will provide helpers to run selected tools against the [LaunchDarkly API](https://apidocs.launchdarkly.com/) from SDK-based workflows. The public surface is not yet finalized; see [CHANGELOG.md](CHANGELOG.md) for updates.
This package provides helpers for running iterative AI prompt optimization workflows from within LaunchDarkly SDK-based applications. It drives the optimization loop — generating candidate variations, evaluating them with judges, and optionally committing winners back to LaunchDarkly — while delegating all LLM calls to your own handler functions.

## Requirements

- Python `>=3.9`
- A configured [LaunchDarkly server-side SDK](https://docs.launchdarkly.com/sdk/server-side/python) client
- The [LaunchDarkly AI package](https://pypi.org/project/launchdarkly-server-sdk-ai/) (`launchdarkly-server-sdk-ai>=0.16.0`) — pulled in automatically as a dependency
- **`LAUNCHDARKLY_API_KEY` environment variable** — required only when using `auto_commit=True` or `optimize_from_config`. Not needed for basic `optimize_from_options` runs without auto-commit.

> [!NOTE]
> **`LAUNCHDARKLY_API_KEY` is used exclusively for discrete LaunchDarkly REST API calls** (fetching configs, publishing results). It is never included in any LLM prompt and is never forwarded to your handler callbacks. All API calls made by this package are isolated; they have no access to your runtime environment beyond the key you explicitly provide via the environment variable.

## Installation

```bash
pip install launchdarkly-server-sdk-ai-optimization
pip install ldai_optimizer
```

## Status
## Quick Start

### Basic optimization (`optimize_from_options`)

- 3/24/26: Initial package creation
No `LAUNCHDARKLY_API_KEY` required unless `auto_commit=True`.

```python
import ldclient
from ldai import LDAIClient
from ldai_optimizer import (
OptimizationClient,
OptimizationJudge,
OptimizationOptions,
OptimizationResponse,
LLMCallConfig,
LLMCallContext,
)

ldclient.set_config(ldclient.Config("sdk-your-sdk-key"))
ld = LDAIClient(ldclient.get())
client = OptimizationClient(ld)

def handle_llm_call(
run_id: str,
config: LLMCallConfig,
context: LLMCallContext,
is_evaluation: bool,
) -> OptimizationResponse:
# config.model, config.instructions, config.key are available
# context.user_input, context.current_variables are available
response = your_llm_client.chat(
model=config.model.name if config.model else "gpt-4o",
system=config.instructions,
user=context.user_input or "",
)
return OptimizationResponse(completion=response.text)

result = await client.optimize_from_options(
OptimizationOptions(
agent_key="my-agent",
handle_agent_call=handle_llm_call,
judge_model="gpt-4o-mini",
judges={
"quality": OptimizationJudge(
threshold=1.0,
acceptance_statement="The response is accurate and concise.",
)
},
model_choices=["gpt-4o", "gpt-4o-mini"],
variable_choices=[{"user_id": "user-123"}],
user_input_choices=["What is my account balance?"],
)
)
```

### Ground truth optimization

```python
from ldai_optimizer import GroundTruthOptimizationOptions, GroundTruthSample

result = await client.optimize_from_options(
GroundTruthOptimizationOptions(
agent_key="my-agent",
handle_agent_call=handle_llm_call,
judge_model="gpt-4o-mini",
judges={
"accuracy": OptimizationJudge(
threshold=1.0,
acceptance_statement="The response matches the expected answer.",
)
},
model_choices=["gpt-4o", "gpt-4o-mini"],
ground_truth_responses=[
GroundTruthSample(
user_input="What is 2+2?",
ground_truth_response="4",
)
],
)
)
```

### Config-driven optimization (`optimize_from_config`)

Requires `LAUNCHDARKLY_API_KEY`.

```python
from ldai_optimizer import OptimizationFromConfigOptions

result = await client.optimize_from_config(
OptimizationFromConfigOptions(
config_key="my-optimization-config",
project_key="my-project",
handle_agent_call=handle_llm_call,
auto_commit=True,
)
)
```

## License

Expand Down
6 changes: 3 additions & 3 deletions packages/optimization/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "launchdarkly-server-sdk-ai-optimization"
name = "launchdarkly-ai-optimizer"
version = "0.1.0" # x-release-please-version
description = "LaunchDarkly AI SDK optimization helpers"
description = "LaunchDarkly AI tool — Optimization"
authors = [{name = "LaunchDarkly", email = "dev@launchdarkly.com"}]
license = {text = "Apache-2.0"}
readme = "README.md"
Expand Down Expand Up @@ -42,7 +42,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/ldai_optimization"]
packages = ["src/ldai_optimizer"]

[tool.mypy]
python_version = "3.10"
Expand Down
13 changes: 0 additions & 13 deletions packages/optimization/src/ldai_optimization/__init__.py

This file was deleted.

20 changes: 0 additions & 20 deletions packages/optimization/src/ldai_optimization/client.py

This file was deleted.

44 changes: 44 additions & 0 deletions packages/optimization/src/ldai_optimizer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""LaunchDarkly AI SDK — optimization.

This package will provide helpers to run selected tools against the LaunchDarkly API from SDK-based workflows.
"""

from ldai.tracker import TokenUsage

from ldai_optimizer.client import OptimizationClient
from ldai_optimizer.dataclasses import (
AIJudgeCallConfig,
GroundTruthOptimizationOptions,
GroundTruthSample,
LLMCallConfig,
LLMCallContext,
OptimizationContext,
OptimizationFromConfigOptions,
OptimizationJudge,
OptimizationJudgeContext,
OptimizationOptions,
OptimizationResponse,
ToolDefinition,
)
from ldai_optimizer.ld_api_client import LDApiError

__version__ = "0.0.0"

__all__ = [
'__version__',
'AIJudgeCallConfig',
'GroundTruthOptimizationOptions',
'GroundTruthSample',
'LDApiError',
'LLMCallConfig',
'LLMCallContext',
'OptimizationClient',
'OptimizationContext',
'OptimizationFromConfigOptions',
'OptimizationJudge',
'OptimizationJudgeContext',
'OptimizationOptions',
'OptimizationResponse',
'TokenUsage',
'ToolDefinition',
]
74 changes: 74 additions & 0 deletions packages/optimization/src/ldai_optimizer/_slug_words.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Word lists for slug generation.

Adjectives and nouns curated from the coolname package's word files
(Apache-2.0 licensed). Used by generate_slug() to produce
``adjective-noun`` variation keys (e.g. ``blazing-lobster``).

640 × 454 possible combinations from the full coolname corpus would be
overkill; ~100 × ~100 = ~10 000 combinations is sufficient given that
_commit_variation already appends a hex suffix on collisions.
"""

_ADJECTIVES: tuple = (
# appearance / texture
"blazing", "bouncy", "brawny", "chubby", "curvy", "elastic", "ethereal",
"fluffy", "foamy", "furry", "fuzzy", "glaring", "hairy", "hissing",
"icy", "luminous", "lumpy", "misty", "noisy", "quiet", "quirky",
"radiant", "roaring", "ruddy", "shaggy", "shiny", "silent", "silky",
"singing", "skinny", "smooth", "soft", "spicy", "spiked", "sticky",
"tall", "venomous", "warm", "winged", "wooden",
# personality / disposition
"adorable", "amazing", "amiable", "calm", "charming", "cute",
"dainty", "easygoing", "elegant", "famous", "friendly", "funny",
"graceful", "gracious", "happy", "hilarious", "jolly", "jovial",
"kind", "laughing", "lovely", "mellow", "neat", "nifty", "noble",
"popular", "pretty", "refreshing", "spiffy", "stylish", "sweet",
"tactful", "whimsical",
# character / trait
"adventurous", "ambitious", "audacious", "bold", "brave", "cheerful",
"curious", "daring", "determined", "eager", "enthusiastic", "faithful",
"fearless", "fierce", "generous", "gentle", "gleeful", "grateful",
"hopeful", "humble", "intrepid", "lively", "loyal", "merry",
"mysterious", "optimistic", "passionate", "polite", "proud", "rebel",
"relaxed", "reliable", "resolute", "romantic", "sincere", "spirited",
"stalwart", "thankful", "upbeat", "valiant", "vigorous", "vivacious",
"zealous", "zippy",
# quality / impressiveness
"ancient", "awesome", "brilliant", "classic", "dazzling", "fabulous",
"fantastic", "glorious", "legendary", "magnificent", "majestic",
"marvellous", "miraculous", "phenomenal", "remarkable", "splendid",
"wonderful",
# size
"colossal", "enormous", "gigantic", "huge", "massive", "tiny",
"towering",
)

_NOUNS: tuple = (
# common mammals
"badger", "bat", "bear", "beaver", "bison", "bobcat", "buffalo",
"capybara", "cheetah", "chipmunk", "coyote", "dingo", "dormouse",
"elephant", "ermine", "ferret", "fox", "gazelle", "gibbon", "gorilla",
"groundhog", "hamster", "hare", "hedgehog", "hippo", "horse",
"hyena", "jaguar", "kangaroo", "koala", "leopard", "lion", "lynx",
"mammoth", "marmot", "meerkat", "mongoose", "monkey", "moose",
"otter", "panda", "panther", "porcupine", "puma", "rabbit",
"raccoon", "rhinoceros", "seal", "skunk", "sloth", "squirrel",
"tiger", "walrus", "weasel", "whale", "wolf", "wombat",
"wolverine", "zebra",
# birds
"condor", "crane", "crow", "dove", "eagle", "falcon", "flamingo",
"hawk", "heron", "hummingbird", "kingfisher", "macaw", "magpie",
"ostrich", "owl", "parrot", "peacock", "pelican", "penguin",
"phoenix", "puffin", "raven", "robin", "sparrow", "starling",
"stork", "swan", "toucan", "vulture",
# reptiles / amphibians / fish
"cobra", "crocodile", "gecko", "iguana", "jellyfish", "lobster",
"narwhal", "octopus", "orca", "python", "rattlesnake", "salmon",
"seahorse", "shark", "snake", "squid", "tortoise", "turtle",
"viper",
# legendary / breed
"basilisk", "chimera", "chupacabra", "dragon", "griffin",
"kraken", "pegasus", "unicorn", "wyvern",
"beagle", "bulldog", "collie", "corgi", "dalmatian", "husky",
"labrador", "poodle", "rottweiler",
)
Loading
Loading