Skip to main content

Apache-2.0 prompt optimization package with GEPA and MIPRO adapters.

Project description

synth-optimizers

Apache-2.0 licensed Python-only prompt optimization package that provides a local offline mirror of the public Synth GEPA/MIPRO SDK surfaces under prompt_opt.sdk.optimization.*.

Repo name: optimizers

PyPI distribution name: synth-optimizers

Python import package: prompt_opt

What is included

  • prompt_opt.sdk.optimization.policy.v1.PolicyOptimizationOfflineJob
    • Local drop-in offline job surface for gepa_offline and mipro_offline.
  • prompt_opt.sdk.optimization.internal.prompt_learning.PromptLearningJob
    • High-level local prompt-learning wrapper with candidate/state accessors.
  • prompt_opt.sdk.optimization.internal.configs.prompt_learning.PromptLearningConfig
    • Canonical prompt-learning config models with local proxied -> retrieved normalization.
  • prompt_opt.dspy.MIPROv2
    • DSPy-compatible local MIPRO wrapper routed through the mirrored offline SDK.
  • prompt_opt.dspy.gepa
    • GEPA slot-in wrapper routed through the same local offline runtime.
  • prompt_opt.adapters.synth_container
    • Container request/response helpers for local rollout integration.
  • src/gepa/__init__.py
    • Import compatibility shim so import gepa works against this package.

Install (editable, local)

cd optimizers
pip install -e .

Quick usage

from prompt_opt.sdk.optimization.policy.v1 import PolicyOptimizationOfflineJob

def task_model(prompt: str) -> str:
    if "Return exactly one of" in prompt:
        return "paris"
    return "rome"

job = PolicyOptimizationOfflineJob.create(
    kind="mipro_offline",
    system_name="cities-local",
    backend_url="local://prompt-opt",
    api_key="local",
    config={
        "prompt_learning": {
            "algorithm": "mipro",
            "execution_mode": "retrieved",
            "task_data": {
                "train_examples": [{"input": "Capital of France?", "answer": "paris"}],
                "validation_examples": [{"input": "Capital of France?", "answer": "paris"}],
            },
            "mipro": {
                "initial_candidate": {
                    "stages": [
                        {
                            "id": "main",
                            "name": "main",
                            "messages": [
                                {"role": "system", "pattern": "Answer the question.", "order": 0},
                                {"role": "user", "pattern": "{input}", "order": 1},
                            ],
                        }
                    ]
                },
                "num_candidates": 4,
                "max_iterations": 3,
            },
            "local_runtime": {"task_model": task_model},
        }
    },
)

result = job.stream_until_complete(timeout=30.0, interval=0.05)
best_candidate = job.get_state_envelope()["state"]["candidates"][result["best_candidate_id"]]
print(best_candidate["candidate_content"])

GEPA shim

from gepa import optimize
from prompt_opt.adapters.synth_offline import LocalEvaluator, SynthOfflineLearningAdapter

def score_fn(example, candidate):
    expected = str(example.get("answer", "")).strip().lower()
    prompt = " ".join(candidate.values()).lower()
    return 1.0 if expected and expected in prompt else 0.0

adapter = SynthOfflineLearningAdapter(LocalEvaluator(score_fn=score_fn))
result = optimize(
    seed_candidate={"system_prompt": "Answer briefly."},
    trainset=[{"input": "Capital of France?", "answer": "paris"}],
    adapter=adapter,
    max_metric_calls=8,
)

print(result.best_candidate)
print(result.val_aggregate_scores[result.best_idx])

Notes

  • This package is local-only and does not call Synth backend APIs.
  • Runtime execution is retrieval-based only. Hosted configs that specify execution_mode="proxied" are accepted and normalized to retrieved locally.
  • The local runtime mirrors candidate/state/result payloads and offline job methods; backend_url and api_key are kept for signature parity but are inert in local mode.
  • Multi-stage candidates are first-class in both local GEPA and local MIPRO.

Examples

  • Local DSPy MIPRO:
    • examples/mipro_local_example.py
  • DSPy GEPA slot-in:
    • examples/dspy_gepa_slot_example.py
  • Local offline Banking77 via Synth InProcessContainer:
    • examples/banking77_container_example.py
  • Local offline GEPA for simple JSONL task files:
    • examples/gepa_taskfile_container_eval.py

Run the Banking77 local regression harness with:

PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/banking77_container_example.py \
  --algorithms gepa mipro \
  --train-per-label 4 \
  --held-out-per-label 2 \
  --num-generations 2 \
  --children-per-generation 8 \
  --num-candidates 8 \
  --max-iterations 6

The script prints per-algorithm JSON with train_* and held_out_* metrics and raises if held-out improvement does not exceed --min-held-out-delta.

Find simple eval task files and run local taskfile optimization (gepa or mipro) with:

PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --list-simple-task-files
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm gepa \
  --task-file /Users/joshpurtell/Documents/GitHub/evals/mipro/tasks/multimodal_smoke.jsonl \
  --train-size 8 \
  --num-generations 2 \
  --children-per-generation 8 \
  --total-rollouts 64
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm gepa \
  --task-preset medec_smoke \
  --train-size 8 \
  --num-generations 2 \
  --children-per-generation 8 \
  --total-rollouts 64
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm mipro \
  --task-preset drugprot_train_public \
  --train-size 8 \
  --num-candidates 24 \
  --max-iterations 16 \
  --children-per-generation 16 \
  --total-rollouts 256
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm mipro \
  --task-preset langprobe_banking77 \
  --train-size 32 \
  --num-candidates 12 \
  --max-iterations 8 \
  --children-per-generation 8 \
  --total-rollouts 96
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm mipro \
  --task-preset langprobe_hotpotqa \
  --train-size 8 \
  --num-candidates 8 \
  --max-iterations 6 \
  --children-per-generation 8 \
  --total-rollouts 64
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm mipro \
  --task-preset langprobe_iris \
  --train-size 60 \
  --num-candidates 10 \
  --max-iterations 8 \
  --children-per-generation 8 \
  --total-rollouts 96
PYTHONPATH=/Users/joshpurtell/Documents/GitHub/prompt-opt/src:/Users/joshpurtell/Documents/GitHub/synth-ai \
uv run --active /Users/joshpurtell/Documents/GitHub/prompt-opt/examples/gepa_taskfile_container_eval.py \
  --algorithm mipro \
  --task-preset langprobe_hover \
  --train-size 60 \
  --num-candidates 10 \
  --max-iterations 8 \
  --children-per-generation 8 \
  --total-rollouts 96

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

synth_optimizers-0.1.0.tar.gz (39.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

synth_optimizers-0.1.0-py3-none-any.whl (42.0 kB view details)

Uploaded Python 3

File details

Details for the file synth_optimizers-0.1.0.tar.gz.

File metadata

  • Download URL: synth_optimizers-0.1.0.tar.gz
  • Upload date:
  • Size: 39.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for synth_optimizers-0.1.0.tar.gz
Algorithm Hash digest
SHA256 90db81b7b60e0126952aad65ad65a965452977d589e37bfd841e4eb5c5c52d41
MD5 42128c37e3bba9325da03e6c1c9f41fc
BLAKE2b-256 9a635713da08c73708d3947de4987c9958ced31440c3e1b14b81d56d8e627e4a

See more details on using hashes here.

File details

Details for the file synth_optimizers-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for synth_optimizers-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 93ab470821f29b4ad23f280b7fd976b25848dde3c35d374ceae21d0cfc779dac
MD5 9c50029aa9a68ec107304ad622af3b97
BLAKE2b-256 06c7fcc2254c32e3aec1a7700981b46b74b4e99b6e4010573a218ea21ba7265a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page