pageindex 0.2.10.dev3__tar.gz → 0.2.10.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/PKG-INFO +3 -7
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/README.md +1 -5
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/__init__.py +5 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/client.py +185 -43
- pageindex-0.2.10.dev5/pageindex/config.yaml +15 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/local_api.py +21 -8
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/local_chat.py +178 -74
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/page_index_classic.py +0 -1
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/utils.py +85 -11
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pyproject.toml +2 -2
- pageindex-0.2.10.dev3/pageindex/config.yaml +0 -13
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/LICENSE +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/_version.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/agent_tools.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/cloud_api.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/errors.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/README.md +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/api.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/blocks/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/blocks/build.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/blocks/join_rules.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/classification/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/classification/body_text.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/classification/header_footer.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/classification/keyword_tables.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/classification/toc_boilerplate.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/clustering/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/clustering/build.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/clustering/merge_rules.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/columns/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/columns/gutters.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/columns/splitting.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/data/boilerplate_phrases.json +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/data/dictionaries.json +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/data/glyph_name_table.json +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/data/normalized_unicodes.json +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/data/script_bucket_table.json +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/embedded_toc.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/candidates.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/detectors.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/keyword_tables.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/neighbors.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/page_scan.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/style_detectors.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/heading_detection/text_checks.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/labels/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/labels/caption_regions.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/labels/caption_text.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/main.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/model/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/model/block.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/model/char_stats.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/model/numbering.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/model/rects.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/model/span_line.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline/filtering.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline/tree.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline_assembly/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline_assembly/assembly.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline_assembly/candidates.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline_assembly/cliques.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline_assembly/selection.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/outline_assembly/style_context.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/char_extract.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/cmap_parse.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/code_walk.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/content_stream.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/font_unicode.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/geometry.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/glyph_tables.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/merge.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/pdf_objects.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/pipeline.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/remerge.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/text_normalize.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_charlevel/unicode_apply.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/parser_pdfium_parallel.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/phases/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/phases/line_numbers.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/phases/page_view.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/stats/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/stats/aggregates.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/stats/scripts.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/title/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/title/detect.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/title/dicts.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/title/scoring.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/tokens/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/tokens/hashing.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/tokens/token_types.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/tokens/tokenizer.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/flash/tokens/tries.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/integrations/__init__.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/integrations/anthropic_sdk.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/integrations/claude_agent_sdk.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/integrations/openai_agents.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/local_store.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/mcp_bridge.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/page_index_md.py +0 -0
- {pageindex-0.2.10.dev3 → pageindex-0.2.10.dev5}/pageindex/tree_optimize.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pageindex
|
|
3
|
-
Version: 0.2.10.
|
|
3
|
+
Version: 0.2.10.dev5
|
|
4
4
|
Summary: Python SDK for PageIndex — reasoning-based, vectorless document retrieval, cloud and local
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -24,7 +24,7 @@ Provides-Extra: openai
|
|
|
24
24
|
Requires-Dist: PyPDF2 (>=3.0.0)
|
|
25
25
|
Requires-Dist: anthropic (>=0.108.0) ; extra == "anthropic"
|
|
26
26
|
Requires-Dist: claude-agent-sdk (>=0.1.53) ; extra == "claude"
|
|
27
|
-
Requires-Dist: litellm (>=1.
|
|
27
|
+
Requires-Dist: litellm (>=1.97.0)
|
|
28
28
|
Requires-Dist: openai (>=1.70.0)
|
|
29
29
|
Requires-Dist: openai-agents (>=0.18.1)
|
|
30
30
|
Requires-Dist: pypdfium2 (>=4.30.0)
|
|
@@ -218,7 +218,7 @@ You can customize the processing with additional optional arguments (the structu
|
|
|
218
218
|
|
|
219
219
|
```
|
|
220
220
|
--mode Processing mode: flash (default) or standard
|
|
221
|
-
--model
|
|
221
|
+
--index-model LLM model used to index the document (default: gpt-5.6-luna)
|
|
222
222
|
--toc-check-pages Pages to check for table of contents (default: 20)
|
|
223
223
|
--max-pages-per-node Max pages per node (default: 10)
|
|
224
224
|
--max-tokens-per-node Max tokens per node (default: 20000)
|
|
@@ -254,10 +254,6 @@ python3 run_pageindex.py --md_path /path/to/your/document.md
|
|
|
254
254
|
For a simple, end-to-end **agentic vectorless RAG** example using **self-hosted PageIndex** (with OpenAI Agents SDK), see [`examples/agentic_vectorless_rag_demo.py`](examples/agentic_vectorless_rag_demo.py).
|
|
255
255
|
|
|
256
256
|
```bash
|
|
257
|
-
# Install optional dependency
|
|
258
|
-
pip3 install openai-agents
|
|
259
|
-
|
|
260
|
-
# Run the demo
|
|
261
257
|
python3 examples/agentic_vectorless_rag_demo.py
|
|
262
258
|
```
|
|
263
259
|
|
|
@@ -177,7 +177,7 @@ You can customize the processing with additional optional arguments (the structu
|
|
|
177
177
|
|
|
178
178
|
```
|
|
179
179
|
--mode Processing mode: flash (default) or standard
|
|
180
|
-
--model
|
|
180
|
+
--index-model LLM model used to index the document (default: gpt-5.6-luna)
|
|
181
181
|
--toc-check-pages Pages to check for table of contents (default: 20)
|
|
182
182
|
--max-pages-per-node Max pages per node (default: 10)
|
|
183
183
|
--max-tokens-per-node Max tokens per node (default: 20000)
|
|
@@ -213,10 +213,6 @@ python3 run_pageindex.py --md_path /path/to/your/document.md
|
|
|
213
213
|
For a simple, end-to-end **agentic vectorless RAG** example using **self-hosted PageIndex** (with OpenAI Agents SDK), see [`examples/agentic_vectorless_rag_demo.py`](examples/agentic_vectorless_rag_demo.py).
|
|
214
214
|
|
|
215
215
|
```bash
|
|
216
|
-
# Install optional dependency
|
|
217
|
-
pip3 install openai-agents
|
|
218
|
-
|
|
219
|
-
# Run the demo
|
|
220
216
|
python3 examples/agentic_vectorless_rag_demo.py
|
|
221
217
|
```
|
|
222
218
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"""PageIndex SDK."""
|
|
2
|
+
import os as _os
|
|
2
3
|
from typing import TYPE_CHECKING as _TYPE_CHECKING
|
|
3
4
|
|
|
5
|
+
# LiteLLM's import otherwise fetches its model map over the network — seconds
|
|
6
|
+
# of blocking (or a hang offline). setdefault, so an explicit user choice wins.
|
|
7
|
+
_os.environ.setdefault("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
|
8
|
+
|
|
4
9
|
from .client import PageIndexClient, PageIndexCloudClient, PageIndexLocalClient
|
|
5
10
|
from .errors import PageIndexAPIError
|
|
6
11
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
+
import threading
|
|
5
6
|
import time
|
|
6
7
|
import warnings
|
|
7
8
|
from typing import Any, Callable, Iterator, Optional, Union, cast
|
|
@@ -9,6 +10,13 @@ from typing import Any, Callable, Iterator, Optional, Union, cast
|
|
|
9
10
|
from .errors import PageIndexAPIError
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
def _preload_litellm() -> None:
|
|
14
|
+
try:
|
|
15
|
+
import litellm # noqa: F401
|
|
16
|
+
except Exception:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
12
20
|
def _parse_pages(pages: str) -> list[int]:
|
|
13
21
|
result: set[int] = set()
|
|
14
22
|
too_many = (f"Page specification '{pages}' spans more than "
|
|
@@ -33,7 +41,7 @@ def _parse_pages(pages: str) -> list[int]:
|
|
|
33
41
|
return sorted(result)
|
|
34
42
|
|
|
35
43
|
|
|
36
|
-
def
|
|
44
|
+
def _agents_sdk_model_name(model: str) -> str:
|
|
37
45
|
"""Preserve supported Agents SDK prefixes and route other provider paths via LiteLLM."""
|
|
38
46
|
passthrough_prefixes = ("litellm/", "openai/")
|
|
39
47
|
if not model or "/" not in model:
|
|
@@ -56,18 +64,38 @@ class PageIndexClient:
|
|
|
56
64
|
Args:
|
|
57
65
|
api_key (str, optional): PageIndex cloud API key
|
|
58
66
|
(https://dash.pageindex.ai/api-keys). Omit for local mode.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
index_model (str, optional): Local mode only — LLM used to index
|
|
68
|
+
documents (structure and summaries). Defaults to the SDK
|
|
69
|
+
default (fast and cheap).
|
|
70
|
+
chat_model (str, optional): Local mode only — the model the chat
|
|
71
|
+
surfaces (``chat``, ``chat_completions``, ``responses``)
|
|
72
|
+
default to, exposed as ``client.chat_model``. Chat names
|
|
73
|
+
route through LiteLLM and mean what LiteLLM says they mean;
|
|
74
|
+
bare names are OpenAI-compatible shorthand, and
|
|
75
|
+
``openai/Qwen/...`` is the form for an OpenAI-compatible
|
|
76
|
+
server that itself serves slashed model ids (vLLM, TGI).
|
|
77
|
+
Defaults to the SDK default (strong).
|
|
78
|
+
model (str, optional): Local mode only — one model for both roles:
|
|
79
|
+
sets the default for ``index_model`` and ``chat_model`` at
|
|
80
|
+
once. The role-specific arguments win over it. (Also the
|
|
81
|
+
0.2.8-era name for the indexing model — old configs keep
|
|
82
|
+
working unchanged.)
|
|
83
|
+
summary_model (str, optional): Local mode only — legacy: overrides
|
|
84
|
+
the model used for node summaries and document descriptions;
|
|
85
|
+
``index_model`` covers this.
|
|
86
|
+
retrieve_model (str, optional): Local mode only — legacy name for
|
|
87
|
+
``chat_model``.
|
|
69
88
|
storage_path (str, optional): Local mode only — directory where
|
|
70
89
|
indexed documents are stored. Defaults to ``./.pageindex``.
|
|
90
|
+
index_backend (dict, optional): Local mode only — connection
|
|
91
|
+
overrides for the indexing lane's LLM calls. Keys are
|
|
92
|
+
LiteLLM's own connection params — ``api_key``, ``api_base``,
|
|
93
|
+
``api_version``, ``aws_*``, … — passed through verbatim.
|
|
94
|
+
chat_backend (dict, optional): Local mode only — default
|
|
95
|
+
connection overrides for the chat surfaces; a call's own
|
|
96
|
+
``backend`` keys win over it. The dict reaches whichever
|
|
97
|
+
door runs, in that door's vocabulary (see each method) —
|
|
98
|
+
``api_key`` / ``base_url`` mean the same thing on all three.
|
|
71
99
|
|
|
72
100
|
Usage:
|
|
73
101
|
client = PageIndexClient(api_key="...") # cloud
|
|
@@ -88,19 +116,27 @@ class PageIndexClient:
|
|
|
88
116
|
self,
|
|
89
117
|
api_key: Optional[str] = None,
|
|
90
118
|
*,
|
|
119
|
+
index_model: Optional[str] = None,
|
|
120
|
+
chat_model: Optional[str] = None,
|
|
91
121
|
model: Optional[str] = None,
|
|
92
122
|
summary_model: Optional[str] = None,
|
|
93
123
|
retrieve_model: Optional[str] = None,
|
|
94
124
|
storage_path: Optional[str] = None,
|
|
125
|
+
index_backend: Optional[dict[str, Any]] = None,
|
|
126
|
+
chat_backend: Optional[dict[str, Any]] = None,
|
|
95
127
|
):
|
|
96
128
|
if api_key == "":
|
|
97
129
|
raise PageIndexAPIError(
|
|
98
130
|
"api_key is an empty string. Pass a real PageIndex API key for "
|
|
99
131
|
"cloud mode, or omit api_key entirely for local mode."
|
|
100
132
|
)
|
|
133
|
+
model_args = {"index_model": index_model, "chat_model": chat_model,
|
|
134
|
+
"model": model, "summary_model": summary_model,
|
|
135
|
+
"retrieve_model": retrieve_model}
|
|
101
136
|
if api_key is not None:
|
|
102
|
-
local_only =
|
|
103
|
-
|
|
137
|
+
local_only = dict(model_args, storage_path=storage_path,
|
|
138
|
+
index_backend=index_backend,
|
|
139
|
+
chat_backend=chat_backend)
|
|
104
140
|
passed = [name for name, value in local_only.items() if value is not None]
|
|
105
141
|
if passed:
|
|
106
142
|
raise PageIndexAPIError(
|
|
@@ -113,23 +149,31 @@ class PageIndexClient:
|
|
|
113
149
|
self._api = CloudAPI(self)
|
|
114
150
|
else:
|
|
115
151
|
from .utils import ConfigLoader
|
|
116
|
-
overrides = {key: value for key, value in
|
|
117
|
-
{"model": model, "summary_model": summary_model,
|
|
118
|
-
"retrieve_model": retrieve_model}.items()
|
|
152
|
+
overrides = {key: value for key, value in model_args.items()
|
|
119
153
|
if value}
|
|
120
154
|
opt = ConfigLoader().load(overrides or None)
|
|
121
155
|
self.model = opt.model
|
|
122
|
-
self.
|
|
123
|
-
self.
|
|
124
|
-
|
|
156
|
+
self.index_model = opt.index_model
|
|
157
|
+
self.summary_model = opt.summary_model
|
|
158
|
+
self.chat_model = _agents_sdk_model_name(opt.chat_model)
|
|
159
|
+
self.chat_backend = chat_backend
|
|
125
160
|
self.storage_path = storage_path or ".pageindex"
|
|
126
161
|
from .local_api import LocalAPI
|
|
127
162
|
self._api = LocalAPI(
|
|
128
163
|
storage_path=self.storage_path,
|
|
129
164
|
model=self.model,
|
|
130
165
|
summary_model=self.summary_model,
|
|
131
|
-
retrieve_model=self.
|
|
166
|
+
retrieve_model=self.chat_model,
|
|
167
|
+
index_backend=index_backend,
|
|
132
168
|
)
|
|
169
|
+
# LiteLLM's multi-second import would otherwise land on the
|
|
170
|
+
# first chat call; failures resurface there with real context.
|
|
171
|
+
threading.Thread(target=_preload_litellm, daemon=True).start()
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def retrieve_model(self):
|
|
175
|
+
"""Legacy name for ``chat_model``."""
|
|
176
|
+
return self.chat_model
|
|
133
177
|
|
|
134
178
|
# ---------- DOCUMENT SUBMISSION ----------
|
|
135
179
|
|
|
@@ -353,6 +397,7 @@ class PageIndexClient:
|
|
|
353
397
|
doc_id: Optional[Union[str, list[str]]] = None,
|
|
354
398
|
stream: bool = False,
|
|
355
399
|
model: Optional[str] = None,
|
|
400
|
+
reasoning_effort: Optional[str] = None,
|
|
356
401
|
) -> Union[str, Iterator[str]]:
|
|
357
402
|
"""
|
|
358
403
|
Ask a question about your documents, get the answer.
|
|
@@ -371,14 +416,19 @@ class PageIndexClient:
|
|
|
371
416
|
Keep it identical across a conversation's calls.
|
|
372
417
|
stream: Yield the answer as text chunks as it is produced.
|
|
373
418
|
model: Local only — backend model name (defaults to
|
|
374
|
-
``
|
|
419
|
+
``chat_model``).
|
|
420
|
+
reasoning_effort: Local only — how hard the model thinks
|
|
421
|
+
(``"low"`` / ``"medium"`` / ``"high"``; what a backend
|
|
422
|
+
accepts is its own). Unset sends nothing — the model's
|
|
423
|
+
default behavior applies.
|
|
375
424
|
|
|
376
425
|
Returns:
|
|
377
426
|
- stream=False: the answer string
|
|
378
427
|
- stream=True: iterator of text chunks
|
|
379
428
|
"""
|
|
380
429
|
result = self.chat_completions(messages, stream=stream,
|
|
381
|
-
doc_id=doc_id, model=model
|
|
430
|
+
doc_id=doc_id, model=model,
|
|
431
|
+
reasoning_effort=reasoning_effort)
|
|
382
432
|
if stream:
|
|
383
433
|
return cast(Iterator[str], result)
|
|
384
434
|
envelope = cast(dict[str, Any], result)
|
|
@@ -394,20 +444,26 @@ class PageIndexClient:
|
|
|
394
444
|
enable_citations: bool = False,
|
|
395
445
|
model: Optional[str] = None,
|
|
396
446
|
max_turns: Optional[int] = None,
|
|
447
|
+
top_p: Optional[float] = None,
|
|
448
|
+
max_tokens: Optional[int] = None,
|
|
449
|
+
reasoning_effort: Optional[str] = None,
|
|
450
|
+
extra_body: Optional[dict[str, Any]] = None,
|
|
451
|
+
extra_headers: Optional[dict[str, str]] = None,
|
|
452
|
+
backend: Optional[dict[str, Any]] = None,
|
|
397
453
|
) -> Union[dict[str, Any], Iterator[str], Iterator[dict[str, Any]]]:
|
|
398
454
|
"""
|
|
399
455
|
PageIndex Chat Completions: document QA in one call.
|
|
400
456
|
|
|
401
457
|
Cloud: the hosted chat endpoint. Local: a managed document-QA agent
|
|
402
|
-
run over the local tools against your own LLM backend
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
when the
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
prefix cache-marked automatically
|
|
458
|
+
run over the local tools against your own LLM backend, routed
|
|
459
|
+
through LiteLLM — model names mean what LiteLLM says they mean.
|
|
460
|
+
Bare names are OpenAI-compatible shorthand (the OpenAI SDK's usual
|
|
461
|
+
env config — OPENAI_API_KEY, OPENAI_BASE_URL — selects the
|
|
462
|
+
backend, so any OpenAI-compatible server works; write
|
|
463
|
+
``openai/Qwen/...`` when the server itself serves slashed ids),
|
|
464
|
+
provider-prefixed names — ``anthropic/…``, ``bedrock/…`` — reach
|
|
465
|
+
that provider, and LiteLLM-routed Claude models get the managed
|
|
466
|
+
prompt prefix cache-marked automatically. The non-stream
|
|
411
467
|
response carries the final answer only; streaming yields the
|
|
412
468
|
agent's visible text as it is produced, including narration before
|
|
413
469
|
tool calls. ``finish_reason`` reports loop completion ("stop") —
|
|
@@ -434,8 +490,34 @@ class PageIndexClient:
|
|
|
434
490
|
enable_citations: Cloud-only — local mode raises (citations need
|
|
435
491
|
block-level OCR data local mode does not store).
|
|
436
492
|
model: Local only — backend model name (defaults to
|
|
437
|
-
``
|
|
493
|
+
``chat_model``). The cloud endpoint selects its own.
|
|
438
494
|
max_turns: Local only — cap on agent turns per call.
|
|
495
|
+
top_p: Local only — nucleus sampling, passed through to the
|
|
496
|
+
model.
|
|
497
|
+
max_tokens: Local only — per-call output cap, passed through;
|
|
498
|
+
it bounds each backend call in the agent loop (the way
|
|
499
|
+
max_turns bounds the loop), not the whole run.
|
|
500
|
+
reasoning_effort: Local only — passed through verbatim as
|
|
501
|
+
LiteLLM's ``reasoning_effort``; each provider maps it to
|
|
502
|
+
its own thinking control, and the values mean what the
|
|
503
|
+
backend says they mean. Unset sends nothing (the
|
|
504
|
+
backend's default applies).
|
|
505
|
+
extra_body: Local only — extra request fields beyond this
|
|
506
|
+
method's parameters, merged last so they win.
|
|
507
|
+
OpenAI-compatible backends take them verbatim in the
|
|
508
|
+
request body; LiteLLM-routed providers take them as
|
|
509
|
+
LiteLLM's own params (mapped or refused per provider).
|
|
510
|
+
Credentials belong in ``backend``, never here.
|
|
511
|
+
extra_headers: Local only — extra HTTP headers merged into
|
|
512
|
+
each backend request; caller headers win. One exception:
|
|
513
|
+
LiteLLM's anthropic adapter owns the ``anthropic-beta``
|
|
514
|
+
header (your value is dropped there) — use ``messages()``
|
|
515
|
+
for Anthropic beta flags.
|
|
516
|
+
backend: Local only — connection overrides for this call's
|
|
517
|
+
backend, merged over the client's ``chat_backend``
|
|
518
|
+
(per-call keys win). Keys are LiteLLM's own connection
|
|
519
|
+
params — ``api_key``, ``base_url``, ``api_version``,
|
|
520
|
+
``aws_*``, … — passed through verbatim.
|
|
439
521
|
|
|
440
522
|
Returns:
|
|
441
523
|
- stream=False: complete response dict ({'id', 'object', 'created',
|
|
@@ -456,12 +538,18 @@ class PageIndexClient:
|
|
|
456
538
|
self, messages, stream=stream, doc_id=doc_id,
|
|
457
539
|
temperature=temperature, stream_metadata=stream_metadata,
|
|
458
540
|
enable_citations=enable_citations, model=model,
|
|
459
|
-
max_turns=max_turns,
|
|
541
|
+
max_turns=max_turns, top_p=top_p, max_tokens=max_tokens,
|
|
542
|
+
reasoning_effort=reasoning_effort, extra_body=extra_body,
|
|
543
|
+
extra_headers=extra_headers, backend=backend,
|
|
460
544
|
)
|
|
461
|
-
if model is not None or max_turns is not None
|
|
545
|
+
if (model is not None or max_turns is not None or top_p is not None
|
|
546
|
+
or max_tokens is not None or reasoning_effort is not None
|
|
547
|
+
or extra_body is not None or extra_headers is not None
|
|
548
|
+
or backend is not None):
|
|
462
549
|
raise PageIndexAPIError(
|
|
463
|
-
"model
|
|
464
|
-
"
|
|
550
|
+
"model, max_turns, top_p, max_tokens, reasoning_effort, "
|
|
551
|
+
"extra_body, extra_headers and backend are local-mode "
|
|
552
|
+
"parameters — the cloud chat endpoint selects its own model."
|
|
465
553
|
)
|
|
466
554
|
return self._api.chat_completions(
|
|
467
555
|
messages=messages, stream=stream, doc_id=doc_id,
|
|
@@ -479,6 +567,11 @@ class PageIndexClient:
|
|
|
479
567
|
temperature: Optional[float] = None,
|
|
480
568
|
top_p: Optional[float] = None,
|
|
481
569
|
max_turns: Optional[int] = None,
|
|
570
|
+
max_output_tokens: Optional[int] = None,
|
|
571
|
+
reasoning: Optional[dict[str, Any]] = None,
|
|
572
|
+
extra_body: Optional[dict[str, Any]] = None,
|
|
573
|
+
extra_headers: Optional[dict[str, str]] = None,
|
|
574
|
+
backend: Optional[dict[str, Any]] = None,
|
|
482
575
|
) -> Union[dict[str, Any], Iterator[dict[str, Any]]]:
|
|
483
576
|
"""
|
|
484
577
|
Document QA over the OpenAI Responses protocol — the agentic surface.
|
|
@@ -502,7 +595,7 @@ class PageIndexClient:
|
|
|
502
595
|
Args:
|
|
503
596
|
input: A user message string, or a list of Responses input items
|
|
504
597
|
(round-trip prior ``items`` here).
|
|
505
|
-
model: Backend model name (defaults to ``
|
|
598
|
+
model: Backend model name (defaults to ``chat_model``).
|
|
506
599
|
stream: Yield Responses stream events as dicts — one logical
|
|
507
600
|
response per call: per-turn backend lifecycle events are
|
|
508
601
|
collapsed, sequence numbers are reassigned monotonically,
|
|
@@ -517,6 +610,25 @@ class PageIndexClient:
|
|
|
517
610
|
instructions: Appended to the managed system prompt.
|
|
518
611
|
temperature / top_p: Passed through to the model.
|
|
519
612
|
max_turns: Cap on agent turns per call.
|
|
613
|
+
max_output_tokens: Per-call output cap, passed through; it
|
|
614
|
+
bounds each backend call in the agent loop (the way
|
|
615
|
+
max_turns bounds the loop), not the whole run. Echoed in
|
|
616
|
+
the envelope.
|
|
617
|
+
reasoning: Responses reasoning options, forwarded verbatim
|
|
618
|
+
(e.g. ``{"effort": "low", "summary": "auto"}``) — the
|
|
619
|
+
values mean what the backend says they mean. Unset sends
|
|
620
|
+
nothing (the backend's default applies).
|
|
621
|
+
extra_body: Extra request fields beyond this method's
|
|
622
|
+
parameters, merged verbatim into the request body (last,
|
|
623
|
+
so they win). Credentials belong in ``backend``, never
|
|
624
|
+
here.
|
|
625
|
+
extra_headers: Extra HTTP headers merged into each request;
|
|
626
|
+
caller headers win over defaults.
|
|
627
|
+
backend: Connection overrides for this call's backend client,
|
|
628
|
+
merged over the client's ``chat_backend`` (per-call keys
|
|
629
|
+
win). Keys are the openai SDK's client params —
|
|
630
|
+
``api_key``, ``base_url``, ``organization``, … — passed
|
|
631
|
+
verbatim; unknown keys raise.
|
|
520
632
|
"""
|
|
521
633
|
from .cloud_api import CloudAPI
|
|
522
634
|
if isinstance(self._api, CloudAPI):
|
|
@@ -528,7 +640,9 @@ class PageIndexClient:
|
|
|
528
640
|
return run_responses(
|
|
529
641
|
self, input, model=model, stream=stream, doc_id=doc_id,
|
|
530
642
|
instructions=instructions, temperature=temperature, top_p=top_p,
|
|
531
|
-
max_turns=max_turns,
|
|
643
|
+
max_turns=max_turns, max_output_tokens=max_output_tokens,
|
|
644
|
+
reasoning=reasoning, extra_body=extra_body,
|
|
645
|
+
extra_headers=extra_headers, backend=backend,
|
|
532
646
|
)
|
|
533
647
|
|
|
534
648
|
def messages(
|
|
@@ -544,6 +658,10 @@ class PageIndexClient:
|
|
|
544
658
|
top_k: Optional[int] = None,
|
|
545
659
|
stop_sequences: Optional[list[str]] = None,
|
|
546
660
|
max_turns: Optional[int] = None,
|
|
661
|
+
thinking: Optional[dict[str, Any]] = None,
|
|
662
|
+
extra_body: Optional[dict[str, Any]] = None,
|
|
663
|
+
extra_headers: Optional[dict[str, str]] = None,
|
|
664
|
+
backend: Optional[dict[str, Any]] = None,
|
|
547
665
|
) -> Union[dict[str, Any], Iterator[Any]]:
|
|
548
666
|
"""
|
|
549
667
|
Document QA over the Anthropic Messages protocol — Claude-native.
|
|
@@ -578,6 +696,20 @@ class PageIndexClient:
|
|
|
578
696
|
OpenAI surfaces). A truncated run reports
|
|
579
697
|
``stop_reason: "tool_use"`` and its ``messages`` remain
|
|
580
698
|
valid for continuation.
|
|
699
|
+
thinking: Anthropic thinking configuration, forwarded verbatim
|
|
700
|
+
(e.g. ``{"type": "adaptive"}``) — the values and their
|
|
701
|
+
constraints are the backend's. Unset sends nothing.
|
|
702
|
+
extra_body: Extra request fields beyond this method's
|
|
703
|
+
parameters, merged verbatim into each request body.
|
|
704
|
+
Credentials belong in ``backend``, never here.
|
|
705
|
+
extra_headers: Extra HTTP headers merged into each request
|
|
706
|
+
(e.g. ``anthropic-beta`` feature flags); caller headers
|
|
707
|
+
win over defaults.
|
|
708
|
+
backend: Connection overrides for this call's backend client,
|
|
709
|
+
merged over the client's ``chat_backend`` (per-call keys
|
|
710
|
+
win). Keys are the anthropic SDK's client params —
|
|
711
|
+
``api_key``, ``base_url``, ``auth_token``, … — passed
|
|
712
|
+
verbatim; unknown keys raise.
|
|
581
713
|
"""
|
|
582
714
|
from .cloud_api import CloudAPI
|
|
583
715
|
if isinstance(self._api, CloudAPI):
|
|
@@ -591,6 +723,8 @@ class PageIndexClient:
|
|
|
591
723
|
stream=stream, doc_id=doc_id, system=system,
|
|
592
724
|
temperature=temperature, top_p=top_p, top_k=top_k,
|
|
593
725
|
stop_sequences=stop_sequences, max_turns=max_turns,
|
|
726
|
+
thinking=thinking, extra_body=extra_body,
|
|
727
|
+
extra_headers=extra_headers, backend=backend,
|
|
594
728
|
)
|
|
595
729
|
|
|
596
730
|
# ---------- DOCUMENT MANAGEMENT ----------
|
|
@@ -742,9 +876,11 @@ class PageIndexClient:
|
|
|
742
876
|
Sugar over the explicit form — ``agent_instructions`` (with
|
|
743
877
|
``doc_id`` targeting) as the instructions and
|
|
744
878
|
``as_openai_tools`` as the tools; local clients also carry their
|
|
745
|
-
configured ``
|
|
879
|
+
configured ``chat_model`` (cloud omits ``model`` so the
|
|
746
880
|
framework default applies). To customize further, switch to
|
|
747
|
-
those methods directly.
|
|
881
|
+
those methods directly. You run this config in your own
|
|
882
|
+
environment, so its model auth comes from there —
|
|
883
|
+
``chat_backend`` does not travel with it.
|
|
748
884
|
|
|
749
885
|
Args:
|
|
750
886
|
doc_id: Document ID or list of IDs to target, as in
|
|
@@ -763,7 +899,7 @@ class PageIndexClient:
|
|
|
763
899
|
scoped=scope is not None),
|
|
764
900
|
"tools": self.as_openai_tools(include_management, doc_id=scope),
|
|
765
901
|
}
|
|
766
|
-
model = model or getattr(self, "
|
|
902
|
+
model = model or getattr(self, "chat_model", None)
|
|
767
903
|
if model:
|
|
768
904
|
config["model"] = model
|
|
769
905
|
return config
|
|
@@ -1020,10 +1156,16 @@ class PageIndexLocalClient(PageIndexClient):
|
|
|
1020
1156
|
def __init__(
|
|
1021
1157
|
self,
|
|
1022
1158
|
*,
|
|
1159
|
+
index_model: Optional[str] = None,
|
|
1160
|
+
chat_model: Optional[str] = None,
|
|
1023
1161
|
model: Optional[str] = None,
|
|
1024
1162
|
summary_model: Optional[str] = None,
|
|
1025
1163
|
retrieve_model: Optional[str] = None,
|
|
1026
1164
|
storage_path: Optional[str] = None,
|
|
1165
|
+
index_backend: Optional[dict[str, Any]] = None,
|
|
1166
|
+
chat_backend: Optional[dict[str, Any]] = None,
|
|
1027
1167
|
):
|
|
1028
|
-
super().__init__(None,
|
|
1029
|
-
|
|
1168
|
+
super().__init__(None, index_model=index_model, chat_model=chat_model,
|
|
1169
|
+
model=model, summary_model=summary_model,
|
|
1170
|
+
retrieve_model=retrieve_model, storage_path=storage_path,
|
|
1171
|
+
index_backend=index_backend, chat_backend=chat_backend)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Models — index_model indexes documents (structure and summaries),
|
|
2
|
+
# chat_model answers questions on the chat surfaces; set model to use one
|
|
3
|
+
# for both. Unset keys use the SDK defaults shown below. Legacy keys
|
|
4
|
+
# (model, summary_model, retrieve_model) keep working.
|
|
5
|
+
# Indexing names without a provider prefix use the OpenAI SDK directly;
|
|
6
|
+
# for other providers, use "provider/model" (e.g. "anthropic/claude-sonnet-4-6").
|
|
7
|
+
# index_model: "gpt-5.6-luna"
|
|
8
|
+
# chat_model: "gpt-5.6-sol"
|
|
9
|
+
toc_check_page_num: 20
|
|
10
|
+
max_page_num_each_node: 10
|
|
11
|
+
max_token_num_each_node: 20000
|
|
12
|
+
if_add_node_id: "yes"
|
|
13
|
+
if_add_node_summary: "yes"
|
|
14
|
+
if_add_doc_description: "no"
|
|
15
|
+
if_add_node_text: "no"
|
|
@@ -35,14 +35,25 @@ class LocalAPI:
|
|
|
35
35
|
"""Backs PageIndexClient's local mode. One instance per client."""
|
|
36
36
|
|
|
37
37
|
def __init__(self, storage_path: str, model: str, summary_model: str,
|
|
38
|
-
retrieve_model: str):
|
|
38
|
+
retrieve_model: str, index_backend: dict | None = None):
|
|
39
39
|
self._store = DocStore(storage_path)
|
|
40
40
|
self._model = model
|
|
41
41
|
self._summary_model = summary_model
|
|
42
42
|
self._retrieve_model = retrieve_model
|
|
43
|
+
self._index_backend = index_backend
|
|
43
44
|
from .utils import ConfigLoader
|
|
44
45
|
self._config_loader = ConfigLoader()
|
|
45
46
|
|
|
47
|
+
def _with_backend(self, func, *args):
|
|
48
|
+
"""Scope the indexing lane's connection overrides around one
|
|
49
|
+
operation — runs inside whatever thread _run_indexer picked."""
|
|
50
|
+
from .utils import _llm_backend
|
|
51
|
+
token = _llm_backend.set(self._index_backend)
|
|
52
|
+
try:
|
|
53
|
+
return func(*args)
|
|
54
|
+
finally:
|
|
55
|
+
_llm_backend.reset(token)
|
|
56
|
+
|
|
46
57
|
# ── indexing ──
|
|
47
58
|
|
|
48
59
|
def submit_document(
|
|
@@ -104,11 +115,12 @@ class LocalAPI:
|
|
|
104
115
|
try:
|
|
105
116
|
if mode == "flash":
|
|
106
117
|
structure, description = _run_indexer(
|
|
107
|
-
self._index_flash, file_path, page_texts
|
|
118
|
+
self._with_backend, self._index_flash, file_path, page_texts
|
|
108
119
|
)
|
|
109
120
|
else:
|
|
110
121
|
structure, description = _run_indexer(
|
|
111
|
-
self._index_standard, file_path,
|
|
122
|
+
self._with_backend, self._index_standard, file_path,
|
|
123
|
+
page_texts
|
|
112
124
|
)
|
|
113
125
|
except PageIndexAPIError:
|
|
114
126
|
raise
|
|
@@ -183,11 +195,12 @@ class LocalAPI:
|
|
|
183
195
|
from .utils import (add_node_text, create_clean_structure_for_description,
|
|
184
196
|
generate_doc_description, write_node_id)
|
|
185
197
|
import litellm
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
198
|
+
if not self._index_backend:
|
|
199
|
+
env = litellm.validate_environment(self._summary_model)
|
|
200
|
+
if not env["keys_in_environment"]:
|
|
201
|
+
raise PageIndexAPIError(
|
|
202
|
+
f"Failed to submit document: missing API key for "
|
|
203
|
+
f"{self._summary_model}: {', '.join(env['missing_keys'])}")
|
|
191
204
|
result = page_index_flash(file_path, summary=True,
|
|
192
205
|
summary_model=self._summary_model,
|
|
193
206
|
optimize="full",
|