diff --git a/autoresearch/prefill/supervisor.py b/autoresearch/prefill/supervisor.py index 399d238..648c630 100644 --- a/autoresearch/prefill/supervisor.py +++ b/autoresearch/prefill/supervisor.py @@ -64,7 +64,24 @@ def _candidate_snapshot(module) -> dict: def validate_candidate(candidate: dict) -> None: - missing = [field for field in REQUIRED_CANDIDATE_FIELDS if not candidate.get(field)] + text_fields = { + "candidate_id", + "target_obligation_id", + "hypothesis", + "generator_directive", + "critic_directive", + } + missing = [ + field + for field in REQUIRED_CANDIDATE_FIELDS + if ( + not candidate.get(field) + or ( + field in text_fields + and not isinstance(candidate.get(field), str) + ) + ) + ] if missing: raise ValueError(f"candidate missing fields: {missing}") if candidate["prefill_compute_chunk_tokens"] not in (64, 128, 256): @@ -148,6 +165,41 @@ def repair_candidate_schema( repaired[field] = value changed.append(field) break + nested_hypothesis = repaired.get("hypothesis") + if isinstance(nested_hypothesis, dict): + nested_target = ( + nested_hypothesis.get("target_obligation") + or nested_hypothesis.get("target_obligation_id") + or nested_hypothesis.get("target") + ) + if nested_target and not repaired.get("target_obligation_id"): + repaired["target_obligation_id"] = str(nested_target).strip() + changed.append("target_obligation_id") + repaired["hypothesis"] = str( + nested_hypothesis.get("statement") + or nested_hypothesis.get("text") + or nested_hypothesis.get("claim") + or "" + ).strip() + changed.append("hypothesis") + for field in ( + "candidate_id", + "target_obligation_id", + "generator_directive", + "critic_directive", + ): + value = repaired.get(field) + if isinstance(value, dict): + repaired[field] = str( + value.get("statement") + or value.get("text") + or value.get("content") + or "" + ).strip() + changed.append(field) + elif value is not None and not isinstance(value, str): + repaired[field] = str(value).strip() + changed.append(field) target = str(repaired.get("target_obligation_id", "")) leaves = _pending_leaf_ids(ledger) if target not in leaves: @@ -164,10 +216,19 @@ def repair_candidate_schema( if item.get("obligation_id") == target ) hypothesis = str(repaired.get("hypothesis", "")).strip() + repaired["hypothesis"] = hypothesis + plan = repaired.get("plan", {}) + plan_steps = plan.get("steps", []) if isinstance(plan, dict) else [] + plan_text = " ".join( + f"Step {index}: {str(step).strip()}" + for index, step in enumerate(plan_steps, start=1) + if str(step).strip() + ) if not repaired.get("generator_directive") and hypothesis: repaired["generator_directive"] = ( f"Focus exclusively on {target}: {statement} " - f"Construct and test this hypothesis: {hypothesis}" + f"Construct and test this hypothesis: {hypothesis} " + f"{plan_text}" ) changed.append("generator_directive") if not repaired.get("critic_directive") and hypothesis: diff --git a/tests/inference_engine/bench/test_autoresearch_supervisor.py b/tests/inference_engine/bench/test_autoresearch_supervisor.py index ba3dd52..d06b34f 100644 --- a/tests/inference_engine/bench/test_autoresearch_supervisor.py +++ b/tests/inference_engine/bench/test_autoresearch_supervisor.py @@ -125,6 +125,54 @@ def test_strategy_schema_repair_accepts_uppercase_and_alias_keys(): } +def test_strategy_schema_repair_flattens_nested_hypothesis_and_plan(): + repaired, fields = repair_candidate_schema( + { + "candidate_id": "candidate-v2-c2-sub-01", + "hypothesis": { + "statement": "Construct regularized analytic continuations.", + "target_obligation": "RH-C2-child", + }, + "plan": { + "steps": [ + "Define a regularization kernel.", + "Attempt to prove compact convergence.", + ], + }, + }, + current={**_candidate(), "target_obligation_id": "RH-C2"}, + ledger={"obligations": [ + { + "obligation_id": "RH-C2", + "statement": "Zero convergence.", + "status": "UNRESOLVED", + "parent_id": "", + }, + { + "obligation_id": "RH-C2-child", + "statement": "Prove regularized compact convergence.", + "status": "UNRESOLVED", + "parent_id": "RH-C2", + }, + ]}, + ) + assert repaired["hypothesis"] == ( + "Construct regularized analytic continuations." + ) + assert repaired["target_obligation_id"] == "RH-C2-child" + assert "Step 1: Define a regularization kernel." in ( + repaired["generator_directive"] + ) + assert isinstance(repaired["hypothesis"], str) + assert { + "hypothesis", + "target_obligation_id", + "generator_directive", + "critic_directive", + "prefill_compute_chunk_tokens", + }.issubset(set(fields)) + + def test_keep_requires_novel_mathematical_advancement(): baseline = { "proof_obligations_unresolved": "5",