Skip to content

Add --evaluator-args flag forwarding kwargs to evaluate() via signature sniffing - #506

Merged
codelion merged 2 commits into
algorithmicsuperintelligence:mainfrom
akushonkamen:feat/474-evaluator-args
Oct 10, 2026
Merged

codelion merged 2 commits into
algorithmicsuperintelligence:mainfrom
akushonkamen:feat/474-evaluator-args

Conversation

@akushonkamen

@akushonkamen akushonkamen commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #474

Implements the approach @codelion outlined in the issue: a --evaluator-args CLI flag that accepts a JSON object and forwards the parsed kwargs to the user's evaluate() function, using inspect.signature so that existing single-argument evaluators keep working unchanged.

@ai-nikolai — this implements the approach discussed in #474. I left a comment on the issue before starting; if you had work in flight since July, happy to coordinate or step back in favor of your PR.

What it does

  • New config field: evaluator.evaluator_args: Optional[Dict[str, Any]] (openevolve/config.py), settable from YAML or the CLI. It round-trips through Config.to_dict() → EvaluatorConfig(**d["evaluator"]), so it propagates to process-pool workers with no extra serialization work.
  • CLI flag: --evaluator-args '{"weight": 2.0, "label": "exp-1"}' (openevolve/cli.py). Invalid JSON or a non-object value exits with code 1 and an actionable message. Only kwarg key names are logged, never values (they may contain secrets).
  • Signature sniffing (Evaluator._resolve_evaluator_kwargs, openevolve/evaluator.py):
    • evaluate() accepts **kwargs → all configured kwargs are forwarded.
    • evaluate() has named parameters covering the configured keys → forwarded on every call via functools.partial at the run_in_executor call site.
    • evaluate() cannot accept a configured key → startup error naming the offending keys and the actual signature (this also catches a kwarg colliding with the positional program path, e.g. evaluator_args={"program_path": ...}), instead of failing on every evaluation.
    • No evaluator_args configured (the common single-program_path case) → behavior is completely unchanged.
  • Docs: new README section "Passing Arguments to Your Evaluator" (CLI + YAML), noting this replaces the env-var workaround and that custom top-level config.yaml keys are silently dropped by the config parser (as @codelion pointed out in the issue).

Out of scope (as discussed in #474)

Cascade evaluate_stage1/2/3 functions are not touched — the issue covers evaluate() only. Positional-argument forwarding is also not included.

Testing (red → green)

New tests/test_evaluator_args.py (15 tests, unittest, fully offline):

  • kwargs forwarding to named-param and **kwargs evaluators
  • backward compat: single-param evaluator with no/empty args unchanged
  • startup validation: unsupported key, program_path collision, actual signature included in the error message
  • YAML load of evaluator.evaluator_args and worker config dict round-trip
  • parse_evaluator_args: valid / empty / invalid-JSON / non-object
  • end-to-end CLI subprocess: invalid --evaluator-args JSON → exit code 1

Red on main (tests added first, source unchanged):

ImportError: cannot import name 'parse_evaluator_args' from 'openevolve.cli'
FAILED (errors=1)

Green after the change:

$ OPENAI_API_KEY=test-key-for-unit-tests python -m unittest tests.test_evaluator_args -v
...
Ran 15 tests in 0.543s
OK

Full suite:

$ OPENAI_API_KEY=test-key-for-unit-tests python -m unittest discover tests
Ran 620 tests in 72.887s
OK

Format: black --line-length 100 / isort --profile black --line-length 100 applied to all changed files (isort also removed two pre-existing duplicate imports in evaluator.py).


Update (fix commit dac0e21, pushed after the numbers above were written): the signature.bind collision guard now runs before the **kwargs shortcut, so evaluator_args keys shadowing a positional parameter (e.g. {"program_path": ...} for evaluate(program_path, **kwargs)) are rejected at startup with an actionable message instead of raising TypeError on every evaluation. Two tests added: tests/test_evaluator_args.py now has 17 tests, and the full suite is Ran 622 tests ... OK.

…re sniffing

- New evaluator.evaluator_args config field (Optional[Dict[str, Any]]),
  settable from YAML or the new --evaluator-args '{"weight": 2.0}' CLI flag
  (invalid JSON or a non-object value exits 1 with an actionable message)
- Evaluator inspects evaluate() with inspect.signature: configured kwargs
  are forwarded via functools.partial when the function accepts them
  (named parameters or **kwargs); unsupported keys - or a kwarg colliding
  with the positional program path - fail at startup with an error naming
  the offending keys and the actual signature instead of failing on every
  evaluation; single-argument evaluators without evaluator_args keep their
  existing behavior unchanged
- Log kwarg key names only, never values (they may contain secrets)
- Document the flag in README, replacing the env-var workaround for
  per-run evaluator settings
- Add tests/test_evaluator_args.py: kwargs forwarding (named + **kwargs),
  backward compatibility, startup validation, YAML load, worker config
  dict round-trip, CLI JSON parsing and invalid-JSON exit code

Cascade evaluate_stage1/2/3 functions are intentionally out of scope;
issue algorithmicsuperintelligence#474 only covers evaluate().

Fixes algorithmicsuperintelligence#474
…**kwargs evaluators at startup

Move the signature.bind guard ahead of the VAR_KEYWORD shortcut so a
configured key colliding with a positional parameter (e.g.
evaluator_args={"program_path": ...} for evaluate(program_path, **kwargs))
fails at load time with an actionable message instead of raising
TypeError on every evaluation. The bind check mirrors the actual call
functools.partial(evaluate, **args)(program_path), so it can only reject
configurations that would fail at call time anyway.

Adds two tests: collision rejection for **kwargs evaluators and continued
forwarding of non-colliding keys.
@codelion
codelion merged commit 04874f0 into algorithmicsuperintelligence:main Oct 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Question/Usage] What is the best way of passing arguments to the evaluate.py function?

2 participants