fix(lharness): Improve proof extraction to handle LLM response formatting variations

- Extract markdown code blocks first, fallback to manual stripping
- Handle multiple 'by' keyword case variations (:= by, by line starts, etc.)
- Avoid duplicate 'by by' syntax errors on insertion
- Strip trailing whitespace in proof positions
This commit is contained in:
allaun 2026-06-27 14:46:56 -05:00
parent 5bca8fe5e3
commit 551df86e1d

View file

@ -315,21 +315,35 @@ def extract_errors(log: str) -> str:
def extract_proof_code(response: str) -> str: def extract_proof_code(response: str) -> str:
text = re.sub(r"^\s*```(?:lean)?\s*\n?", "", response, flags=re.MULTILINE) # 1. Try to extract content inside markdown code blocks first to ignore explanations
text = re.sub(r"\n\s*```\s*$", "", text, flags=re.MULTILINE) blocks = re.findall(r"```(?:lean)?\n(.*?)\n```", response, re.DOTALL | re.IGNORECASE)
text = text.strip() if blocks:
text = blocks[-1].strip()
if text.startswith(":= by"): else:
return text # Fallback to manual stripping if markdown block is unclosed or malformed
text = re.sub(r"^\s*```(?:lean)?\s*\n?", "", response, flags=re.MULTILINE)
text = re.sub(r"\n\s*```\s*$", "", text, flags=re.MULTILINE)
text = text.strip()
# 2. Extract proof body matching case variations to prevent formatting drift failures:
# Case A: Contains ":= by"
idx = text.find(":= by") idx = text.find(":= by")
if idx >= 0: if idx >= 0:
return text[idx:] return text[idx:]
idx = text.find("\nby ") # Case B: Contains ":=" followed by optional space/newline and then "by"
if idx >= 0: m = re.search(r":=\s*(by\b.*)", text, re.DOTALL)
prev_newline = text.rfind("\n", 0, idx) if m:
return text[prev_newline + 1:].strip() return ":= " + m.group(1)
# Case C: Starts with "by"
if text.startswith("by\n") or text.startswith("by "):
return text
# Case D: Contains a line starting with "by "
m = re.search(r"(?:\n|^)\s*(by\b.*)", text, re.DOTALL)
if m:
return m.group(1)
return text return text
@ -371,6 +385,20 @@ def insert_proof(lean_path: Path, sorry_line: int, proof_code: str) -> bool:
if code.startswith(":= by"): if code.startswith(":= by"):
code = code[5:].strip() code = code[5:].strip()
# Avoid duplicate "by by" syntax errors if insertion point already has "by"
insertion_line = lines[insert_idx]
has_by = "by" in insertion_line or (insert_idx > 0 and "by" in lines[insert_idx - 1])
if has_by:
if code.startswith("by "):
code = code[3:].strip()
elif code.startswith("by\n"):
code = code[2:].strip()
else:
# Ensure code starts with "by" if insertion point is missing it
if not code.startswith("by\n") and not code.startswith("by "):
code = "by\n " + code
# Build indented proof lines (preserve blank lines). # Build indented proof lines (preserve blank lines).
proof_lines: list[str] = [] proof_lines: list[str] = []
for pl in code.split("\n"): for pl in code.split("\n"):