mirror of
https://github.com/VectifyAI/PageIndex.git
synced 2026-10-02 07:44:37 +08:00
fix: the slots accept any Mapping at runtime; eight docstrings stop calling cloud tool scoping server-side (#429)
fix: the slots accept any Mapping at runtime, as their annotation admits; eight docstrings stop calling cloud tool scoping server-side4e9c56cwidened index=/chat= to Mapping[str, Any] so the exported TypedDicts pass a checker, but _resolve_index_slot/_resolve_chat_slot still dispatched on isinstance(..., dict): a MappingProxyType or ChainMap was pyright-clean and raised "must be a string or a dict" at construction. The resolvers now narrow on Mapping — the comprehension already copies, so a read-only proxy proves the caller's mapping is never mutated.4e9c56ccorrected three of eleven "scoping is server-side" sites; the remaining eight said the same untrue thing about doc_id on cloud (its tools carry no allowlist — targeting is prompt-level, as the runtime error already explains). Deleted rather than reworded. local_chat.py's module docstring predates own-model chat over the cloud bridge; storage_path's prose now names the PathLike5e2dc9btyped. Claude-Session: https://claude.ai/code/session_01VQ6mruXZBgw9Hjii8KPbQP
This commit is contained in:
@@ -1503,7 +1503,7 @@ def _tool_specs(client, include_management: bool = False, doc_ids=None,
|
||||
"""(name, description, schema, invoke) per tool, for adapters that take
|
||||
the wire schema verbatim. ``invoke`` returns (envelope_text, is_error).
|
||||
Schemas are copies (frameworks keep the dict by reference). ``doc_ids``
|
||||
is the local chat scope; cloud scoping is server-side."""
|
||||
is the local chat scope."""
|
||||
_require_local_scope(client, doc_ids)
|
||||
if getattr(client, "api_key", None):
|
||||
bridge = _cloud_bridge(client, gated=not include_management)
|
||||
|
||||
+12
-17
@@ -126,7 +126,7 @@ def _resolve_index_slot(index) -> "tuple[_CloudKey, dict[str, Any]]":
|
||||
raise PageIndexAPIError(
|
||||
"index is an empty string — pass a local index model name, "
|
||||
'or "cloud".')
|
||||
if isinstance(index, dict):
|
||||
if isinstance(index, Mapping):
|
||||
# None-valued keys mean "absent", exactly like the flat arguments.
|
||||
conf = {name: value for name, value in index.items()
|
||||
if value is not None}
|
||||
@@ -195,7 +195,7 @@ def _resolve_chat_slot(chat) -> "tuple[Optional[str], dict[str, Any]]":
|
||||
raise PageIndexAPIError(
|
||||
"chat is an empty string — pass a model name, or "
|
||||
'"cloud" for the managed chat.')
|
||||
if isinstance(chat, dict):
|
||||
if isinstance(chat, Mapping):
|
||||
# None-valued keys mean "absent", exactly like the flat arguments.
|
||||
conf = {name: value for name, value in chat.items()
|
||||
if value is not None}
|
||||
@@ -308,8 +308,9 @@ class PageIndexClient:
|
||||
``index_model`` covers this.
|
||||
retrieve_model (str, optional): Legacy name for ``chat_model`` —
|
||||
same meaning everywhere, cloud clients included.
|
||||
storage_path (str, optional): Local mode only — directory where
|
||||
indexed documents are stored. Defaults to ``./.pageindex``.
|
||||
storage_path (str or os.PathLike, optional): Local mode only —
|
||||
directory where indexed documents are stored. Defaults to
|
||||
``./.pageindex``.
|
||||
index_backend (dict, optional): Local mode only — connection
|
||||
overrides for the indexing lane's LLM calls. Keys are
|
||||
LiteLLM's own connection params — ``api_key``, ``api_base``,
|
||||
@@ -1201,8 +1202,7 @@ class PageIndexClient:
|
||||
the full ``/mcp`` list (upload, delete, ...).
|
||||
doc_id: Local only — restrict the tools to this document ID
|
||||
(or list of IDs), enforced at the tool layer: out-of-scope
|
||||
lookups return NOT_FOUND. Raises on cloud, where scoping
|
||||
is server-side.
|
||||
lookups return NOT_FOUND. Raises on cloud.
|
||||
"""
|
||||
from .agent_tools import build_agent_tools
|
||||
return build_agent_tools(self, include_management, doc_ids=doc_id)
|
||||
@@ -1245,8 +1245,7 @@ class PageIndexClient:
|
||||
for server-side tool execution (OpenAI models only).
|
||||
doc_id: Local only — restrict the tools to this document ID
|
||||
(or list of IDs), enforced at the tool layer: out-of-scope
|
||||
lookups return NOT_FOUND. Raises on cloud, where scoping
|
||||
is server-side.
|
||||
lookups return NOT_FOUND. Raises on cloud.
|
||||
"""
|
||||
from .integrations.openai_agents import build_openai_tools
|
||||
return build_openai_tools(self, include_management, hosted,
|
||||
@@ -1297,8 +1296,7 @@ class PageIndexClient:
|
||||
Args:
|
||||
doc_id: Document ID or list of IDs to target, as in
|
||||
``agent_instructions``. Local: also enforced at the tool
|
||||
layer, not just prompted. Cloud: prompt-level targeting
|
||||
(tool scoping is server-side).
|
||||
layer, not just prompted. Cloud: prompt-level targeting.
|
||||
include_management (bool): Also expose tools that modify the
|
||||
library.
|
||||
model: Backend model name; overrides the local default. Same
|
||||
@@ -1386,8 +1384,7 @@ class PageIndexClient:
|
||||
sync and async runners each accept only their own flavor.
|
||||
doc_id: Local only — restrict the tools to this document ID
|
||||
(or list of IDs), enforced at the tool layer: out-of-scope
|
||||
lookups return NOT_FOUND. Raises on cloud, where scoping
|
||||
is server-side.
|
||||
lookups return NOT_FOUND. Raises on cloud.
|
||||
"""
|
||||
from .integrations.anthropic_sdk import build_anthropic_tools
|
||||
return build_anthropic_tools(self, include_management, asynchronous,
|
||||
@@ -1428,8 +1425,7 @@ class PageIndexClient:
|
||||
default).
|
||||
doc_id: Document ID or list of IDs to target, as in
|
||||
``agent_instructions``. Local: also enforced at the tool
|
||||
layer, not just prompted. Cloud: prompt-level targeting
|
||||
(tool scoping is server-side).
|
||||
layer, not just prompted. Cloud: prompt-level targeting.
|
||||
include_management (bool): Also expose tools that modify the
|
||||
library.
|
||||
asynchronous (bool): Build async runnables for
|
||||
@@ -1475,7 +1471,7 @@ class PageIndexClient:
|
||||
same way at registration (requires ``claude-agent-sdk``;
|
||||
``pip install 'pageindex[claude]'``). ``doc_id`` (local only)
|
||||
restricts those tools to that document ID (or list), enforced at
|
||||
the tool layer; it raises on cloud, where scoping is server-side.
|
||||
the tool layer; it raises on cloud.
|
||||
``server_name`` names the in-process server — match it to the key
|
||||
you register the entry under (cloud entries carry no name).
|
||||
|
||||
@@ -1520,8 +1516,7 @@ class PageIndexClient:
|
||||
Args:
|
||||
doc_id: Document ID or list of IDs to target, as in
|
||||
``agent_instructions``. Local: also enforced at the tool
|
||||
layer, not just prompted. Cloud: prompt-level targeting
|
||||
(tool scoping is server-side).
|
||||
layer, not just prompted. Cloud: prompt-level targeting.
|
||||
include_management (bool): Also allow tools that modify the
|
||||
library.
|
||||
server_name (str): Key the server is registered under;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Managed local chat: document-QA agents over the local tools."""
|
||||
"""Own-model chat: document-QA agents over the local or cloud agent tools."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
@@ -289,6 +289,15 @@ def test_slot_flat_equivalence(tmp_path):
|
||||
assert getattr(flat, attr) == getattr(slot, attr), attr
|
||||
|
||||
|
||||
def test_slots_take_any_mapping():
|
||||
"""The slots are typed Mapping so the exported TypedDicts pass a
|
||||
checker; the resolvers must accept what the annotation admits — and
|
||||
read-only proxies prove they never mutate the caller's mapping."""
|
||||
client = PageIndexClient(index=types.MappingProxyType({"api_key": "pi-k"}),
|
||||
chat=types.MappingProxyType({"model": "m"}))
|
||||
assert (client.api_key, client.chat_model) == ("pi-k", "m")
|
||||
|
||||
|
||||
def test_same_side_double_spelling_rejected():
|
||||
for kwargs in ({"api_key": "k", "index": {"api_key": "k"}},
|
||||
{"index": "m", "index_model": "m"},
|
||||
|
||||
Reference in New Issue
Block a user