mirror of
https://github.com/VectifyAI/PageIndex.git
synced 2026-07-15 21:11:05 +02:00
fix: empty-list scope, get_tree bool casing, md fence tracking; dedupe base URL
- local get_agent_tools: `set(doc_ids) if doc_ids is not None else None` so doc_ids=[] is a scope of nothing (reject all), not open mode. The public query path already guarded []; this hardens direct callers. - legacy get_tree: send summary=true/false (lowercase) instead of Python's capitalized True/False, matching the modern CloudBackend and the API. - markdown parser: track the opening fence character so a ```-fence isn't closed by a ~~~ line (CommonMark), keeping '#'-lines inside it out of headings. - dedupe the cloud base URL: single API_BASE in cloud_api, referenced by CloudBackend and PageIndexClient (was three independent copies). Regression tests for each.
This commit is contained in:
parent
e18ccdeaf8
commit
d97231b480
8 changed files with 83 additions and 15 deletions
|
|
@ -135,7 +135,7 @@ def test_get_ocr_and_tree_use_legacy_urls(monkeypatch):
|
|||
|
||||
assert get_calls[0]["method"] == "GET"
|
||||
assert get_calls[0]["url"] == "https://api.pageindex.ai/doc/doc-1/?type=ocr&format=page"
|
||||
assert get_calls[1]["url"] == "https://api.pageindex.ai/doc/doc-1/?type=tree&summary=True"
|
||||
assert get_calls[1]["url"] == "https://api.pageindex.ai/doc/doc-1/?type=tree&summary=true"
|
||||
|
||||
|
||||
def test_get_ocr_rejects_invalid_format():
|
||||
|
|
@ -265,6 +265,22 @@ def test_chat_completions_stream_errors_are_pageindex_api_error(monkeypatch):
|
|||
list(stream)
|
||||
|
||||
|
||||
def test_get_tree_sends_lowercase_summary_bool(monkeypatch):
|
||||
# A Python f-string renders True/False capitalized; the API expects
|
||||
# summary=true/false. A case-sensitive server would silently drop summaries.
|
||||
calls = []
|
||||
|
||||
def fake_request(method, url, **kwargs):
|
||||
calls.append(url)
|
||||
return FakeResponse(payload={"result": []})
|
||||
|
||||
monkeypatch.setattr("pageindex.cloud_api.requests.request", fake_request)
|
||||
PageIndexClient("pi-test").get_tree("doc-1", node_summary=True)
|
||||
assert "summary=true" in calls[0] and "summary=True" not in calls[0]
|
||||
PageIndexClient("pi-test").get_tree("doc-1", node_summary=False)
|
||||
assert "summary=false" in calls[1]
|
||||
|
||||
|
||||
def test_delete_document_tolerates_empty_success_body(monkeypatch):
|
||||
# A successful DELETE may return 200 with no body; delete_document must not
|
||||
# raise JSONDecodeError parsing an empty response (the doc is already gone).
|
||||
|
|
|
|||
|
|
@ -127,6 +127,18 @@ def test_scoped_mode_allows_in_scope_doc_id(populated_backend):
|
|||
assert out.get("doc_name") == "alpha.pdf"
|
||||
|
||||
|
||||
def test_empty_doc_ids_is_scoped_to_nothing_not_open_mode(populated_backend):
|
||||
# doc_ids=[] means "scope to no documents", NOT open mode. It must exclude
|
||||
# list_documents and reject every doc_id — otherwise an empty list would
|
||||
# collapse to None (truthiness) and silently grant access to the whole
|
||||
# collection.
|
||||
tools = populated_backend.get_agent_tools("papers", doc_ids=[])
|
||||
by_name = {t.name: t for t in tools.function_tools}
|
||||
assert "list_documents" not in by_name
|
||||
out = json.loads(_invoke_tool(by_name["get_document"], {"doc_id": "d1"}))
|
||||
assert "error" in out and "not in scope" in out["error"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bad_pages", ["all", "5-", "abc", "3-1"])
|
||||
def test_get_page_content_returns_actionable_error_for_bad_page_spec(populated_backend, bad_pages):
|
||||
# A malformed page spec must come back as a correctable JSON error (like the
|
||||
|
|
|
|||
|
|
@ -100,3 +100,22 @@ def test_tilde_fenced_code_blocks_are_recognized(tmp_path):
|
|||
titles = [n.title for n in result.nodes]
|
||||
assert titles == ["Real Header", "Real Sub"]
|
||||
assert "not a real header" not in " ".join(n.title for n in result.nodes)
|
||||
|
||||
|
||||
def test_backtick_fence_is_not_closed_by_a_tilde_line(tmp_path):
|
||||
"""CommonMark: a ```-opened fence is closed only by ```. A ~~~ line inside
|
||||
it is content, so a '#'-prefixed line stays inside the still-open block and
|
||||
a real heading after the real close is still recognized."""
|
||||
md = tmp_path / "mixed.md"
|
||||
md.write_text(
|
||||
"# Real Header\n"
|
||||
"```\n"
|
||||
"~~~\n" # tilde line INSIDE the backtick fence — NOT a close
|
||||
"# not a heading\n" # stays inside the still-open code block
|
||||
"```\n" # this (matching char) closes the fence
|
||||
"## Real Sub\n"
|
||||
)
|
||||
result = MarkdownParser().parse(str(md))
|
||||
titles = [n.title for n in result.nodes]
|
||||
assert titles == ["Real Header", "Real Sub"]
|
||||
assert "not a heading" not in " ".join(n.title for n in result.nodes)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue