PageIndex/pageindex/types.py
mountain b2756c73d2 refactor(sdk): typed returns, protocol contract, parser layering
Engineering-quality cleanups from the SDK review (no behavior change):

- Return-type discoverability: add pageindex/types.py with TypedDicts
  (DocumentInfo, DocumentDetail, PageContent) and annotate Collection /
  Backend methods with them; add docstrings to every public Collection
  method (including the get_page_content `pages` spec). Exported from the
  package. Zero runtime cost — these are plain dicts.

- Backend protocol as a real contract:
  * query_stream is an async generator, so the protocol now declares it
    as `def ... -> AsyncIterator[QueryEvent]` (not `async def`, which
    typed it as a coroutine and never matched the implementations).
  * custom-parser support is expressed as a runtime_checkable
    SupportsParserRegistration capability protocol; the client uses
    isinstance(...) instead of hasattr(...) duck-typing.

- Parser layering: move count_tokens into a leaf module pageindex/tokens.py
  so parser/* imports it from there instead of reaching back into
  pageindex.index (a reverse dependency). index.utils re-exports it for
  backward compatibility.

Adds tests/test_architecture.py enforcing: parser never imports index,
count_tokens is a single shared leaf, the capability protocol works,
both backends satisfy Backend, and the TypedDicts are exported.

Claude-Session: https://claude.ai/code/session_01Kx5DgKbhK1N8autqXH8SmS
2026-07-07 12:15:34 +08:00

32 lines
959 B
Python

# pageindex/types.py
# TypedDicts describing the plain-dict shapes the SDK returns, so callers get
# key/field discovery in their IDE without any runtime cost (these are dicts).
from __future__ import annotations
from typing import Any, TypedDict
class DocumentInfo(TypedDict):
"""A document as returned by ``list_documents()``."""
doc_id: str
doc_name: str
doc_description: str
doc_type: str
class DocumentDetail(DocumentInfo, total=False):
"""A document with its tree, as returned by ``get_document()``.
``structure`` is always present; ``file_path`` is local-only and
``status`` is cloud-only, hence total=False.
"""
structure: list[dict[str, Any]]
file_path: str # local backend only
status: str # cloud backend only
class PageContent(TypedDict, total=False):
"""One page of content, as returned by ``get_page_content()``."""
page: int
content: str
images: list[dict[str, Any]]