Document that file objects cannot be iterated
Monty 0.0.19 supports open() and with blocks, but a file object is still not iterable. See pydantic/monty#490, which is still open. The instructions now state the limitation in three places and give the alternative next to each one: readlines() or read().split("\n"). Replace chr(10) with "\n" in the prose and in the example. Both work on 0.0.19, and chr(10) implies that the escape is broken. Add a test that pins the limitation. The test fails when Monty gains iteration support, which is the signal to relax the instructions.
This commit is contained in:
parent
5c8df37af1
commit
feaca386d3
2 changed files with 48 additions and 4 deletions
|
|
@ -17,7 +17,7 @@ Inside the code, these functions are available (use `await`):
|
|||
- `await list_documents()` → list of dicts with keys: id, title, uri, created_at
|
||||
|
||||
Available modules: `json`, `re`, `math`, `pathlib`
|
||||
Not supported: class definitions, generators/yield, match statements, decorators, `collections`
|
||||
Not supported: class definitions, generators/yield, match statements, decorators, `collections`, iterating a file object (`for line in f`)
|
||||
|
||||
### analysis_search
|
||||
Search the knowledge base directly (outside code execution). Each result has a `Type:` (paragraph, table, code, list_item, picture). When the Type is `picture`, the corresponding figure may also be attached to the tool response as an image alongside the text — use it directly to answer questions about figures, diagrams, charts, screenshots.
|
||||
|
|
@ -48,7 +48,7 @@ All documents are mounted as a virtual filesystem at `/documents/`:
|
|||
`{document_id}` is an internal identifier, not the user-facing `uri` (filename, URL, etc.). When you only know a document by its URI or title, use `await list_documents()` to enumerate ids and match against `uri` / `title` — that's a single call to the host. Iterating `/documents/` and reading every `metadata.json` works too but is much slower on portal-scale corpora.
|
||||
|
||||
### Reading files
|
||||
Read with `Path.read_text()` or `open()` (including `with` blocks); file objects support `.read()`, `.readline()`, and `.readlines()`. File objects are NOT iterable — do not write `for line in f`; use `.readlines()` or `text.split(chr(10))` for line-wise processing. Files are read-only; writing raises `PermissionError`.
|
||||
Read with `Path.read_text()` or `open()` (including `with` blocks); file objects support `.read()`, `.readline()`, and `.readlines()`. A file object cannot be iterated, so read line-wise with `.readlines()` or `.read().split("\n")` instead of `for line in f`. Files are read-only; writing raises `PermissionError`.
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
|
@ -63,7 +63,7 @@ for doc_dir in Path('/documents').iterdir():
|
|||
content = Path(f'/documents/{doc_id}/content.txt').read_text()
|
||||
|
||||
# Read and parse items
|
||||
for line in Path(f'/documents/{doc_id}/items.jsonl').read_text().strip().split(chr(10)):
|
||||
for line in Path(f'/documents/{doc_id}/items.jsonl').read_text().strip().split("\n"):
|
||||
item = json.loads(line)
|
||||
if item['label'] == 'table':
|
||||
print(item['text'][:200])
|
||||
|
|
@ -112,6 +112,6 @@ You MUST call `analysis_cite` with at least one chunk ID before producing your f
|
|||
- Use `print()` to output results — the output is your only feedback
|
||||
- When you write code, execute it — don't describe what code would do. But not every question needs code; simple lookups are best answered by `analysis_search → analysis_cite`.
|
||||
- Use `await` for all async functions inside `analysis_execute_code` (`search`, `list_documents`)
|
||||
- Read files with `Path.read_text()` or `open()`/`with`; file objects are not iterable (no `for line in f`) and the `collections` module is unavailable
|
||||
- Read files with `Path.read_text()` or `open()`/`with`. For lines use `.readlines()` or `.read().split("\n")`, never `for line in f`. The `collections` module is unavailable.
|
||||
- Do NOT include chunk IDs or UUIDs in your answer text — your answer should read naturally. Use the `analysis_cite` tool separately to register citations. `cite{...}` markdown-style inline references do nothing; only an actual `analysis_cite` tool call registers a citation.
|
||||
- **Before you write your final answer, invoke the `analysis_cite` tool with the supporting chunk_ids.** This is the last tool call before answering whenever your answer draws on retrieved evidence.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import pytest
|
|||
from haiku.rag.client import HaikuRAG
|
||||
from haiku.rag.config.models import AppConfig
|
||||
from haiku.rag.sandbox import AnalysisContext, Sandbox, SandboxResult
|
||||
from haiku.rag.store.models.chunk import Chunk
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
|
@ -454,6 +455,49 @@ class TestSandboxVFS:
|
|||
assert "DENIED" in result.stdout
|
||||
assert "WROTE" not in result.stdout
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_file_objects_are_not_iterable(self, temp_db_path):
|
||||
"""Pins the limitation the instructions warn about: pydantic/monty#490.
|
||||
|
||||
A failure here means Monty gained iteration support and the
|
||||
`for line in f` prohibition in the analysis instructions is now wrong.
|
||||
"""
|
||||
from docling_core.types.doc.document import DoclingDocument
|
||||
from docling_core.types.doc.labels import DocItemLabel
|
||||
|
||||
config = AppConfig()
|
||||
docling = DoclingDocument(name="d")
|
||||
docling.add_text(label=DocItemLabel.TEXT, text="one\ntwo")
|
||||
|
||||
async with HaikuRAG(temp_db_path, create=True) as client:
|
||||
doc = await client.import_document(
|
||||
docling,
|
||||
[
|
||||
Chunk(
|
||||
content="one\ntwo",
|
||||
embedding=[0.1] * config.embeddings.model.vector_dim,
|
||||
order=0,
|
||||
)
|
||||
],
|
||||
uri="test://lines",
|
||||
)
|
||||
|
||||
sb = Sandbox(db_path=temp_db_path, config=config, context=AnalysisContext())
|
||||
try:
|
||||
result = await sb.execute(
|
||||
f"for line in open('/documents/{doc.id}/content.txt'):\n print(line)"
|
||||
)
|
||||
assert result.success is False
|
||||
assert "not iterable" in result.stderr
|
||||
|
||||
# The documented alternatives do work.
|
||||
result = await sb.execute(
|
||||
f"print(len(open('/documents/{doc.id}/content.txt').readlines()))"
|
||||
)
|
||||
assert result.success, result.stderr
|
||||
finally:
|
||||
await sb.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr()
|
||||
async def test_context_filter_limits_vfs(self, temp_db_path):
|
||||
|
|
|
|||
Loading…
Reference in a new issue