TOML configuration reference

ape/run.py runs a full evaluation from a TOML file:

python -m ape config.toml              # resolve relative paths against the config's directory
python -m ape config.toml -r /project  # resolve against /project instead
python -m ape config.toml -o /out      # redirect output files into /out

(python -m ape.run also works, but emits a harmless RuntimeWarning.)

Everything is validated up front — unknown keys, missing required keys, bad imports, out-of-range solver knobs — so a misconfigured run fails with a readable ConfigError before the CLI dashboard starts.

You can print the fully-populated example below at any time with python -m ape config-schema, or copy it to a file with python -m ape config-schema -o config.toml.

Editing the config interactively

Rather than hand-editing the TOML, you can open a small local web UI with per-key documentation, live validation, and a save button:

python -m ape config-edit                 # edits ./config.toml (created on first save if missing)
python -m ape config-edit my/config.toml  # edit a specific file
python -m ape config-edit -p 8765         # serve on a different port (default 8000)
python -m ape config-edit --no-browser    # don't open a browser automatically

By default the editor opens http://localhost:8000 in your browser. Relative paths are resolved and validated against the config file's directory; override this with -r/--root.

The editor's save feature needs the optional config-editor extra (it writes TOML, which the standard library cannot do):

pip install ape-framework[config-editor]

Without it, browsing and validating still work, but saving reports an error asking you to install the extra.

Full config_reference.toml
# ============================================================================
# Maximal APE configuration — every section and key the loader understands.
# (Draft reference; to be revisited.)
#
# Run with:  python -m ape config.toml            (resolves paths against the
#                                                  directory containing this file)
#            python -m ape config.toml -r /proj   (resolve against /proj instead)
#            python -m ape config.toml -o /out    (redirect output files to /out)
# ============================================================================

# --- Input data --------------------------------------------------------------
[data]
path = "data.jsonl"                          # required; .csv or .jsonl, resolved vs project root
schema = "myproj.components.MyProblem"       # required; dotted path to a DataModel subclass
start_id = "problem-0042"                    # optional; resume the input slice from this id
end_id   = "problem-0100"                    # optional; stop after this id (inclusive)

# --- Instruction prompt ------------------------------------------------------
[prompt]
template = "prompts/instruction.md"          # required; mustache template, {{ field }} from the data item

# --- Main LLM ----------------------------------------------------------------
[model]
provider = "langchain_google_genai.ChatGoogleGenerativeAI"  # required; dotted path to a BaseChatModel class
name = "gemini-2.5-pro"                                      # required; passed as model=<name> to the class
# Optional cheaper helper LLM, used to summarize long tool errors.
# Both keys must be set together, or both omitted (=> no helper, fall back to main LLM).
helper_provider = "langchain_google_genai.ChatGoogleGenerativeAI"
helper_name = "gemini-2.5-flash"

# --- Solution adapter --------------------------------------------------------
[adapter]
callable = "myproj.components.parse_solution"  # required; the parsing FUNCTION itself, Callable[[str], SolutionT]

# --- Solver knobs ------------------------------------------------------------
[solver]
reformat_prompt = "That answer didn't parse. Reply with ONLY the final answer."  # required, no default
tools = ["calculator", "lmfdb"]              # optional; names of [tools.X] tables to bind to the LLM

# All optional; names match the SolverConfig fields:
user_system_prompt = "You are an expert algebraist. Be terse."
context_window_size = 256000                 # int; None/omit => best-effort detection from the model
context_compaction_threshold = 0.8           # (0, 1]; occupancy ratio that triggers compaction
max_steps = 8                                # >= 1; inferences allowed before force-finish
error_summarization_threshold = 500          # >= 0; tool-output errors at least this long get summarized
solution_adaption_attempts = 3               # >= 1; reformat retries before declaring PARSING_FAILED
rate_limit_backoff_seconds = [2, 4, 8, 16, 32]  # all > 0; seconds to wait between provider rate-limit retries

# --- Checker -----------------------------------------------------------------
[checker]
callable = "myproj.components.MyChecker"     # required; a Checker class or a zero-arg factory
expose_as_tool = true                        # optional; also expose the checker to the LLM as a tool
checker_tool = "myproj.components.make_checker_tool"  # required iff expose_as_tool; tool factory
# Any extra keys here are forwarded as **kwargs to `callable` (and to `checker_tool`).

# --- Tool definitions --------------------------------------------------------
# Each [tools.X] is instantiated as callable(**remaining_keys). If the result is a
# ToolProvider, its get_tool() output is used (a single BaseTool, or a list — both
# are handled). Only tables listed in [solver] tools are actually loaded.
[tools.lmfdb]
callable = "ape.tools.LMFDB"           # required; this is a ToolProvider -> yields a list of tools
pool_workers = 2                             # extra keys => LMFDB(pool_workers=2, query_timeout=10)
query_timeout = 10

# --- Run parameters (fields of RunParams) ------------------------------------
[run]
save_file = "output/results.jsonl"           # JSONL of OutputBatches; also used for resume
metadata_file = "output/metadata.json"       # run metadata (incl. the pickled SolverConfig)
solver_workers = 4
checker_workers = 2
runs_per_data = 3                            # solver sessions attempted per data item
load_save = true                             # on start, skip ids already completed in save_file

# --- CLI dashboard -----------------------------------------------------------
[cli]
enabled = true                               # default true; set false to run headless (no dashboard)
log_file = "output/run.log"
session_log_file = "output/sessions.log"
log_level = "INFO"                           # name or int

# --- Logging suppression -----------------------------------------------------
[logging]
suppress = ["httpx", "langchain_core", "urllib3"]   # raise these loggers to WARNING

# --- User hooks --------------------------------------------------------------
# Optional callbacks invoked at well-defined points in the run. Each value is a
# dotted path to a Callable. Exceptions raised by hooks are logged and
# swallowed so a faulty hook cannot lose results or stop the run.
[hooks]
on_batch_saved = "myproj.components.on_batch_saved"  # Callable[[OutputBatch], None];
                                                     # fires right after the built-in save handler
                                                     # persists each batch - use to mirror results
                                                     # to an external store, push a metric, etc.


Sections

[data]

key required meaning
path yes input file (.csv or .jsonl), resolved against the project root
schema yes dotted path to a DataModel subclass
start_id no resume the input slice from this id
end_id no stop after this id (inclusive)

[prompt]

key required meaning
template yes path to the instruction-prompt template (mustache: {{ field }} from the data item)

[model]

key required meaning
provider yes dotted path to a BaseChatModel class
name yes model name; passed as model=<name> to the class
helper_provider iff helper_name dotted path to a BaseChatModel class for the helper LLM
helper_name iff helper_provider model name for the helper LLM; both helper_provider and helper_name must be set together or both omitted

The optional helper LLM is used for secondary tasks such as context compaction and tool-error summarization. When omitted, the main LLM is used as a fallback.

[adapter]

key required meaning
callable yes dotted path to the solution-adapter function itself (Callable[[str], SolutionT])

[solver]

key required meaning
reformat_prompt yes prompt shown to the LLM when the adapter fails to parse its answer
tools no list of [tools.X] names to bind to the LLM
user_system_prompt no text appended to the framework's base system prompt, or a path to a file containing it (resolved against the project root)
context_window_size no int; omit ⇒ best-effort detection from the model
context_compaction_threshold no float in (0, 1]; occupancy ratio that triggers compaction
max_steps no ≥ 1; inferences allowed before force-finish
error_summarization_threshold no ≥ 0; tool-output errors at least this long get summarized
solution_adaption_attempts no ≥ 1; total parse attempts (1 on the original answer + up to N−1 after reformat prompts) before declaring PARSING_FAILED
rate_limit_backoff_seconds no list of positive ints; waits between provider rate-limit retries

The optional knobs are the SolverConfig field names verbatim.

[checker]

key required meaning
callable yes a Checker class or a zero-arg factory
expose_as_tool no also expose the checker to the LLM as a tool
checker_tool iff expose_as_tool dotted path to a tool factory

Any other keys are forwarded as **kwargs to callable (and to checker_tool) and recorded in the run metadata. checker_tool is instantiated and resolved exactly like a [tools.X] factory (may return a BaseTool, a list, or a ToolProvider).

[tools.X]

key required meaning
callable yes dotted path; instantiated as callable(**remaining_keys)

If the result is a ToolProvider, its get_tool() output is used (a single BaseTool or a list — both are handled). Only tables named in [solver] tools are actually loaded. Any other keys become constructor kwargs.

[run]

Accepts any field of RunParams:

key default meaning
save_file output/results.jsonl JSONL of OutputBatches; also used for resume
metadata_file output/metadata.json run metadata (incl. the pickled SolverConfig)
solver_workers 1 number of solver workers
checker_workers 1 number of checker workers
runs_per_data 1 solver sessions attempted per data item
load_save true on start, skip ids recorded as completed in metadata_file

[cli]

key default meaning
enabled true set false to run headless (no dashboard)
log_file output/run.log dashboard log file
session_log_file output/sessions.log per-session transcript file
log_level INFO logger level name or int

[logging]

key required meaning
suppress no list of logger names to raise to WARNING

[hooks]

Optional callbacks invoked at well-defined points in the run. Each value is a dotted path to a callable. Exceptions raised by hooks are logged and swallowed so a faulty hook cannot lose results or stop the run.

key required meaning
on_batch_saved no Callable[[OutputBatch], None]; fires right after each batch is persisted by the built-in save handler — use to mirror results to an external store, push a metric, etc.