Extract numbered steps from model output. Strips ... blocks (R1-style reasoning traces), then collects lines matching "N. step" or "N) step".
(raw: str)
| 71 | # ── Step parser ─────────────────────────────────────────────────────────────── |
| 72 | |
| 73 | def parse_steps(raw: str) -> List[str]: |
| 74 | """ |
| 75 | Extract numbered steps from model output. |
| 76 | |
| 77 | Strips <think>...</think> blocks (R1-style reasoning traces), |
| 78 | then collects lines matching "N. step" or "N) step". |
| 79 | """ |
| 80 | text = re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL).strip() |
| 81 | |
| 82 | steps: List[str] = [] |
| 83 | for line in text.splitlines(): |
| 84 | line = line.strip() |
| 85 | m = re.match(r"^(\d+)[.)]\s+(.+)$", line) |
| 86 | if m: |
| 87 | step = m.group(2).strip() |
| 88 | if step: |
| 89 | steps.append(step) |
| 90 | if steps: |
| 91 | last = steps[-1] |
| 92 | if last and last[-1] not in ".!?)" and last[-1].isalpha(): |
| 93 | print( |
| 94 | "[plannd] plan may be truncated — consider increasing max_tokens", |
| 95 | flush=True, |
| 96 | ) |
| 97 | return steps |
| 98 | |
| 99 | |
| 100 | # ── Tool-call step filter ───────────────────────────────────────────────────── |
no test coverage detected