620 lines
24 KiB
Python
620 lines
24 KiB
Python
"""Inert CandidateAttempt run-binding shell per ADR-0232."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from dataclasses import dataclass
|
|
from enum import Enum, unique
|
|
|
|
from generate.candidate_operator import CandidateOperatorResult
|
|
from generate.geometric_search_run import (
|
|
BudgetCharge,
|
|
CandidateAttempt,
|
|
CandidateReplayStatus,
|
|
GeometricSearchRun,
|
|
)
|
|
from generate.kernel_facts import SourceSpan
|
|
|
|
CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION = "candidate_attempt_run_binding.v1"
|
|
|
|
|
|
@unique
|
|
class RunAttemptBindingRefusalReason(str, Enum):
|
|
INVALID_BINDING_INPUT = "invalid_binding_input"
|
|
UNSUPPORTED_BINDING_POLICY = "unsupported_binding_policy"
|
|
RUN_RESULT_MISMATCH = "run_result_mismatch"
|
|
ATTEMPT_RESULT_MISMATCH = "attempt_result_mismatch"
|
|
RECONSTRUCTION_RESULT_MISMATCH = "reconstruction_result_mismatch"
|
|
REPLAY_STATUS_NOT_PENDING = "replay_status_not_pending"
|
|
REPLAY_BLOCKERS_PRESENT = "replay_blockers_present"
|
|
DUPLICATE_ATTEMPT_INDEX = "duplicate_attempt_index"
|
|
DUPLICATE_ATTEMPT_ID = "duplicate_attempt_id"
|
|
DUPLICATE_CANDIDATE_DIGEST = "duplicate_candidate_digest"
|
|
BUDGET_CHARGE_EXCEEDS_REMAINING = "budget_charge_exceeds_remaining"
|
|
OPERATOR_SET_MISMATCH = "operator_set_mismatch"
|
|
RUN_IDENTITY_MISMATCH = "run_identity_mismatch"
|
|
MALFORMED_EVIDENCE_SPAN = "malformed_evidence_span"
|
|
UNSUPPORTED_SCHEMA_VERSION = "unsupported_schema_version"
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class CandidateAttemptRunBindingInput:
|
|
input_digest: str
|
|
binding_policy_version: str
|
|
original_run_id: str
|
|
original_run_policy_version: str
|
|
original_run_input_digest: str
|
|
operator_result_id: str
|
|
operator_policy_version: str
|
|
candidate_attempt_id: str
|
|
attempt_index: int
|
|
candidate_digest: str
|
|
candidate_reconstruction_digest: str
|
|
operator_set_id: str
|
|
operator_set_version: str
|
|
budget_id: str
|
|
gate_decision_id: str
|
|
residual_ids: tuple[str, ...]
|
|
problem_frame_digest: str
|
|
original_contract_assessment_id: str
|
|
schema_versions: tuple[tuple[str, str], ...]
|
|
policy_versions: tuple[tuple[str, str], ...]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class CandidateAttemptRunBinding:
|
|
binding_id: str
|
|
binding_policy_version: str
|
|
input_digest: str
|
|
original_run_id: str
|
|
operator_result_id: str
|
|
candidate_attempt_id: str
|
|
attempt_index: int
|
|
candidate_digest: str
|
|
candidate_reconstruction_digest: str
|
|
candidate_attempt_ref: str
|
|
budget_charge: BudgetCharge
|
|
depth: int
|
|
step_index: int
|
|
run_attempt_membership: str
|
|
evidence_spans: tuple[SourceSpan, ...]
|
|
reason_codes: tuple[str, ...]
|
|
explanation: str
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class CandidateAttemptRunBindingRefusal:
|
|
binding_refusal_id: str
|
|
binding_policy_version: str
|
|
input_digest: str | None
|
|
original_run_id: str | None
|
|
operator_result_id: str | None
|
|
candidate_attempt_id: str | None
|
|
reason_codes: tuple[str, ...]
|
|
explanation: str
|
|
|
|
|
|
CandidateAttemptRunBindingOutcome = (
|
|
CandidateAttemptRunBinding | CandidateAttemptRunBindingRefusal
|
|
)
|
|
|
|
|
|
def _canonical_digest(payload: dict[str, object]) -> str:
|
|
encoded = json.dumps(
|
|
payload,
|
|
ensure_ascii=False,
|
|
sort_keys=True,
|
|
separators=(",", ":"),
|
|
).encode("utf-8")
|
|
return hashlib.sha256(encoded).hexdigest()
|
|
|
|
|
|
def _span_payload(span: SourceSpan) -> dict[str, object]:
|
|
return {
|
|
"text": span.text,
|
|
"start": span.start,
|
|
"end": span.end,
|
|
"sentence_index": span.sentence_index,
|
|
}
|
|
|
|
|
|
def _budget_charge_payload(charge: BudgetCharge) -> dict[str, int]:
|
|
return {"candidates": charge.candidates, "steps": charge.steps}
|
|
|
|
|
|
def _candidate_attempt_payload(attempt: CandidateAttempt) -> dict[str, object]:
|
|
return {
|
|
"attempt_id": attempt.attempt_id,
|
|
"attempt_index": attempt.attempt_index,
|
|
"parent_attempt_id": attempt.parent_attempt_id,
|
|
"operator_id": attempt.operator_id,
|
|
"operator_version": attempt.operator_version,
|
|
"input_digest": attempt.input_digest,
|
|
"candidate_digest": attempt.candidate_digest,
|
|
"budget_charge": _budget_charge_payload(attempt.budget_charge),
|
|
"depth": attempt.depth,
|
|
"step_index": attempt.step_index,
|
|
"replay_status": attempt.replay_status.value,
|
|
"replay_blockers": list(attempt.replay_blockers),
|
|
"evidence_spans": [_span_payload(span) for span in attempt.evidence_spans],
|
|
}
|
|
|
|
|
|
def _version_pairs_payload(versions: tuple[tuple[str, str], ...]) -> list[list[str]]:
|
|
return [[name, version] for name, version in versions]
|
|
|
|
|
|
def _valid_version_pairs(value: object) -> bool:
|
|
if not isinstance(value, tuple):
|
|
return False
|
|
names: list[str] = []
|
|
for entry in value:
|
|
if not isinstance(entry, tuple) or len(entry) != 2:
|
|
return False
|
|
name, version = entry
|
|
if not isinstance(name, str) or not name:
|
|
return False
|
|
if not isinstance(version, str) or not version:
|
|
return False
|
|
names.append(name)
|
|
return len(names) == len(set(names)) and names == sorted(names)
|
|
|
|
|
|
def _valid_span(span: object) -> bool:
|
|
return (
|
|
isinstance(span, SourceSpan)
|
|
and isinstance(span.text, str)
|
|
and type(span.start) is int
|
|
and type(span.end) is int
|
|
and span.start >= 0
|
|
and span.end >= span.start
|
|
and (span.sentence_index is None or type(span.sentence_index) is int)
|
|
)
|
|
|
|
|
|
def _valid_spans(spans: object) -> bool:
|
|
return isinstance(spans, tuple) and all(_valid_span(span) for span in spans)
|
|
|
|
|
|
def _binding_input_payload(
|
|
*,
|
|
original_run: GeometricSearchRun,
|
|
candidate_operator_result: CandidateOperatorResult,
|
|
schema_versions: tuple[tuple[str, str], ...],
|
|
policy_versions: tuple[tuple[str, str], ...],
|
|
) -> dict[str, object]:
|
|
attempt = candidate_operator_result.candidate_attempt
|
|
reconstruction = candidate_operator_result.candidate_reconstruction
|
|
return {
|
|
"binding_policy_version": CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION,
|
|
"original_run_id": original_run.run_id,
|
|
"original_run_policy_version": original_run.run_policy_version,
|
|
"original_run_input_digest": original_run.input_digest,
|
|
"operator_result_id": candidate_operator_result.operator_result_id,
|
|
"operator_policy_version": candidate_operator_result.operator_policy_version,
|
|
"candidate_attempt_id": attempt.attempt_id,
|
|
"attempt_index": attempt.attempt_index,
|
|
"candidate_digest": attempt.candidate_digest,
|
|
"candidate_reconstruction_digest": reconstruction.candidate_reconstruction_digest,
|
|
"operator_set_id": original_run.operator_set_id,
|
|
"operator_set_version": original_run.operator_set_version,
|
|
"budget_id": original_run.budget_id,
|
|
"gate_decision_id": original_run.gate_decision_id,
|
|
"residual_ids": list(original_run.residual_ids),
|
|
"problem_frame_digest": original_run.problem_frame_digest,
|
|
"original_contract_assessment_id": original_run.contract_assessment_id,
|
|
"schema_versions": _version_pairs_payload(schema_versions),
|
|
"policy_versions": _version_pairs_payload(policy_versions),
|
|
}
|
|
|
|
|
|
def _refusal_id_payload(
|
|
*,
|
|
input_digest: str | None,
|
|
original_run_id: str | None,
|
|
operator_result_id: str | None,
|
|
candidate_attempt_id: str | None,
|
|
reason_codes: tuple[str, ...],
|
|
) -> dict[str, object]:
|
|
return {
|
|
"binding_refusal_id": "",
|
|
"binding_policy_version": CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION,
|
|
"input_digest": input_digest,
|
|
"original_run_id": original_run_id,
|
|
"operator_result_id": operator_result_id,
|
|
"candidate_attempt_id": candidate_attempt_id,
|
|
"reason_codes": list(reason_codes),
|
|
}
|
|
|
|
|
|
def _refusal(
|
|
*,
|
|
reason: RunAttemptBindingRefusalReason,
|
|
input_digest: str | None = None,
|
|
original_run_id: str | None = None,
|
|
operator_result_id: str | None = None,
|
|
candidate_attempt_id: str | None = None,
|
|
) -> CandidateAttemptRunBindingRefusal:
|
|
reason_codes = (reason.value,)
|
|
binding_refusal_id = _canonical_digest(
|
|
_refusal_id_payload(
|
|
input_digest=input_digest,
|
|
original_run_id=original_run_id,
|
|
operator_result_id=operator_result_id,
|
|
candidate_attempt_id=candidate_attempt_id,
|
|
reason_codes=reason_codes,
|
|
)
|
|
)
|
|
return CandidateAttemptRunBindingRefusal(
|
|
binding_refusal_id=binding_refusal_id,
|
|
binding_policy_version=CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION,
|
|
input_digest=input_digest,
|
|
original_run_id=original_run_id,
|
|
operator_result_id=operator_result_id,
|
|
candidate_attempt_id=candidate_attempt_id,
|
|
reason_codes=reason_codes,
|
|
explanation="CandidateAttempt run binding refused: " + reason.value + ".",
|
|
)
|
|
|
|
|
|
def _binding_id_payload(
|
|
*,
|
|
input_digest: str,
|
|
original_run_id: str,
|
|
operator_result_id: str,
|
|
candidate_attempt_id: str,
|
|
attempt_index: int,
|
|
candidate_digest: str,
|
|
candidate_reconstruction_digest: str,
|
|
candidate_attempt_ref: str,
|
|
budget_charge: BudgetCharge,
|
|
depth: int,
|
|
step_index: int,
|
|
evidence_spans: tuple[SourceSpan, ...],
|
|
) -> dict[str, object]:
|
|
return {
|
|
"binding_id": "",
|
|
"binding_policy_version": CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION,
|
|
"input_digest": input_digest,
|
|
"original_run_id": original_run_id,
|
|
"operator_result_id": operator_result_id,
|
|
"candidate_attempt_id": candidate_attempt_id,
|
|
"attempt_index": attempt_index,
|
|
"candidate_digest": candidate_digest,
|
|
"candidate_reconstruction_digest": candidate_reconstruction_digest,
|
|
"candidate_attempt_ref": candidate_attempt_ref,
|
|
"budget_charge": _budget_charge_payload(budget_charge),
|
|
"depth": depth,
|
|
"step_index": step_index,
|
|
"run_attempt_membership": "structurally_bound",
|
|
"evidence_spans": [_span_payload(span) for span in evidence_spans],
|
|
"reason_codes": [],
|
|
}
|
|
|
|
|
|
def _operator_provenance_has(
|
|
provenance: tuple[tuple[str, str], ...], key: str, value: str
|
|
) -> bool:
|
|
return (key, value) in provenance
|
|
|
|
|
|
def bind_candidate_attempt_to_run(
|
|
*,
|
|
original_run: GeometricSearchRun,
|
|
candidate_operator_result: CandidateOperatorResult,
|
|
schema_versions: tuple[tuple[str, str], ...] = (),
|
|
policy_versions: tuple[tuple[str, str], ...] = (),
|
|
explanation: str = "",
|
|
) -> CandidateAttemptRunBindingOutcome:
|
|
"""Bind an existing candidate operator result to one immutable run episode."""
|
|
|
|
if not isinstance(original_run, GeometricSearchRun):
|
|
return _refusal(reason=RunAttemptBindingRefusalReason.INVALID_BINDING_INPUT)
|
|
if not isinstance(candidate_operator_result, CandidateOperatorResult):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.INVALID_BINDING_INPUT,
|
|
original_run_id=original_run.run_id,
|
|
)
|
|
|
|
attempt = candidate_operator_result.candidate_attempt
|
|
reconstruction = candidate_operator_result.candidate_reconstruction
|
|
|
|
if not isinstance(attempt, CandidateAttempt):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.INVALID_BINDING_INPUT,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
)
|
|
|
|
if not _valid_version_pairs(schema_versions) or not _valid_version_pairs(
|
|
policy_versions
|
|
):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.UNSUPPORTED_SCHEMA_VERSION,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if candidate_operator_result.geometric_search_run_id != original_run.run_id:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.RUN_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if candidate_operator_result.attempt_id != attempt.attempt_id:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.ATTEMPT_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if candidate_operator_result.attempt_index != attempt.attempt_index:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.ATTEMPT_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if candidate_operator_result.candidate_digest != attempt.candidate_digest:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.ATTEMPT_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if candidate_operator_result.input_digest != attempt.input_digest:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.ATTEMPT_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if candidate_operator_result.operator_name != attempt.operator_id:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.ATTEMPT_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if candidate_operator_result.operator_version != attempt.operator_version:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.ATTEMPT_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if reconstruction.candidate_digest != candidate_operator_result.candidate_digest:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.RECONSTRUCTION_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if (
|
|
reconstruction.candidate_reconstruction_digest
|
|
!= candidate_operator_result.candidate_reconstruction_digest
|
|
):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.RECONSTRUCTION_RESULT_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if attempt.replay_status is not CandidateReplayStatus.REPLAY_PENDING:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.REPLAY_STATUS_NOT_PENDING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if attempt.replay_blockers != ():
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.REPLAY_BLOCKERS_PRESENT,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if reconstruction.source_residual_id not in original_run.residual_ids:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.RUN_IDENTITY_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if reconstruction.problem_frame_digest != original_run.problem_frame_digest:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.RUN_IDENTITY_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if (
|
|
reconstruction.original_contract_assessment_id
|
|
!= original_run.contract_assessment_id
|
|
):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.RUN_IDENTITY_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if not _operator_provenance_has(
|
|
reconstruction.operator_provenance,
|
|
"operator_set_id",
|
|
original_run.operator_set_id,
|
|
) or not _operator_provenance_has(
|
|
reconstruction.operator_provenance,
|
|
"operator_set_version",
|
|
original_run.operator_set_version,
|
|
):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.OPERATOR_SET_MISMATCH,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
if not _valid_spans(attempt.evidence_spans):
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.MALFORMED_EVIDENCE_SPAN,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if candidate_operator_result.evidence_spans != attempt.evidence_spans:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.MALFORMED_EVIDENCE_SPAN,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if reconstruction.evidence_spans != attempt.evidence_spans:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.MALFORMED_EVIDENCE_SPAN,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
for existing in original_run.candidate_attempts:
|
|
if existing.attempt_index == attempt.attempt_index:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.DUPLICATE_ATTEMPT_INDEX,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if existing.attempt_id == attempt.attempt_id:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.DUPLICATE_ATTEMPT_ID,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if existing.candidate_digest == attempt.candidate_digest:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.DUPLICATE_CANDIDATE_DIGEST,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
consumed = original_run.budget_consumed
|
|
charge = attempt.budget_charge
|
|
if charge.candidates < 0 or charge.steps < 0 or attempt.depth < 0:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.BUDGET_CHARGE_EXCEEDS_REMAINING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if attempt.step_index < 0:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.BUDGET_CHARGE_EXCEEDS_REMAINING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if consumed.candidates_considered + charge.candidates > consumed.max_candidates:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.BUDGET_CHARGE_EXCEEDS_REMAINING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if consumed.steps_used + charge.steps > consumed.max_steps:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.BUDGET_CHARGE_EXCEEDS_REMAINING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if max(consumed.depth_reached, attempt.depth) > consumed.max_depth:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.BUDGET_CHARGE_EXCEEDS_REMAINING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
if consumed.parallelism_used > consumed.max_parallelism:
|
|
return _refusal(
|
|
reason=RunAttemptBindingRefusalReason.BUDGET_CHARGE_EXCEEDS_REMAINING,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
)
|
|
|
|
input_payload = _binding_input_payload(
|
|
original_run=original_run,
|
|
candidate_operator_result=candidate_operator_result,
|
|
schema_versions=schema_versions,
|
|
policy_versions=policy_versions,
|
|
)
|
|
input_digest = _canonical_digest(input_payload)
|
|
binding_input = CandidateAttemptRunBindingInput(
|
|
input_digest=input_digest,
|
|
residual_ids=original_run.residual_ids,
|
|
schema_versions=schema_versions,
|
|
policy_versions=policy_versions,
|
|
**{
|
|
key: value
|
|
for key, value in input_payload.items()
|
|
if key not in {"residual_ids", "schema_versions", "policy_versions"}
|
|
},
|
|
)
|
|
candidate_attempt_ref = _canonical_digest(_candidate_attempt_payload(attempt))
|
|
binding_id = _canonical_digest(
|
|
_binding_id_payload(
|
|
input_digest=binding_input.input_digest,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
attempt_index=attempt.attempt_index,
|
|
candidate_digest=attempt.candidate_digest,
|
|
candidate_reconstruction_digest=reconstruction.candidate_reconstruction_digest,
|
|
candidate_attempt_ref=candidate_attempt_ref,
|
|
budget_charge=charge,
|
|
depth=attempt.depth,
|
|
step_index=attempt.step_index,
|
|
evidence_spans=attempt.evidence_spans,
|
|
)
|
|
)
|
|
return CandidateAttemptRunBinding(
|
|
binding_id=binding_id,
|
|
binding_policy_version=CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION,
|
|
input_digest=binding_input.input_digest,
|
|
original_run_id=original_run.run_id,
|
|
operator_result_id=candidate_operator_result.operator_result_id,
|
|
candidate_attempt_id=attempt.attempt_id,
|
|
attempt_index=attempt.attempt_index,
|
|
candidate_digest=attempt.candidate_digest,
|
|
candidate_reconstruction_digest=reconstruction.candidate_reconstruction_digest,
|
|
candidate_attempt_ref=candidate_attempt_ref,
|
|
budget_charge=charge,
|
|
depth=attempt.depth,
|
|
step_index=attempt.step_index,
|
|
run_attempt_membership="structurally_bound",
|
|
evidence_spans=attempt.evidence_spans,
|
|
reason_codes=(),
|
|
explanation=explanation
|
|
or "CandidateAttempt structurally bound to GeometricSearchRun.",
|
|
)
|
|
|
|
|
|
__all__ = [
|
|
"CANDIDATE_ATTEMPT_RUN_BINDING_POLICY_VERSION",
|
|
"RunAttemptBindingRefusalReason",
|
|
"CandidateAttemptRunBindingInput",
|
|
"CandidateAttemptRunBinding",
|
|
"CandidateAttemptRunBindingRefusal",
|
|
"CandidateAttemptRunBindingOutcome",
|
|
"bind_candidate_attempt_to_run",
|
|
]
|