90 lines
4.4 KiB
Python
90 lines
4.4 KiB
Python
"""Bounded review batching after complete-suite discovery and reconciliation."""
|
|
from suite_contract import Problem
|
|
from suite_policy import MAX_GROUP, invocation, validate_natural
|
|
|
|
MAX_REVIEW_BATCHES = 8
|
|
MAX_MODEL_CALLS = 3 + 2 * MAX_REVIEW_BATCHES
|
|
|
|
|
|
def material(stage, original, reviewed=None):
|
|
"""Retain every original oversized family for semantic review and its audit."""
|
|
context = {"oversized_families": [g["members"] for g in original["groups"] if len(g["members"]) > MAX_GROUP]}
|
|
if stage == "large_family_review":
|
|
context["natural_partition"] = original
|
|
else:
|
|
context.update(original_partition=original, reviewed_partition=reviewed)
|
|
return context
|
|
|
|
|
|
def batch_request(stage, source, originals, reviewed, index, total):
|
|
"""Slice only reconciled family boundaries, retaining all their source fields."""
|
|
aliases = {a for g in originals for a in g["members"]}
|
|
subset = {**source, "cases": [c for c in source["cases"] if c["alias"] in aliases]}
|
|
prior = None if reviewed is None else {
|
|
"groups": [g for g in reviewed["groups"] if set(g["members"]) <= aliases],
|
|
"decisions": [d for d in reviewed["decisions"] if set(d["source_members"]) <= aliases]}
|
|
context = material(stage, {"groups": originals}, prior)
|
|
context["review_batch"] = {"index": index, "total": total,
|
|
"case_count": len(aliases), "whole_suite_case_count": len(source["cases"])}
|
|
count = len(prior["groups"]) if prior else len(originals)
|
|
return subset, context, count
|
|
|
|
|
|
def review_stage(workflow, stage, source, original, reviewed, capacity):
|
|
"""Use one full review when it fits, otherwise bounded whole-family batches.
|
|
|
|
Discovery and reconciliation already considered every suite case together.
|
|
Reviews may refine only within an original family under the existing policy;
|
|
their execution batches therefore introduce no new semantic boundaries.
|
|
"""
|
|
context = material(stage, original, reviewed)
|
|
count = len((reviewed or original)["groups"])
|
|
try:
|
|
result = workflow.call(stage, source, context, count)
|
|
except Problem as exc:
|
|
if exc.code not in {"pass_capacity", "pass_request_too_large"}:
|
|
raise
|
|
else:
|
|
validate_natural(result, source, original["groups"])
|
|
return result
|
|
|
|
large = sorted((g for g in original["groups"] if len(g["members"]) > MAX_GROUP),
|
|
key=lambda g: tuple(sorted(g["members"])))
|
|
batches, current = [], []
|
|
for family in large:
|
|
candidate = current + [family]
|
|
subset, context, count = batch_request(stage, source, candidate, reviewed, 8, 8)
|
|
try:
|
|
capacity(invocation(stage, subset, context), workflow.provider, len(subset["cases"]), count)
|
|
except Problem as exc:
|
|
if exc.code not in {"pass_capacity", "pass_request_too_large"}:
|
|
raise
|
|
if not current:
|
|
raise Problem("review_family_capacity", 422, review_pass=stage,
|
|
case_count=len(family["members"]), failure_stage="review_batch_preflight") from None
|
|
batches.append(current)
|
|
current = [family]
|
|
else:
|
|
current = candidate
|
|
if current:
|
|
batches.append(current)
|
|
if not batches or len(batches) > MAX_REVIEW_BATCHES:
|
|
raise Problem("review_batch_limit", 422, review_pass=stage, batch_count=len(batches))
|
|
calls = [batch_request(stage, source, group, reviewed, i, len(batches))
|
|
for i, group in enumerate(batches, 1)]
|
|
# Admit every batch before launching any paid review call.
|
|
for subset, context, count in calls:
|
|
capacity(invocation(stage, subset, context), workflow.provider, len(subset["cases"]), count)
|
|
workflow.max_calls += len(calls) - 1
|
|
if workflow.max_calls > MAX_MODEL_CALLS:
|
|
raise Problem("model_pass_limit", 502)
|
|
combined = {"groups": [g for g in original["groups"] if len(g["members"]) <= MAX_GROUP], "decisions": []}
|
|
for originals, (subset, context, count) in zip(batches, calls):
|
|
value = workflow.call(stage, subset, context, count)
|
|
validate_natural(value, subset, originals)
|
|
combined["groups"].extend(value["groups"])
|
|
combined["decisions"].extend(value["decisions"])
|
|
# Recheck global naming, membership, boundaries and all original decisions.
|
|
validate_natural(combined, source, original["groups"])
|
|
return combined
|