Skip to content

Commit 1b27229

Browse files
authored
Make Instructions parsing fail on invalid input instead of logging (#219)
1 parent 4f6a325 commit 1b27229

File tree

2 files changed

+33
-46
lines changed

2 files changed

+33
-46
lines changed

agents-core/vision_agents/core/instructions.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
import re
44
from pathlib import Path
55

6-
__all__ = ["Instructions"]
6+
__all__ = ["Instructions", "InstructionsReadError"]
7+
78

89
logger = logging.getLogger(__name__)
910

1011
_INITIAL_CWD = os.getcwd()
1112

12-
_MD_PATTERN = re.compile(r"@([^\s@]+\.md)")
13+
_MD_PATTERN = re.compile(r"@([^\s@]+)")
14+
15+
16+
class InstructionsReadError(Exception): ...
1317

1418

1519
class Instructions:
@@ -62,9 +66,7 @@ def _extract_full_reference(self) -> str:
6266
for filename, content in markdown_contents.items():
6367
markdown_lines.append(f"\n### {filename}")
6468
# Only include non-empty content
65-
markdown_lines.append(
66-
content or "*(File not found or could not be read)*"
67-
)
69+
markdown_lines.append(content or "*(File is empty)*")
6870
full_reference = "\n".join(markdown_lines)
6971
return full_reference
7072

@@ -86,30 +88,28 @@ def _read_md_file(self, file_path: str | Path) -> str:
8688

8789
# Check if the path is a file, it exists, and it's a markdown file.
8890
skip_reason = ""
89-
if not full_path.is_file():
91+
if not full_path.exists():
92+
skip_reason = "file not found"
93+
elif not full_path.is_file():
9094
skip_reason = "path is not a file"
9195
elif full_path.name.startswith("."):
92-
skip_reason = "path is invalid"
93-
elif not full_path.exists():
94-
skip_reason = "file not found"
96+
skip_reason = 'filename cannot start with "."'
9597
elif full_path.suffix != ".md":
9698
skip_reason = "file is not .md"
9799
# The markdown file also must be inside the base_dir
98100
elif not full_path.is_relative_to(self._base_dir):
99-
skip_reason = "file outside the base directory"
101+
skip_reason = f"path outside the base directory {self._base_dir}"
100102

101103
if skip_reason:
102-
logger.debug(
103-
f"Skip reading instructions from {full_path}. Reason: {skip_reason}"
104+
raise InstructionsReadError(
105+
f"Failed to read instructions from {full_path}; reason - {skip_reason}"
104106
)
105-
return ""
106107

107108
try:
108109
logger.info(f"Reading instructions from file {full_path}")
109110
with open(full_path, mode="r") as f:
110111
return f.read()
111-
except (OSError, IOError, UnicodeDecodeError):
112-
logger.warning(
113-
f"Failed to read instructions from file {full_path}", exc_info=True
114-
)
115-
return ""
112+
except (OSError, IOError, UnicodeDecodeError) as exc:
113+
raise InstructionsReadError(
114+
f"Failed to read instructions from file {full_path}; reason - {exc}"
115+
) from exc

tests/test_instructions.py

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from vision_agents.core.instructions import Instructions
2+
from vision_agents.core.instructions import Instructions, InstructionsReadError
33

44

55
class TestInstructions:
@@ -28,40 +28,29 @@ def test_parse_not_a_file(self, tmp_path):
2828
file_path = tmp_path / "file1.md"
2929
file_path.mkdir()
3030
input_text = "read @file1.md"
31-
instructions = Instructions(input_text=input_text, base_dir=tmp_path)
32-
assert instructions.input_text == input_text
33-
assert (
34-
instructions.full_reference
35-
== f"{input_text}\n\n\n## Referenced Documentation:\n\n### file1.md\n*(File not found or could not be read)*"
36-
)
31+
with pytest.raises(InstructionsReadError, match="reason - path is not a file"):
32+
Instructions(input_text=input_text, base_dir=tmp_path)
3733

3834
def test_parse_file_doesnt_exist(self, tmp_path):
3935
input_text = "read @file1.md"
40-
instructions = Instructions(input_text=input_text, base_dir=tmp_path)
41-
assert instructions.input_text == input_text
42-
assert (
43-
instructions.full_reference
44-
== f"{input_text}\n\n\n## Referenced Documentation:\n\n### file1.md\n*(File not found or could not be read)*"
45-
)
36+
with pytest.raises(InstructionsReadError, match="reason - file not found"):
37+
Instructions(input_text=input_text, base_dir=tmp_path)
4638

4739
def test_parse_file_not_md(self, tmp_path):
4840
input_text = "read @file1.txt"
4941
file_path = tmp_path / "file1.txt"
5042
file_path.write_text("abcdef", encoding="utf-8")
51-
instructions = Instructions(input_text=input_text, base_dir=tmp_path)
52-
assert instructions.input_text == input_text
53-
assert instructions.full_reference == input_text
43+
with pytest.raises(InstructionsReadError, match="reason - file is not .md"):
44+
Instructions(input_text=input_text, base_dir=tmp_path)
5445

5546
def test_parse_file_hidden(self, tmp_path):
5647
input_text = "read @.file1.md"
5748
file_path = tmp_path / ".file1.md"
5849
file_path.write_text("abcdef", encoding="utf-8")
59-
instructions = Instructions(input_text=input_text, base_dir=tmp_path)
60-
assert instructions.input_text == input_text
61-
assert (
62-
instructions.full_reference
63-
== f"{input_text}\n\n\n## Referenced Documentation:\n\n### .file1.md\n*(File not found or could not be read)*"
64-
)
50+
with pytest.raises(
51+
InstructionsReadError, match='reason - filename cannot start with "."'
52+
):
53+
Instructions(input_text=input_text, base_dir=tmp_path)
6554

6655
def test_parse_file_outside_base_dir(self, tmp_path):
6756
file_path1 = tmp_path / "file1.md"
@@ -73,9 +62,7 @@ def test_parse_file_outside_base_dir(self, tmp_path):
7362
file_path1.write_text("abcdef", encoding="utf-8")
7463
file_path2.write_text("abcdef", encoding="utf-8")
7564

76-
instructions = Instructions(input_text=input_text, base_dir=base_dir)
77-
assert instructions.input_text == input_text
78-
assert (
79-
instructions.full_reference
80-
== f"{input_text}\n\n\n## Referenced Documentation:\n\n### {file_path1}\n*(File not found or could not be read)*"
81-
)
65+
with pytest.raises(
66+
InstructionsReadError, match="reason - path outside the base directory"
67+
):
68+
Instructions(input_text=input_text, base_dir=base_dir)

0 commit comments

Comments
 (0)