Best-of-N (attempt-counted)
Best-of-N that rotates the parent every N attempts — failed/invalid tries spend the budget too.
"""Best-of-N-Attempts — the attempt-counted variant of Best-of-N.
Identical to :mod:`galapagos.scaffolds.best_of_n` except for the SelectionPolicy's counting rule:
here every attempt (iteration) spends one unit of the N budget, valid or not, so the parent rotates
on a fixed cadence of N iterations. The other five components are shared with ``best_of_n`` (a flat
keep-all population, the default multi-section prompt, a SEARCH/REPLACE diff proposer, no memory).
selection_policy.py -> BestOfNAttemptsPolicy (spend budget per attempt; see best_of_n for the
valid-counted original)
population/prompt_builder/proposer/memory reused from ``best_of_n``
scaffold.py -> BestOfNAttemptsScaffold (the orchestrator that composes the six)
"""
from __future__ import annotations
from ...config import GalapagosConfig
from ...models import GalapagosModel
from ..base_scaffold import GalapagosScaffold
from ..registry import register_scaffold
# the five non-policy components are identical to Best-of-N — reuse them rather than duplicate
from ..best_of_n.memory import BestOfNMemory
from ..best_of_n.population import BestOfNPopulation
from ..best_of_n.prompt_builder import BestOfNPromptBuilder
from ..best_of_n.proposer import BestOfNProposer
from .selection_policy import BestOfNAttemptsPolicy
@register_scaffold("best_of_n_attempts")
class BestOfNAttemptsScaffold(GalapagosScaffold):
name = "best_of_n_attempts"
@classmethod
def build_components(cls, config: GalapagosConfig, model: GalapagosModel | None) -> dict:
seed = int(config.seed)
sel = config.selection_policy
return {
"population": BestOfNPopulation(capacity=config.population.capacity),
"selection_policy": BestOfNAttemptsPolicy(
seed=seed,
n=int(sel.best_of_n),
num_inspirations=int(sel.num_inspirations),
),
"prompt_builder": BestOfNPromptBuilder(),
"proposer": BestOfNProposer(),
"memory": BestOfNMemory(),
}