tauopt.engine.ask#

engine.ask – the one suggest entry point (local driver and service both call this).

Semantics: explicit config or policy routing, then never-fail execution down the robustness ladder. In the default on_timeout="ladder" mode, ask() NEVER raises for math reasons: the ladder ends at the stdlib-only random rung and every fallback is visible in provenance (fallback_from).

The wait budget is USER-OWNED: max_wait_s deadlines the whole ask (each GP rung’s timebox derives from the remaining budget), and on_timeout="fail" re-raises TimeboxExceeded instead of degrading – callers who prefer their own backoff get an honest error. Non-timeout math failures ladder in BOTH modes: a budget preference should never turn a numerics crash into an outage. A bad params value refuses at the ask boundary (AskValidationError) before any rung for the built-in strategies (a custom strategy without read_params keeps the ladder); nothing is silently repaired.

The timebox_mode x max_wait_s matrix (exhaustive):

budget x any   -- rung timeboxes capped by the remaining ask budget;
                 between-rung expiry raises in fail mode
off    x any   -- injection-wins: the request bound rides as a
                 budget-mode execution pair; no-scrub both branches
caps   x unset -- pure capped compute, no answer-shaping clock anywhere
                 (measurement-only telemetry exempt)
caps   x set   -- cooperative rung-boundary refusal + ONE post-completion
                 delivery decision, both ``AskDeadlineExceeded`` -- NEVER
                 injection, NEVER a timeout callback, NEVER a budget-pair
                 rewrite. A started rung runs to completion; an expired
                 delivery decision DISCARDS the computed drafts (the
                 withheld answer byte-equals what a longer deadline
                 returns -- availability may be wall-dependent, the answer
                 never is). ``AskDeadlineExceeded`` is structurally
                 invisible to the P6 ladder (never caught, never
                 substituted -- reusing TimeboxExceeded would BE ladder
                 substitution) and fires in BOTH on_timeout modes; state
                 never advances on refusal.

Suggestions are validated in-bounds before they leave the engine.

Deduplication (config.dedup): the suggest-time boundary. Exact re-suggestion is refused against observed union pending union the current batch (canonical active-only equality); REPAIR belongs to the owning strategy (the boundary DETECTS, verifies, and descends the ladder with the exclusion set propagated); on admitted finite spaces (raw universe <= 1,024) the enumeration path fills remaining slots in the declared order and, when it completes with slots still unfilled, PROVES exhaustion (space_exhausted). Typed intent requests may then fill remaining slots as authorized duplicates – authorization recomputed from canonical (state, config), never trusted (engine._intents). Flag-off asks take the plain path (pinned byte-for-byte).

Queue selection (config.queue_selection, requires dedup): temperature- annealed selection over each slot’s already-budgeted candidate queue. gp_ei and ucb_cl expose per-slot ranked queues (their retained restart pools – generation streams identical to the dedup path, selection deviates AFTER); the boundary’s SlotSelector (engine._select) applies the dedup eligibility filter, distance scoring, and the hashed temperature protocol, and records one provenance entry per slot. Rungs without queues fill pass-through, recorded. Flag-off asks are byte-identical to the dedup path (pinned).

Module Contents#

Classes#

AskResult

The ask surface (additive schema): drafts are the returned candidates (unique fills first, then authorized grants – the slot-fill order); the counters partition requested_q. Precedence: space_exhausted is set whenever the enumeration PROOF holds – including alongside a full batch of authorized replicas; unique_candidate_unavailable only when slots remain UNFILLED without proof, and it is NON-RETRYABLE for identical (state, config) – P4 guarantees the same answer until the state hash changes. The partition invariants are MODEL-ENFORCED: a counter that cannot partition requested_q dies at construction, never ships.

Functions#

ask

Produce up to n suggestion drafts + the provenance that re-derives them.

API#

exception tauopt.engine.ask.AskValidationError#

Bases: ValueError

The caller’s argument error at the ask boundary – the ValueError class both drivers answer as 400 validation_error: n < 1, a max_wait_s outside (0, 300], strategy: "auto" with params, intents without dedup, a params key the strategy does not read, or a params value the strategy’s reader refuses (the wrong type, out of range, an unknown enum value, a two-writer or posture conflict, or a composition of valid values the strategy cannot run on this space). Any other ValueError out of the engine is the engine’s own fault and surfaces as a 500 internal_error instead of being blamed on the caller (a pydantic ValidationError deep in a strategy, a partition violation in a record).

Initialization

Initialize self. See help(type(self)) for accurate signature.

exception tauopt.engine.ask.AskFailed(message: str, *, error_type: str, details: dict | None = None)#

Bases: RuntimeError

The ask could not answer and the failure is the engine’s, not the caller’s: raised OUTSIDE the never-fail net, carrying a machine-readable error_type and a details payload both drivers forward – the local driver as a 500 ApiError of the same error_type and details, the hosted service as the same JSON error shape with the exception TEXT redacted (a constant message per type; details without exception_text, which stays in the service log under the response’s X-Request-Id), which the transport passes through unchanged. Two kinds:

  • all_rungs_failed: every rung of the fallback chain failed, the stdlib-only random rung included (its one honest refusal is the rejection cap on a thin linear-constrained region). details carries intended and attempts (one record per failed rung: rung, stage, exception_type, exception_text, the same evidence fallback_attempts carries on a served suggestion); __cause__ is the last rung’s exception; the message is the attempts summary.

  • integrity_violation: an integrity postcondition failed after a rung answered – a center-flagged sobol answer without a center record, or queue-selection drafts that fail canonical key computation or disagree with the selector’s commit ledger. details carries check (center_record, selection_key, selection_ledger). Fail-closed by design: a receipt that would lie is never shipped.

Initialization

Initialize self. See help(type(self)) for accurate signature.

class tauopt.engine.ask.AskResult(/, **data: Any)#

Bases: pydantic.BaseModel

The ask surface (additive schema): drafts are the returned candidates (unique fills first, then authorized grants – the slot-fill order); the counters partition requested_q. Precedence: space_exhausted is set whenever the enumeration PROOF holds – including alongside a full batch of authorized replicas; unique_candidate_unavailable only when slots remain UNFILLED without proof, and it is NON-RETRYABLE for identical (state, config) – P4 guarantees the same answer until the state hash changes. The partition invariants are MODEL-ENFORCED: a counter that cannot partition requested_q dies at construction, never ships.

Initialization

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

model_config#

‘ConfigDict(…)’

drafts: tuple[tauopt.engine.strategies.base.SuggestionDraft, ...]#

None

provenance: tauopt.engine.provenance.EngineProvenance#

None

requested_q: int#

None

unique_count: int#

None

authorized_replica_count: int#

0

unfilled_count: int#

0

uniqueness_status: Literal[space_exhausted, unique_candidate_unavailable] | None#

None

tauopt.engine.ask.ask(state: tauopt.engine.state.ExperimentState, *, config: tauopt.engine.config.OptimizerConfig | None = None, policy: tauopt.engine.policy.base.Policy | None = None, n: int = 1, max_wait_s: float | None = None, on_timeout: Literal[ladder, fail] = 'ladder', intents: Sequence[tauopt.engine._intents.IntentRequest] = ()) → tauopt.engine.ask.AskResult#

Produce up to n suggestion drafts + the provenance that re-derives them.

config (explicit, wins) or policy (defaults to DefaultPolicyV1) chooses the intended strategy. strategy: "auto" is the seedable policy path: it routes exactly like config=None but carries explicit top-level fields (seed, center_in_bootstrap, dedup, duplicate_intents, queue_selection, categorical_mode, timebox_mode, base_noise – the composable flag surface) into whatever rung the policy picks and down the fallback ladder. max_wait_s deadlines the WHOLE ask; on_timeout="fail" re-raises TimeboxExceeded instead of laddering. intents are typed duplicate requests, validated – never trusted – after unique search; they require config.dedup. Flag-off asks always return exactly n drafts; dedup asks may return fewer, with the shortfall accounted on the AskResult.