Skip to main content
Hosted Optimizers is the fastest path to run GEPA prompt optimization and GELO exploration jobs on Synth infrastructure. Use synth-optimizers from your local machine, point it at a reachable task container, and follow the hosted run until it reaches a terminal state.

Quickstart

Install the package and configure a Synth API key:
uv add synth-optimizers
export SYNTH_API_KEY="sk_..."
Submit a hosted GEPA run from a TOML config:
synth-optimizers gepa submit \
  --config gepa.toml \
  --tunnel-url http://localhost:8765 \
  --tunnel-provider synth_tunnel \
  --follow
Submit a hosted GELO run from a preset:
synth-optimizers gelo submit \
  --preset crafter_smoke \
  --tunnel-url http://localhost:8943 \
  --tunnel-provider synth_tunnel \
  --follow
--follow keeps the local tunnel lease open while the hosted run is active and streams lifecycle events until the run finishes.

GEPA

GEPA is available both locally and hosted. For hosted execution, provide a GEPA TOML config and exactly one task target:
  • --tunnel-url for a local task service exposed through a tunnel
  • --container-pool for an existing hosted pool
  • a direct container URL inside the config
Watch an existing hosted GEPA run:
synth-optimizers gepa watch gepa_... --events
Python callers can submit the same TOML directly:
from pathlib import Path

from synth_optimizers.hosted import HostedOptimizerClient

client = HostedOptimizerClient()

with client.open_tunnel("http://127.0.0.1:8765", provider="synth_tunnel") as tunnel:
    run = client.submit_gepa_toml(
        Path("gepa.toml").read_text(),
        container_tunnel=tunnel,
    )
    result = client.wait_for_run(run.run_id, timeout_seconds=3600)

print(result.status.value)

GELO

GELO is hosted-only in the public package. You can submit from a built-in preset, a materialized JSON config, or a structured TOML/JSON config.
synth-optimizers gelo startup --json

synth-optimizers gelo materialize \
  --preset crafter_smoke \
  --container-url https://your-task-app.example.com \
  -o .out/crafter_goex.json

synth-optimizers gelo submit \
  --config .out/crafter_goex.json \
  --follow
Python callers can submit a typed preset:
from synth_optimizers.gelo import GeloPreset
from synth_optimizers.hosted import HostedOptimizerClient

client = HostedOptimizerClient()

with client.open_tunnel("http://127.0.0.1:8943", provider="synth_tunnel") as tunnel:
    config = GeloPreset.crafter_smoke().materialize(
        container_tunnel=tunnel,
    )
    run = client.submit_gelo(config)
    result = client.wait_for_run(run.run_id, timeout_seconds=3600)

board = client.get_state_slice(run.run_id, "board")

Task Targets

Each hosted optimizer run must target one task service authority:
TargetUse when
Local tunnelYou are developing against a local container or task server.
Direct URLYour task service is already publicly reachable.
Container poolThe task is already registered in Synth container pools.
Tunnels support synth_tunnel, cloudflared, and ngrok providers through one parent abstraction. synth_tunnel is the default and can refresh backend-owned lease auth during long hosted runs. cloudflared and ngrok submit as public URLs.

Usage Registration

Hosted submits send a best-effort usage registration event unless disabled. The event is content-free:
  • package name and version
  • algorithm (gepa or go-ex)
  • client surface (sdk or cli)
  • event name (run_submit)
  • source IP derived by the backend
It does not send prompts, configs, code, artifacts, repository paths, run IDs, user IDs, or organization IDs. Disable it when needed:
SYNTH_OPTIMIZERS_DISABLE_USAGE_REGISTRATION=1 \
  synth-optimizers gelo submit \
  --preset crafter_smoke \
  --container-url https://task.example.com
synth-optimizers gelo submit \
  --preset crafter_smoke \
  --container-url https://task.example.com \
  --disable-usage-registration
client = HostedOptimizerClient(register_usage=False)

GELO Plugin Lanes

GELO plugin lanes are typed so configs can describe training extensions without implying every lane is live. For this launch, SFT is the accepted beta lane. RLVR and OPSD are reserved future lanes and are rejected by the SDK/API until explicitly enabled.
from synth_optimizers.gelo import GeloPluginKind, GeloPreset

config = GeloPreset.crafter_smoke().materialize(
    container_url="https://your-task-app.example.com",
)
config["plugins"] = {"lanes": [{"kind": GeloPluginKind.SFT.value}]}
Requests that declare RLVR, OPSD, or an unknown plugin kind fail closed instead of silently downgrading to prompt-only behavior.