Configuration Reference¶
New to OptPilot?
Start with Getting Started if you have not run OptPilot yet. Use this page as a field reference once you have seen one successful run.
OptPilot public configs are YAML files validated by packaged JSON Schemas. In a
source checkout, those schemas live under src/optpilot/schemas/. They are used
by:
Schema surface is broader than the current runner
The Realm runner currently executes parameter and bounded file candidates
with Python batch methods/evaluators and package-owned trialWorkspace
seeds on the local process runtime, without setup/build, containers, host
values for Environments/backends, or ambient inheritance. A process Method
may explicitly declare runtime.envFromHost; only those named values are
supplied to its worker. Studio Runs retain only opaque local value
revisions; raw values are excluded from the durable process request and Run
evidence.
Command batch methods also execute on this slice: the command head must be
the logical interpreter name python/python3, mapped to the prepared
method runtime. Session methods, command evaluators, containers, and legacy
path-backed output declarations may validate as authored schema but are not
executable by this retained slice. Unsupported studies fail closed.
To validate a whole package folder, use:
For package source paths, setup files, and Python import targets, run the explicit deeper checks:
These checks are intentionally stricter than normal runtime path resolution:
package source paths must stay inside the package being validated. This is what
lets a package keep working after it is moved from an attached external project
into catalog/my_package/ or published as an immutable Realm package revision.
To check or execute setup declarations:
optpilot package setup-check path/to/package
optpilot package setup-check path/to/package --run-setup
To smoke-run a package study:
It covers the three public experiment config roles: environment, method,
and study. Catalog packages may also include optional resource manifests
for support material and launchable helper interfaces. Resources are secondary
catalog entries, not part of the core environment-method-study experiment
contract.
For the conceptual model behind those roles, use Concepts. For the runtime procedure after these files are loaded and validated, use How a Run Works.
If you are deciding how to connect a new method to a new environment, read Candidate Contracts before using this field reference. The reference tells you which fields exist; the contract guide explains how the fields fit together.
Every public config starts with:
config selects the schema. At launch, OptPilot captures the explicit package
root and retains an exact path-free study definition in the Realm. Users do not
author or locate that internal definition as a run-directory file.
Naming Rules¶
The public config uses concrete names for concrete jobs.
| Name | Use |
|---|---|
config |
Identifies the config file role: environment, method, study, or resource. |
format |
Identifies candidate representation: parameters, files, or opaque. |
valueType |
Identifies one parameter value shape inside candidate.parameters.schema. |
python, command, adapter |
Identify how evaluator or method code is invoked without a separate discriminator field. |
source |
Identifies where a value comes from for selector fields such as metrics.source and records[].source. |
Candidate compatibility is based on the candidate format plus required contract paths and capabilities. OptPilot does not require a separate candidate domain label.
Reference Types¶
| Type | Meaning |
|---|---|
| Free string | You choose the value. Used for ids, names, labels, descriptions, tags, or method-specific settings. |
| Enum | Must be one of the listed values. JSON Schema validates it. |
| Path | A filesystem path. Relative paths are resolved from the YAML file that contains the path unless noted otherwise. |
| Python import | module:function or module:Class, resolved through the config's pythonPath. Config-local imports such as evaluator:evaluate are preferred for catalog packages. |
| Command | A list of strings passed to a subprocess, for example [python, script.py, "{input_file}", "{output_file}"]. |
| Object | JSON/YAML object. Some objects are passed through to user code as settings. |
Validation Pipeline¶
optpilot validate is intended to check more than YAML syntax.
The validation pipeline is:
parse YAML
validate each config against JSON Schema
resolve referenced configs and relative paths
run semantic compatibility checks
compile to internal StudySpec
validate internal StudySpec invariants
That is why validation is the recommended first command whenever you create or edit a study.
Config Roles¶
This reference covers three authored experiment config roles:
config: environmentdescribes what can be evaluated and howconfig: methoddescribes how candidates are proposed and what contracts the method acceptsconfig: studybinds one environment to one method and chooses one run policy
Packages can also include config: resource manifests for optional support
material or launchable helper interfaces. Resources are not part of the core
environment-method-study experiment contract.
Directory Layout¶
The same organization is used for built-in examples, local user-owned code, and additional packages:
catalog/
my_package/
environments/
my_environment/
environment.yaml
evaluator.py
assets/
prompts/
methods/
my_method/
method.yaml
method.py
studies/
my_study.yaml
Environment and method configs are reusable. A single environment implementation can have multiple environment YAML files for different datasets, fidelity levels, metrics, or runtime settings. A single method implementation can have multiple method YAML files for different prompts, models, hyperparameters, or runtime settings.
Study configs are concrete run plans. Keep authored studies with their package or project. Studio Study Builder stores a new study in a Realm-managed workspace assembled from the exact selected package roots, so edits and launch can be fenced by an exact workspace revision.
Path Resolution¶
| Field | Relative to |
|---|---|
study.environmentConfig, study.methodConfig |
The study config file. |
environment.evaluator.pythonPath, environment.trialWorkspace[].from, environment.methodContext.instructions, environment.methodContext.references[].path |
The environment config file. |
method.entrypoint.pythonPath, method.runtime.container.build.context |
The method config file. |
environment.evaluator.cwd |
The trial workspace created for the candidate evaluation. |
environment.outputFiles[].path and string environment.outputFiles entries |
The trial workspace after evaluator execution. |
environment.records[].path for file-backed records |
The trial workspace after evaluator execution. |
Python import strings are resolved by normal Python import rules after any declared pythonPath entries are prepended.
These are authoring-path rules inside a package; they are not the browser API.
Realm-backed Studio rows use exact catalog entry refs, and Study Builder accepts
those refs rather than host config paths. The generated YAML remains portable
because it writes ordinary relative
environmentConfig and methodConfig strings.
Most setup paths resolve from the config file that owns the field. Runtime paths that describe what the evaluator should read or produce resolve inside the trial workspace, because that workspace is the directory OptPilot prepares and evaluates for each candidate.
Example:
catalog/my_package/studies/my_study.yamlresolvesenvironmentConfigrelative to the study filecatalog/my_package/environments/my_environment/environment.yamlresolves evaluatorpythonPath,trialWorkspace, andmethodContextpaths relative to the environment filecatalog/my_package/methods/my_method/method.yamlresolves anypythonPathentries relative to the method file
Environment Config¶
An environment config describes what can be evaluated and how the evaluation happens.
The block below is an annotated schema template, not a runnable retained-study example. It intentionally shows alternatives; the current runner accepts only the Python/parameters/process subset described above.
apiVersion: optpilot.io/v1
config: environment
# Free string. Stable id shown in the UI and run evidence.
id: my-environment
# Optional free text and tags.
description: My simulator or evaluator.
tags: [tutorial]
# Required. Exactly one of python, command, or adapter.
evaluator:
# Python import. Function signature:
# evaluate(candidate_runtime, context) -> dict
python: evaluator:evaluate
# Alternative command evaluator.
# command: [python, run_eval.py, "{candidate_json}", "{settings_file}", "{metrics_file}"]
# Alternative custom adapter class.
# Use only when a direct Python function or command is not enough.
# adapter: adapter:MyAdapter
# Optional evaluator controls.
# timeoutSeconds bounds each evaluation by wall clock. A slower evaluation
# ends as a typed "timeout" trial result with its logs; a worker that
# cannot be interrupted is stopped shortly after the limit. The effective
# limit is the smaller of this value and the study's execution
# timeoutSeconds; 600 when neither is declared.
timeoutSeconds: 600
pythonPath: [.]
# Runtime working directory inside the trial workspace, not relative to this YAML file.
cwd: .
# NOTE: `env:` is NOT supported by the retained runner. Declaring it fails
# the Run at attempt binding with `evaluator_environment_unsupported`,
# because evaluator paths and environment must come from typed runtime
# scopes. Put scenario values in `settings:` below, and use a method's
# `runtime.envFromHost` when a secret is genuinely needed.
# Free object passed to the evaluator in context["settings"].
# Use it for environment-owned scenario, dataset, query, case-list, or simulator arguments.
settings:
target_x: 4.0
# Optional runtime for the environment evaluator.
runtime:
sandbox: process # enum: process | container
# container:
# image: python:3.11-slim
# executable: docker
# network: disabled # enum: enabled | disabled
# Optional package files/directories mapped into each fresh trial workspace.
trialWorkspace:
- from: assets/input_data
to: input_data
# Required. Defines what methods must produce.
candidate:
format: parameters # enum: parameters | files | opaque
description: Parameters accepted by the evaluator.
parameters:
schema:
x:
valueType: float # enum: float | int | bool | string | categorical | array | object
min: 0.0
max: 1.0
# Optional method-visible context resolved from this environment config.
methodContext:
instructions:
- prompts/system_prompt.md
references:
- name: dataset_notes
path: assets/notes.md
type: markdown
description: Natural-language dataset notes for the method.
mimeType: text/markdown
- name: historical_results
path: assets/results.sqlite
type: sqlite
description: Read-only historical evaluation database.
mimeType: application/vnd.sqlite3
# Required. Declares where metrics come from.
metrics:
source: return # enum: return | file | stdout | sqlite | custom
keys: [score]
# Legacy authoring fields. The retained runner rejects these until the strict
# artifact/record contract lands.
records:
- name: events
source: jsonl # enum: jsonl | csv | sqlite_table | sqlite_query | custom
path: events.jsonl
outputFiles:
- metrics.json
- path: logs/*.txt
name: logs
required: false
# Optional capability ids exposed by this environment.
capabilities:
- id: historical_db_query
description: Read-only access to a historical SQLite database.
- id: exact_seed_replay
description: Replays a candidate on an exact evaluation seed.
callable: evaluator:replay_candidate
# Optional static policy contract for generated candidate code.
policyValidation:
entrypoint:
file: scheduler.py
callable: create_scheduler
maxArguments: 0
forbiddenImports: [os, sys, subprocess]
forbiddenNames: [create_controller]
lints:
- id: battery-field
forbiddenConstant: battery
message: use 'battery_level' for AGV records.
A capability may declare an environment-owned callable (module:object)
that implements it. The module resolves against the environment's Python
import roots; when a method's accepts.requires.capabilities names such a
capability, the retained runner adds those environment import roots to the
method's runtime, so the method can resolve the declared entry without a
pythonPath that reaches across the package. Retained compilation verifies
the declared callable module is present under the retained environment roots.
policyValidation declares the static contract that generated candidate code
must satisfy: a required entrypoint (one synchronous top-level function with a
bounded signature that nothing rebinds), forbidden import roots, forbidden
identifiers, and string-constant lints with authored messages. The block
travels in the candidate context (context.policyValidation), and any
code-editing method can apply it generically with
optpilot.policy_validation.validate_policy_sources(sources, policy) instead
of hardcoding per-environment AST checks. These lints give code-generating
methods early, high-quality feedback; they are not a security boundary —
candidate code always runs under the evaluator's own isolation.
For the retained local-process slice, each trialWorkspace.from
must resolve to a regular file or directory inside the explicit package root.
OptPilot seals those bytes with the package, compiles ordered portable input
layers, and gives each attempt a fresh writable trial volume initialized from
them. The evaluator may modify that trial volume without changing the retained
package. Shared directories and identical files may overlap; conflicting or
case-colliding destinations fail closed. The config does not select copying,
overlay, reflink, or another provider realization strategy.
For a current runnable environment, use a Python evaluator, a parameter or
bounded file candidate contract, process runtime, and no legacy
records/outputFiles.
Evaluator Return¶
Python evaluators normally return:
def evaluate(candidate_runtime, context):
settings = context["settings"]
return {
"status": "success",
"metric_values": {"score": 0.9},
"constraint_results": {},
"output_files": [],
"event_summary": {},
}
For parameter candidates, candidate_runtime is the candidate parameter
dictionary. For file candidates, it contains the fresh trial-workspace path,
the environment-owned candidate root, the validated path-free file declaration,
and optional entrypoint/options. It never contains a Realm content ref,
immutable-store path, or staging token.
evaluator.settings is intentionally a plain object. OptPilot does not define
domain-specific concepts such as scenarios, datasets, queries, or benchmark
cases. If an environment needs those inputs, put them in
evaluator.settings and let the evaluator or custom adapter interpret them.
For example:
evaluator:
python: evaluator:evaluate
settings:
dataset: data/train.csv
split: validation
simulation:
duration: 1000
num_aircraft: 4
For multi-case benchmarks, keep the same pattern:
evaluator:
adapter: adapter:BenchmarkAdapter
pythonPath: [.]
settings:
cases:
- id: small
path: assets/cases/small.yaml
- id: medium
path: assets/cases/medium.yaml
The adapter can loop over cases, call domain code, aggregate metrics, and
return one OptPilot evaluator result. If a method must read the same case files
before proposing a candidate, expose those environment-owned files through
methodContext.references. Keep method settings for method-owned knobs,
model choices, prompts, or assets. This keeps case handling out of OptPilot
core while preserving a clear environment/method boundary.
Typed Settings With settingsSchema¶
evaluator.settings stays a free object by default, but an environment may
declare an optional evaluator.settingsSchema that types it. Each entry uses
the same parameter definition as candidate.parameters.schema (valueType,
min/max, values, default, description, unit, pattern, and nested
items/properties):
evaluator:
python: evaluator:evaluate
settingsSchema:
scenario:
valueType: categorical
values: [baseline, faults, long_horizon]
default: baseline
description: Which bundled scenario to simulate.
replications:
valueType: int
min: 1
max: 50
default: 5
settings:
scenario: baseline
When settingsSchema is declared, validation enforces it: every declared
setting without a default must be present in settings, undeclared keys are
rejected, and each value must match its declared type, bounds, membership, and
pattern. A config without settingsSchema keeps the untyped behavior
unchanged. Declaring a schema is what lets Studio render a real input form for
the component instead of falling back to YAML inspection, so prefer it for any
setting a user is expected to change.
Command Placeholders¶
Command evaluators can use these placeholders:
| Placeholder | Meaning |
|---|---|
{python} |
Current Python executable. |
{workspace} |
Trial workspace path. |
{candidate_root} |
Root directory containing the materialized candidate. |
{candidate_file} |
Single candidate file path when unambiguous, otherwise candidate root. |
{candidate} |
Alias for {candidate_file}. |
{candidate_json} |
JSON file containing the candidate runtime payload. |
{settings_file} |
JSON file containing evaluator.settings. |
{metrics_file} |
Expected metrics file path. |
{trial_id} |
Trial id. |
{study_id} |
Study id. |
Candidate Formats¶
parameters candidates are JSON-like assignments validated against a schema:
Candidate field fragment:
candidate:
format: parameters
parameters:
schema:
rate:
valueType: float
min: 0.0
max: 8.0
default: 4.0
mode:
valueType: categorical
values: [balanced, aggressive, conservative]
constraints:
- id: aggressive-rate
description: Aggressive mode requires rate at least 2.
expr:
any:
- compare:
left: {param: mode}
op: "!="
right: {const: aggressive}
- compare:
left: {param: rate}
op: ">="
right: {const: 2.0}
Parameter Constraints¶
candidate.parameters.constraints is optional. Use it when individual parameter bounds are not enough and a valid candidate must satisfy relationships among fields.
Each constraint has an id, an optional description, and an expr. The expression is a small YAML/JSON tree. It is intentionally simple so OptPilot can validate it before a run and evaluate it during candidate materialization.
Boolean expression nodes:
| Node | Meaning |
|---|---|
compare |
Compare two scalar expressions. |
all |
Every child expression must be true. |
any |
At least one child expression must be true. |
not |
Negates one child expression. |
Comparison operators:
| Operator | Meaning |
|---|---|
<, <=, >, >= |
Numeric or ordered comparison. |
==, != |
Equality comparison. |
in, not_in |
Membership comparison. |
Scalar expression nodes:
| Node | Meaning |
|---|---|
{param: name} |
Read a candidate value from spec.name. |
{const: value} |
Use a literal value. |
{op: add, args: [...]} |
Add scalar expressions. |
{op: sub, args: [...]} |
Subtract scalar expressions from the first argument. |
{op: mul, args: [...]} |
Multiply scalar expressions. |
{op: div, args: [...]} |
Divide the first argument by each following argument. |
For example, this constraint requires batch_size * workers <= 256:
constraints:
- id: total-worker-batch-limit
description: Total worker batch must fit the memory budget.
expr:
compare:
left:
op: mul
args:
- {param: batch_size}
- {param: workers}
op: "<="
right: {const: 256}
If a candidate violates a constraint, OptPilot rejects it before calling the evaluator and records the failed constraint id in candidate evidence.
files candidates are generated file sets. trialWorkspace optionally seeds
the workspace, and the method stages generated files through
runtime_context.candidate_staging_dir. OptPilot freezes the complete proposal,
seals each tree, atomically admits it, and projects the selected immutable tree
under candidate.materialize.root as the final input layer of every fresh
attempt. The config describes semantics only; it does not select copying,
overlay, reflink, or another provider realization.
Environment field fragments:
trialWorkspace:
- from: assets/template_project
to: project
candidate:
format: files
description: Editable source file.
materialize:
root: project
files:
editable:
- path: solver.py
required:
- solver.py
allow:
- solver.py
deny: []
opaque candidates are for custom method/environment pairs that share their own payload semantics:
Candidate field fragment:
Method Config¶
A method config describes candidate proposal code and declares which environment contracts it accepts.
The block below is an annotated field template, not a runnable example file. A real method config should choose one entrypoint style and only include the fields it uses.
apiVersion: optpilot.io/v1
config: method
id: my-method
description: My optimizer.
entrypoint:
# Python import. Class constructed as MyMethod(definition, study_spec, rng).
python: method:MyMethod
protocol: batch # enum: batch | session
pythonPath: [.]
# Maximum duration of one propose/observe exchange. Defaults to 10 seconds.
# This is not a whole-Run or internal HTTP-client timeout.
exchangeTimeoutSeconds: 60
# Alternative command entrypoint. The retained runner executes command
# BATCH METHODS: one bounded subprocess per proposal exchange, with
# `command[0]` restricted to python/python3 (it is mapped to the worker's
# prepared interpreter). Command *evaluators* remain validate-only.
# See methods.md for the JSON stdin/stdout and {input_file}/{output_file}
# protocols.
# command: [python, method.py, "{input_file}", "{output_file}"]
# Free object passed to the method as method settings.
settings:
batchSize: 4
# Optional typed declaration for settings. Uses the same parameter
# definition as candidate.parameters.schema. When declared, settings are
# validated against it: declared settings without a default are required,
# undeclared keys are rejected, and values must match types and bounds.
# Typed settings also let Studio render an input form for this method.
settingsSchema:
batchSize:
valueType: int
min: 1
max: 64
default: 4
description: Candidates proposed per exchange.
# Required compatibility declaration.
accepts:
formats: [parameters] # list of parameters | files | opaque
requires:
context:
- candidate.parameters.schema
capabilities: []
# Optional method runtime. Useful for methods with their own dependencies.
runtime:
sandbox: process # enum: process | container
For a current runnable method, use a Python batch entrypoint accepting
parameters with process runtime. The environment owns the candidate contract;
OptPilot validates every submitted candidate against that environment contract
during the run.
Exact Python Dependencies For Retained Runs¶
An Environment or Method in the current process runtime can declare Python
dependencies without relying on packages installed in Studio's host Python.
Vendor pure-Python wheels in the same package, record each wheel's SHA-256 in a
lock file, and use the same narrow runtime.setup declaration on whichever
component needs those imports:
runtime:
sandbox: process
setup:
cache: prepared
timeoutSeconds: 300
steps:
- uses: python-venv
cwd: ../..
requirements: [requirements.lock]
Paths are resolved from cwd, which is itself relative to the component YAML.
Each non-comment lock-file line has exactly this form:
This first retained dependency slice intentionally accepts only vendored,
hash-locked py3-none-any (or py2.py3-none-any) wheels. It does not run shell
commands, contact a package index, install the project itself, inherit host
secrets, or accept native extensions. During Workspace Setup, Check validates
the declaration and paths; Test verifies the hashes, prepares the exact
read-only dependency layers, and executes the Study through the ordinary Run
path. A successful preparation is cached locally for speed, while every Run
retains the exact dependency trees it used.
A method that names an environment capability under
accepts.requires.capabilities runs that environment's own code inside the
method process, so it also receives the environment's prepared dependency
layer, read-only. The layer is placed after the method's own imports, which
means a method that locks its own dependencies keeps them: the environment's
layer only supplies imports the method does not provide itself. Declaring the
capability is what grants this — a method that reaches into an environment
directory through entrypoint.pythonPath alone sees that source but none of
its locked dependencies.
Batch Python methods can implement:
class MyMethod:
def __init__(self, definition, study_spec, rng=None):
...
def propose(self, n_candidates, study_state):
return [
{
"candidate_id": "candidate-001",
"format": "parameters",
"spec": {"x": 0.5},
"generator": {"method_id": self.definition["id"]},
}
]
def observe(self, observations):
...
protocol: session is reserved in the authoring schema, but the public Realm
runner rejects it. Live submit/wait/poll semantics are not implemented; OptPilot
does not execute a session config with degraded batch timing.
Command batch methods (entrypoint.command) execute on the retained slice as
one bounded subprocess per proposal exchange — see the Methods guide for the
exact request/response contract and its interpreter constraint. Command
evaluators remain an authoring target, not a current retained execution
capability.
Study Config¶
A study config binds one environment config to one method config.
The block below is an annotated field template for the current retained study shape.
apiVersion: optpilot.io/v1
config: study
name: my-study
description: Compare one method against one environment.
tags: [local]
# Paths resolved from this study file.
environmentConfig: ../environments/my_environment/environment.yaml
methodConfig: ../methods/my_method/method.yaml
objective:
metric: score
direction: maximize # enum: maximize | minimize
aggregation: mean # enum: mean | median | min | max | sum | last | weighted_mean
secondaryMetrics: []
budget:
maxTrials: 10
maxFailures: 5
execution:
parallelism: 2
timeoutSeconds: 600
evidence:
level: standard # enum: minimal | standard | full
reproducibility:
seed: 0
weighted_mean currently uses uniform weights because the study schema does
not yet expose an explicit weight vector. The evaluator and the Workbench
candidate-result projection apply that same rule; use mean when the more
specific label is unnecessary.
A study config does not describe domain inputs directly. If the selected
environment needs a scenario, dataset, query, simulator argument set, or
benchmark case list, put that in the environment config's evaluator.settings
or create another environment config variant. This keeps studies small: they
choose the environment, method, objective, budget, evidence policy, and seed.
For values that legitimately change per launch rather than per environment
variant, declare per-launch inputs (see below).
Containerized environment runtime. The image must already be built and is
named by fingerprint — a tag can resolve to different bytes later, so a record
naming one would not describe what ran. There is deliberately no way to
declare a build: building fetches software from the network, and what it
fetches can differ between builds. The image must be approved for study
execution (optpilot image approve) before anything runs in it.
Environment runtime fragment:
runtime:
sandbox: container
container:
image: ghcr.io/example/pkg@sha256:<64 hex digits>
platform: linux/amd64
network: disabled
# Optional: raise resource limits for this component's containers.
# OptPilot applies defaults (2 cpus, 4g memory, 512 processes); a raise
# is shown when the image is approved, because raising one is part of
# what is being agreed to. The wall-clock limit per evaluation stays in
# evaluator.timeoutSeconds above.
limits:
cpus: "4"
memory: 8g
pids: 1024
Methods declare the same runtime.container shape. Each candidate evaluates
in a fresh container from this image; a method gets one container for the
whole run.
Per-Launch Study Inputs¶
A study may declare an optional inputs map of typed per-launch values. Each
entry uses the same parameter definition as candidate.parameters.schema and
settingsSchema (valueType, min/max, values, default, description,
unit, pattern, and nested items/properties):
inputs:
problem:
valueType: string
description: Natural-language problem statement.
budget_hint:
valueType: int
min: 1
default: 60
Values are supplied at launch time. In Studio, a Run setup that declares
inputs shows a Launch inputs form on its detail page — one typed field
per declared input (dropdowns for categorical/bool values, number fields with
declared bounds, JSON for nested values) — and the entered values are bound
into the Run when you launch. On the CLI, use repeatable --input key=value
flags and/or one --inputs-file YAML mapping (--input wins on conflicting
keys). Flag values are parsed as YAML scalars, so 30 becomes an int, true
a bool, and a quoted value stays a string:
uv run optpilot run studies/my_study.yaml \
--package-root . \
--input problem="maximize throughput on line 3" \
--input budget_hint=30
Validation is fail-closed and happens before anything is retained: a declared
input without a default must be supplied, undeclared keys are rejected, each
value must match its declared type and bounds, and supplying any launch inputs
to a study that declares none is an error. A study that declares inputs can
be launched without flags only when every input has a default.
Each rejection carries a stable machine code so a UI can act on it rather than match error text. The codes are:
| Code | Meaning |
|---|---|
study_inputs_required |
A declared input has no default and no supplied value. Carries the missing names and their declarations, so a caller can collect the values and retry. |
study_inputs_invalid |
A supplied value failed its declared type, bounds, or membership, or an undeclared key was passed. |
study_inputs_undeclared |
Launch inputs were supplied to a study that declares none. |
study_inputs_reserved_key |
The environment's evaluator.settings or the method's settings already declares the reserved top-level inputs key. |
These are raised as ValueError subclasses carrying a code attribute, so
existing except ValueError handlers keep working unchanged. Studio surfaces
the code on every launch route — the CLI path, the Studio launch form, and the
Assistant — and uses study_inputs_required to ask for the missing values
instead of reporting a generic failure.
The resolved mapping is delivered to authored code under a reserved inputs
settings key: the evaluator reads it as context["settings"]["inputs"] and
the method as settings["inputs"]. Because the key is reserved, declaring
study inputs while the environment's evaluator.settings or the method's
settings already contain a top-level inputs key fails validation.
Per-launch inputs are problem payloads, not secrets. They are compiled into
the retained Run definition and appear in evidence like any other config, and
they participate in the definition's content digest, so the same study with
the same inputs reproduces the identical retained definition while different
inputs produce a different one. This is the opposite of the method runtime
envFromHost pattern, which exists to keep secret host values out of retained
evidence; never pass credentials through inputs.
Environment Variants And Inputs¶
Environment configs are the reusable place to bind evaluator-specific inputs. Use multiple environment YAML files when the same evaluator should be run with different datasets, fidelity levels, simulator arguments, case suites, or metric extraction settings.
For example, these are separate environment configs rather than different OptPilot study concepts:
catalog/my_package/environments/my_benchmark/
environment_small.yaml
environment_large.yaml
evaluator.py
assets/
Both files can point to the same evaluator but use different evaluator.settings.
The study then chooses which environment variant to run.
When an environment needs to evaluate several internal cases for one candidate,
implement that loop inside the evaluator or a custom adapter. The evaluator
still returns one OptPilot result with metric values, output files, records,
and event summary. If per-case details matter, write them as configured
records or outputFiles so they appear in evidence.
Studio Study Builder Boundary¶
Study Builder accepts an exact environment and method from one immutable Realm package revision or from non-conflicting package roots in different Realm revisions in the same content store. Saving supplies the complete exact package selections and component focus paths to one actor-bound Create Workspace command. One distinct root is adopted directly. Multiple roots use one whole-tree manifest union without copying file blobs: directories can merge, but any file overlap, file/directory conflict, or case-fold collision rejects.
The command binds and checks recovery before source reads; multi-root assembly uses a leased internal attempt, retained proof, atomic final workspace commit, and bounded startup cleanup. Cross-store transfer remains a future explicit import, never an implicit copy. Updates include the expected workspace revision. Launch commits that checkout and validates and plans from a read-only projection of the exact committed workspace revision. The checkout's absolute path is never part of the study's durable identity.
Launchable Interfaces¶
Reusable environments, methods, and resources can optionally declare a small
frontend or graphical helper with an interface block. Studio shows Launch
Interface for catalog entries that include this block.
When launched from the Catalog, Studio keeps component source read-only and
creates private launch-scoped runtime, dependency, and frontend storage. An
interface that declares outputs: true (or the action form described below)
also receives private control and output storage. Studio starts the command in
that transient runtime and opens the configured port in Preview. Use Edit in
Workspace when the source itself must be changed; launching an interface does
not create a durable workspace.
interface:
label: Demo UI
description: Optional short note shown in Studio.
outputs: true
command: [python, -m, http.server, "5173", --bind, 0.0.0.0]
cwd: .
env:
APP_MODE: demo
runtime:
sandbox: process
grants:
network: disabled
envFromHost: []
secretsFromHost: []
resources:
cpu: 1
memoryMiB: 2048
gpus: 0
timeoutSeconds: 3600
presentation:
kind: web
port: 5173
extraPorts: [8000]
readyPath: /
readyTimeoutSeconds: 60
accepts:
selectionKinds: [candidate, trial]
mediaTypes: []
Use command for the long-running frontend process and presentation.port for
the main browser port. The command should bind to 0.0.0.0 inside its runtime
so Studio can proxy it. cwd is a portable path relative to the component
source. Fixed nonsecret values belong in env; required user- or machine-selected
values such as model ids belong in grants.envFromHost; secret names belong in
grants.secretsFromHost; and network authority belongs in grants.network.
Studio resolves both host-variable lists only from Local environment variables and
never exposes their values in catalog summaries. Capacity and duration belong
in resources and timeoutSeconds. presentation.extraPorts exposes
additional local service ports, while its readiness fields define the bounded
HTTP probe.
Catalog launch currently accepts process-declared profiles and runs them through Studio's managed authoring-runtime provider. It does not yet independently enforce every declared per-profile network, resource, or long-running timeout field; unsupported runtime/profile combinations fail closed. Contextual Environment Preview uses the narrower retained-profile and trusted-container checks described in Studio UI.
Output reporting is part of this same optional interface contract for
Environments, Methods, and Resources. Set outputs: true on a producing launch
profile; omit it for a view-only interface. This flag declares the capability,
not a list of paths. For each opted-in launch, Studio creates a different private
output area and control file and injects their locations as
OPTPILOT_INTERFACE_OUTPUT_ROOT and OPTPILOT_INTERFACE_OUTPUTS_FILE.
These launch-owned runtime paths are chosen and set by Studio. A Catalog author
does not configure them, and they are not relative paths inside the Environment,
Method, or Resource source tree.
To report a result, the interface writes the complete file or folder below
OPTPILOT_INTERFACE_OUTPUT_ROOT, stops modifying it, and appends one
newline-terminated JSON object to OPTPILOT_INTERFACE_OUTPUTS_FILE:
{"schema_version":"optpilot.interface.output.v1","id":"simulator-001","label":"Generated simulator","kind":"tree","root":"output","path":"results/simulator-001"}
kind is file or tree, root is always output, and path is a canonical
relative path below the supplied root. This is a language-neutral environment
variable and JSONL boundary: interface code does not import or depend on an
OptPilot module. Studio validates and seals the relinquished bytes, then shows
a read-only output card. Repeating the same record is idempotent; changed bytes
must use a new id. A ready folder can then be saved as a managed editable
Workspace through the same one-selection Create Workspace command used by
Study Builder, without trusting a mutable app path. Output missing? is a
manual recovery path for a completed folder that the interface failed to
report; it uses the same capture lifecycle.
These two handles are supplied only to an opted-in interface launch. The Environment evaluator and Method entrypoint used by a Study keep their ordinary execution contracts and do not need to implement this protocol.
If a reported folder has a useful bounded command—for example, running a generated simulator—declare that command in the registered interface profile:
interface:
outputs:
actions:
- id: run-simulation
label: Run simulation
command:
- bash
- -lc
- 'exec "$OPTPILOT_PREPARED_RUNTIME_ROOT/python-venv/bin/python" -u run.py "$@"'
- output-action
cwd: .
timeoutSeconds: 120
acceptsArguments: true
runtime: originating-interface
showInOutputCard: false
# command, presentation, runtime, grants, and other profile fields follow
The command belongs to the exact registered Catalog profile. A launch-local
broker request—or an output-card browser request when the action is shown—may
select its id, but cannot supply another command, image, environment, mount,
or network policy. Studio snapshots the selected
folder and starts a fresh network-disabled sibling container from the same
immutable image and read-only prepared runtime as the originating interface.
It does not execute generated code inside the live interface container and
does not pass that interface's environment variables or secrets. cwd is
relative to the selected folder, timeoutSeconds is bounded to one hour, and
extra arguments are accepted only when acceptsArguments is true.
A request may omit timeout_seconds to use the authored maximum, or supply a
positive value no greater than that maximum to finish sooner; it can never
extend the registered action's lifetime.
By default, Studio also shows each registered action on every compatible
output card. Set showInOutputCard: false when the launched interface already
provides the student-facing controls and uses the action only through its
launch-local broker. The action remains available to that interface, while the
generic output card stays focused on viewing and saving the result.
An interface that needs the same action for its own verification UI also
receives OPTPILOT_INTERFACE_OUTPUT_ACTION_ROOT. Its language-neutral request,
response, cancellation, and result files let the interface select a declared
action for a tree staged below the broker's dedicated inputs/ directory
without importing OptPilot. These transient execution inputs are separate from
OPTPILOT_INTERFACE_OUTPUT_ROOT, so they never appear as generated output.
Studio still chooses the runtime and command. This is the same execution path used by the
output card, so automatic checks and user-triggered runs do not require two
different sandbox mechanisms.
When a component genuinely needs several independent commands or runtime
policies, use launchProfiles. Each entry is complete and named; profiles do
not inherit from the surrounding interface or from one another:
interface:
launchProfiles:
- id: inspect
label: Inspect Candidate
command: [python, -m, viewer]
presentation: {kind: web, port: 5173}
accepts: {selectionKinds: [candidate]}
- id: replay
label: Replay Trial
command: [python, -m, replay]
grants: {network: disabled, envFromHost: [], secretsFromHost: []}
presentation: {kind: web, port: 6173, readyPath: /health}
accepts: {selectionKinds: [trial]}
launchProfiles is mutually exclusive with all top-level profile fields. A
profile runtime may contain only sandbox, setup, and typed container
image/build/platform/engine settings. It cannot carry environment variables,
host-secret names, network authority, filesystem grants, resources, or launch
timeouts.
Resources can declare the same block in an optional
optpilot.resource.yaml file at the resource root:
apiVersion: optpilot.io/v1
config: resource
id: case-browser
name: Case Browser
purpose: viewer
tags: [frontend]
interface:
command: [python, -m, http.server, "5173", --bind, 0.0.0.0]
presentation: {kind: web, port: 5173}
accepts: {selectionKinds: [workspace]}
The optional purpose field is bounded to generator, viewer, template,
or reference. Studio shows the matching human-readable Catalog badge. If the
field is absent, Studio shows Resource; it does not infer a role from tags,
paths, files, or commands.
JSON Schema Files¶
The canonical schemas live in:
src/optpilot/schemas/environment.schema.json
src/optpilot/schemas/method.schema.json
src/optpilot/schemas/resource.schema.json
src/optpilot/schemas/study.schema.json
src/optpilot/schemas/defs/
The Python validator loads these packaged files, so schema validation is the same in the CLI, UI, and tests.