Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 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
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
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 = "ldai_optimizer"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't block on this but all other launchdarkly python packages have the launchdarkly-* name.

version = "0.1.0" # x-release-please-version
description = "LaunchDarkly AI SDK optimization helpers"
description = "LaunchDarkly AI tool — optimizer"
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