Skip to content

Quickstart

Evolve a solution to the bundled circle_packing task — pack 26 circles into the unit square, maximizing the sum of radii — with OpenEvolve.

You need

pip install open-galapagos, an LLM API key, and a running Docker daemon.

No Docker?

Score on the host instead — add --general.eval_mode local. For this task that needs only numpy (the [math] extra).

Cost

Every iteration is a live LLM call. Keep the first run small: max_iterations=20, or the tiny function_minimization task.

1. Set a key

Galapagos reads the OpenRouter credential from OPENROUTER_API_KEY (see Installation):

export OPENROUTER_API_KEY=sk-or-...

2. Run the loop

from galapagos import GalapagosModel, GalapagosConfig, GalapagosScaffold, GalapagosTask

model    = GalapagosModel.from_card(name="openai/gpt-4o-mini", host="openrouter")
config   = GalapagosConfig.from_config(scaffold_name="openevolve")
scaffold = GalapagosScaffold.from_card(name="openevolve", config=config, model=model)
task     = GalapagosTask.from_card(name="circle_packing")
result   = scaffold.run(task=task, max_iterations=20)

print(result.best_score, result.run_dir)

By default, circle_packing is scored inside its own container image — built once, then reused. See Task environments.

3. Read the result

run() normally drives the loop until general.max_iterations; a method with a finite frontier, such as ALE-Agent, can also stop when no expandable state remains. It returns a RunResult:

Attribute What it gives you
result.best the best Genome found; its .content is the solution.
result.best_score that Genome's fitness (combined_score), as a float.
result.history every scored Genome, in evaluation order.
result.run_dir where the run was written (or None).
result.summary run statistics — scaffold, task, iterations, evaluations, best_score, best_metrics, cost_usd, no_diff, rejected_too_long, language, population_size.

Other ways to load the same three objects

Only the model / scaffold / task trio changes between these forms. The run() call never does.

Each runnable scaffold loads its own card and default config, so you can skip GalapagosConfig:

from galapagos import GalapagosModel, OpenEvolveScaffold, GalapagosTask

model    = GalapagosModel.from_card(name="openai/gpt-4o-mini", host="openrouter")
scaffold = OpenEvolveScaffold.from_card(model=model)
result   = scaffold.run(task=GalapagosTask.from_card(name="circle_packing"))

name= resolves a bundled card. For a card you wrote yourself use load_scaffold(path=...), which instantiates the class named by the card's controller: field. (The typed entry points always load their own bundled card — OpenEvolveScaffold.from_card() ignores path=.)

from galapagos import GalapagosModel, GalapagosTask, load_scaffold

model    = GalapagosModel.from_card(name="openai/gpt-4o-mini", host="openrouter")
scaffold = load_scaffold(path="my_scaffolds/banditevolve/card.yaml", model=model)
task     = GalapagosTask.from_card(path="my_tasks/sphere/card.yaml")
result   = scaffold.run(task=task)

Skip cards entirely. Each slot takes a component instance, a "module.Class" dotted path, or a path to a .py file:

import galapagos as gx

scaffold = gx.GalapagosScaffold.from_card(
    model=gx.load_model("openai/gpt-4o-mini", host="openrouter"),
    population="galapagos.components.population.IslandPopulation",
    selection_policy="galapagos.components.selection.ExploreExploitPolicy",
    prompt_builder="galapagos.components.prompt.DefaultPromptBuilder",
    proposer="galapagos.components.proposer.LLMProposer",
    memory="my_components/scratchpad.py",          # a .py file you wrote
)
result = scaffold.run(task=gx.GalapagosTask.from_card(name="circle_packing"))

The evaluator slot comes from the task, never the scaffold — that lets a scaffold reuse runnable compatible task evaluators. Omit any other slot and it falls back to the base default (InMemoryPopulation, ExploreExploitPolicy, DefaultPromptBuilder, LLMProposer, NullMemory — only the Memory default is a no-op). See Build your own scaffold.

Every *.from_card constructor has a short alias:

import galapagos as gx

model    = gx.load_model("openai/gpt-5.5", host="openrouter")
config   = gx.load_config("openevolve")
scaffold = gx.load_scaffold("openevolve", config=config, model=model)
task     = gx.load_task("circle_packing")
result   = scaffold.run(task=task)

Tune the configuration

GalapagosConfig uses dotted paths via .get / .set (which returns the config, so it chains):

config = (gx.GalapagosConfig.from_config(scaffold_name="openevolve")
          .set("general.max_iterations", 100)
          .set("general.checkpoint_interval", 5))

print(config.get("general.max_iterations"))      # 100

Every knob is a real config path — see Configs for all of them.

From the command line

The galapagos console script mirrors the Python API. An override takes either form — canonical --set key=value, or the dotted key as a flag:

galapagos run --scaffold openevolve --task circle_packing \
    --proposer.model_name openai/gpt-4o-mini \
    --proposer.api_base openrouter \
    --general.max_iterations 20

galapagos scaffold list     # the runnable method catalog
galapagos task list         # the task catalog

There are no bespoke --model / --iterations / --seed flags: everything is a config path. See CLI commands.


Next: the Concepts behind the loop, or a Guide.