diff --git a/src/lab_harness/analysis/analyzer.py b/src/lab_harness/analysis/analyzer.py index 406cc65..a1dc627 100644 --- a/src/lab_harness/analysis/analyzer.py +++ b/src/lab_harness/analysis/analyzer.py @@ -143,8 +143,9 @@ def generate_script_with_ai( # Strip markdown fences if present if script.startswith("```"): - first_nl = script.index("\n") - script = script[first_nl + 1 :] + # Drop the opening fence line; a single-line reply has no newline to split on. + first_nl = script.find("\n") + script = script[first_nl + 1 :] if first_nl != -1 else script[3:] if script.endswith("```"): script = script[:-3] script = script.strip() diff --git a/tests/test_analyzer_ai_paths.py b/tests/test_analyzer_ai_paths.py index b61d2a7..a54e3ae 100644 --- a/tests/test_analyzer_ai_paths.py +++ b/tests/test_analyzer_ai_paths.py @@ -78,6 +78,16 @@ def test_markdown_fences_are_stripped(self, llm_on, tmp_path: Path, raw: str): script = Analyzer().generate_script_with_ai(tmp_path / "x.csv", "IV") assert script == "import numpy\nprint(1)" + @pytest.mark.parametrize( + ("raw", "expected"), + [("```print(1)```", "print(1)"), ("```", ""), ("```python\n```", "")], + ids=["single-line", "bare", "empty-block"], + ) + def test_fences_without_newline_do_not_raise(self, llm_on, tmp_path: Path, raw: str, expected: str): + """A fenced reply with no newline after the opening fence must not raise ValueError.""" + llm_on.return_value = _router(raw) + assert Analyzer().generate_script_with_ai(tmp_path / "x.csv", "IV") == expected + class TestInterpretResults: def test_data_path_preview_in_context(self, llm_on, tmp_path: Path):