Skip to content

fix: the .env search ends at the cwd tree; a local client with a blank chat_model refuses at the chat door; storage_path is typed PathLike - #428

Merged
rejojer merged 3 commits into
mainfrom
fix/client-config-followups
Aug 26, 2026
Merged

fix: the .env search ends at the cwd tree; a local client with a blank chat_model refuses at the chat door; storage_path is typed PathLike#428
rejojer merged 3 commits into
mainfrom
fix/client-config-followups

Conversation

@rejojer

@rejojer rejojer commented Aug 26, 2026

Copy link
Copy Markdown
Member

Three follow-ups to #424, on the shipped v0.2.11.

.env search ends at the cwd tree. find_dotenv(usecwd=True) returns '' when nothing is reachable from the cwd, and or None turned that into load_dotenv's own upward walk from utils.py — the install-dir search the cwd search was added to replace. A pip-installed SDK could silently load another project's .env from above site-packages. Test plants a .env above a symlinked package and runs from a tree that has none.

A local client with a blank chat_model refuses at the chat door. _local_chat reads blank as "managed chat", which a client without an api_key does not have: chat_completions() reached for LocalAPI.chat_completions and raised a bare AttributeError. The managed branch now raises a PageIndexAPIError naming chat_model.

storage_path is typed str | os.PathLike[str]. _ARG_TYPES accepts os.PathLike, so Path(...) ran fine, and py.typed made the Optional[str] annotation authoritative for callers' type checkers. Both signatures and LocalIndexConfig now agree with the runtime check.

Also on the branch: index= / chat= typed Mapping[str, Any] so the exported config shapes pass, plus a comment and two docstrings that stop overclaiming.

434 tests (432 + 2, both red on main). A review view of #424 + these follow-ups together is open against pre-424-main.

https://claude.ai/code/session_017Fd7jVm366S2Xamzhxv6yb

… a blank chat_model refuses at the chat door; storage_path is typed PathLike

find_dotenv(usecwd=True) returns '' when nothing is reachable from the
cwd, and `or None` turned that into load_dotenv's own upward walk from
utils.py — the install-dir leak the cwd search was added to replace. A
pip-installed SDK could load another project's .env from above
site-packages, silently.

_local_chat treats a blank chat_model as "managed chat", which a client
without an api_key does not have: chat_completions() then reached for
LocalAPI.chat_completions and raised a bare AttributeError. The managed
branch now refuses as a PageIndexAPIError naming chat_model.

py.typed made the annotations authoritative while storage_path was typed
str; _ARG_TYPES accepts os.PathLike, so Path(...) ran fine and failed the
user's type check. Both signatures and LocalIndexConfig now say so.

Claude-Session: https://claude.ai/code/session_017Fd7jVm366S2Xamzhxv6yb
… two docstrings stop overclaiming

The slots were annotated dict[str, Any]. A TypedDict is consistent with
Mapping[str, object], never with dict (PEP 589: a dict-typed receiver
could write arbitrary keys through it), so the four shapes types.py
exports — and py.typed advertises to installed callers' checkers —
could not be passed to the one place they describe. pyright on a probe
that does exactly that: 9 errors before, 0 after. The constructor only
reads the slot (items(), then a fresh conf dict), so Mapping is the
honest bound; a plain dict is a Mapping, and TypedDict instances are
plain dicts at runtime, so nothing moves at runtime.

The _ARG_TYPES comment said "every value" is shape-checked; api_key is
not in the table (its empty check is separate, its type check stays
unchecked by ruling), so the comment now speaks for the table only.

_local_doc_scope and _require_local_scope still explained the cloud
drop as "scoping is server-side" — true of the managed chat, which
never reaches either function. What reaches them on a cloud client is
own-model chat and the config helpers, whose cloud tools take no
allowlist: targeting there is prompt-level only, as the error message
between them already said.

434 passed; pyright on pageindex/ unchanged at 235 (0 in the touched
files, before and after).

Claude-Session: https://claude.ai/code/session_01TxG8u8x29XRnK4yscZVCch
@rejojer
rejojer merged commit b9a9a3b into main Aug 26, 2026
14 checks passed
rejojer added a commit that referenced this pull request Aug 26, 2026
Two post-0.2.11 follow-up squashes (b9a9a3b, 174f95f) land on the review
branch the same way f6fa99b did: main's tree recorded as merged, history
kept, so PR #400 keeps spanning 0.2.9 → current main.

Claude-Session: https://claude.ai/code/session_01VQ6mruXZBgw9Hjii8KPbQP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant