pageindex 0.2.10.dev2__tar.gz → 0.2.10.dev4__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.
Files changed (104) hide show
  1. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/PKG-INFO +4 -8
  2. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/README.md +1 -5
  3. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/__init__.py +5 -0
  4. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/client.py +127 -44
  5. pageindex-0.2.10.dev4/pageindex/config.yaml +15 -0
  6. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/integrations/openai_agents.py +1 -1
  7. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/local_chat.py +137 -70
  8. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/utils.py +46 -1
  9. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pyproject.toml +6 -5
  10. pageindex-0.2.10.dev2/pageindex/config.yaml +0 -13
  11. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/LICENSE +0 -0
  12. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/_version.py +0 -0
  13. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/agent_tools.py +0 -0
  14. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/cloud_api.py +0 -0
  15. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/errors.py +0 -0
  16. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/README.md +0 -0
  17. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/__init__.py +0 -0
  18. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/api.py +0 -0
  19. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/blocks/__init__.py +0 -0
  20. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/blocks/build.py +0 -0
  21. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/blocks/join_rules.py +0 -0
  22. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/classification/__init__.py +0 -0
  23. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/classification/body_text.py +0 -0
  24. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/classification/header_footer.py +0 -0
  25. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/classification/keyword_tables.py +0 -0
  26. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/classification/toc_boilerplate.py +0 -0
  27. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/clustering/__init__.py +0 -0
  28. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/clustering/build.py +0 -0
  29. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/clustering/merge_rules.py +0 -0
  30. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/columns/__init__.py +0 -0
  31. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/columns/gutters.py +0 -0
  32. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/columns/splitting.py +0 -0
  33. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/data/boilerplate_phrases.json +0 -0
  34. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/data/dictionaries.json +0 -0
  35. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/data/glyph_name_table.json +0 -0
  36. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/data/normalized_unicodes.json +0 -0
  37. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/data/script_bucket_table.json +0 -0
  38. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/embedded_toc.py +0 -0
  39. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/__init__.py +0 -0
  40. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/candidates.py +0 -0
  41. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/detectors.py +0 -0
  42. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/keyword_tables.py +0 -0
  43. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/neighbors.py +0 -0
  44. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/page_scan.py +0 -0
  45. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/style_detectors.py +0 -0
  46. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/heading_detection/text_checks.py +0 -0
  47. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/labels/__init__.py +0 -0
  48. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/labels/caption_regions.py +0 -0
  49. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/labels/caption_text.py +0 -0
  50. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/main.py +0 -0
  51. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/model/__init__.py +0 -0
  52. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/model/block.py +0 -0
  53. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/model/char_stats.py +0 -0
  54. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/model/numbering.py +0 -0
  55. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/model/rects.py +0 -0
  56. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/model/span_line.py +0 -0
  57. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline/__init__.py +0 -0
  58. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline/filtering.py +0 -0
  59. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline/tree.py +0 -0
  60. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline_assembly/__init__.py +0 -0
  61. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline_assembly/assembly.py +0 -0
  62. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline_assembly/candidates.py +0 -0
  63. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline_assembly/cliques.py +0 -0
  64. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline_assembly/selection.py +0 -0
  65. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/outline_assembly/style_context.py +0 -0
  66. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/__init__.py +0 -0
  67. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/char_extract.py +0 -0
  68. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/cmap_parse.py +0 -0
  69. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/code_walk.py +0 -0
  70. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/content_stream.py +0 -0
  71. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/font_unicode.py +0 -0
  72. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/geometry.py +0 -0
  73. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/glyph_tables.py +0 -0
  74. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/merge.py +0 -0
  75. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/pdf_objects.py +0 -0
  76. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/pipeline.py +0 -0
  77. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/remerge.py +0 -0
  78. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/text_normalize.py +0 -0
  79. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_charlevel/unicode_apply.py +0 -0
  80. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/parser_pdfium_parallel.py +0 -0
  81. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/phases/__init__.py +0 -0
  82. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/phases/line_numbers.py +0 -0
  83. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/phases/page_view.py +0 -0
  84. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/stats/__init__.py +0 -0
  85. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/stats/aggregates.py +0 -0
  86. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/stats/scripts.py +0 -0
  87. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/title/__init__.py +0 -0
  88. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/title/detect.py +0 -0
  89. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/title/dicts.py +0 -0
  90. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/title/scoring.py +0 -0
  91. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/tokens/__init__.py +0 -0
  92. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/tokens/hashing.py +0 -0
  93. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/tokens/token_types.py +0 -0
  94. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/tokens/tokenizer.py +0 -0
  95. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/flash/tokens/tries.py +0 -0
  96. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/integrations/__init__.py +0 -0
  97. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/integrations/anthropic_sdk.py +0 -0
  98. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/integrations/claude_agent_sdk.py +0 -0
  99. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/local_api.py +0 -0
  100. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/local_store.py +0 -0
  101. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/mcp_bridge.py +0 -0
  102. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/page_index_classic.py +0 -0
  103. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/pageindex/page_index_md.py +0 -0
  104. {pageindex-0.2.10.dev2 → pageindex-0.2.10.dev4}/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.dev2
3
+ Version: 0.2.10.dev4
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,9 +24,9 @@ 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.84.0)
27
+ Requires-Dist: litellm (>=1.97.0)
28
28
  Requires-Dist: openai (>=1.70.0)
29
- Requires-Dist: openai-agents (>=0.18.1) ; extra == "openai"
29
+ Requires-Dist: openai-agents (>=0.18.1)
30
30
  Requires-Dist: pypdfium2 (>=4.30.0)
31
31
  Requires-Dist: python-dotenv (>=1.0.0)
32
32
  Requires-Dist: pyyaml (>=6.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 LLM model to use (default: gpt-4o-2024-11-20)
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 LLM model to use (default: gpt-4o-2024-11-20)
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 _normalize_retrieve_model(model: str) -> str:
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,16 +64,27 @@ 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
- model (str, optional): Local mode only — LLM used to build document
60
- trees. Defaults to the packaged config (see pageindex/config.yaml).
61
- summary_model (str, optional): Local mode only — LLM used for node
62
- summaries and document descriptions.
63
- retrieve_model (str, optional): Local mode only — the model the
64
- local chat surfaces (``chat_completions``, ``responses``)
65
- default to, exposed as ``client.retrieve_model``.
66
- ``provider/model`` names route through LiteLLM; for an
67
- OpenAI-compatible server that itself serves slashed model ids
68
- (vLLM, TGI), prefix ``openai/`` (e.g. ``openai/Qwen/...``).
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``.
71
90
 
@@ -88,6 +107,8 @@ class PageIndexClient:
88
107
  self,
89
108
  api_key: Optional[str] = None,
90
109
  *,
110
+ index_model: Optional[str] = None,
111
+ chat_model: Optional[str] = None,
91
112
  model: Optional[str] = None,
92
113
  summary_model: Optional[str] = None,
93
114
  retrieve_model: Optional[str] = None,
@@ -98,9 +119,11 @@ class PageIndexClient:
98
119
  "api_key is an empty string. Pass a real PageIndex API key for "
99
120
  "cloud mode, or omit api_key entirely for local mode."
100
121
  )
122
+ model_args = {"index_model": index_model, "chat_model": chat_model,
123
+ "model": model, "summary_model": summary_model,
124
+ "retrieve_model": retrieve_model}
101
125
  if api_key is not None:
102
- local_only = {"model": model, "summary_model": summary_model,
103
- "retrieve_model": retrieve_model, "storage_path": storage_path}
126
+ local_only = dict(model_args, storage_path=storage_path)
104
127
  passed = [name for name, value in local_only.items() if value is not None]
105
128
  if passed:
106
129
  raise PageIndexAPIError(
@@ -113,23 +136,29 @@ class PageIndexClient:
113
136
  self._api = CloudAPI(self)
114
137
  else:
115
138
  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()
139
+ overrides = {key: value for key, value in model_args.items()
119
140
  if value}
120
141
  opt = ConfigLoader().load(overrides or None)
121
142
  self.model = opt.model
122
- self.summary_model = getattr(opt, "summary_model", None) or opt.model
123
- self.retrieve_model = _normalize_retrieve_model(
124
- getattr(opt, "retrieve_model", None) or opt.model)
143
+ self.index_model = opt.index_model
144
+ self.summary_model = opt.summary_model
145
+ self.chat_model = _agents_sdk_model_name(opt.chat_model)
125
146
  self.storage_path = storage_path or ".pageindex"
126
147
  from .local_api import LocalAPI
127
148
  self._api = LocalAPI(
128
149
  storage_path=self.storage_path,
129
150
  model=self.model,
130
151
  summary_model=self.summary_model,
131
- retrieve_model=self.retrieve_model,
152
+ retrieve_model=self.chat_model,
132
153
  )
154
+ # LiteLLM's multi-second import would otherwise land on the
155
+ # first chat call; failures resurface there with real context.
156
+ threading.Thread(target=_preload_litellm, daemon=True).start()
157
+
158
+ @property
159
+ def retrieve_model(self):
160
+ """Legacy name for ``chat_model``."""
161
+ return self.chat_model
133
162
 
134
163
  # ---------- DOCUMENT SUBMISSION ----------
135
164
 
@@ -353,6 +382,7 @@ class PageIndexClient:
353
382
  doc_id: Optional[Union[str, list[str]]] = None,
354
383
  stream: bool = False,
355
384
  model: Optional[str] = None,
385
+ reasoning_effort: Optional[str] = None,
356
386
  ) -> Union[str, Iterator[str]]:
357
387
  """
358
388
  Ask a question about your documents, get the answer.
@@ -371,14 +401,19 @@ class PageIndexClient:
371
401
  Keep it identical across a conversation's calls.
372
402
  stream: Yield the answer as text chunks as it is produced.
373
403
  model: Local only — backend model name (defaults to
374
- ``retrieve_model``).
404
+ ``chat_model``).
405
+ reasoning_effort: Local only — how hard the model thinks
406
+ (``"low"`` / ``"medium"`` / ``"high"``; what a backend
407
+ accepts is its own). Unset sends nothing — the model's
408
+ default behavior applies.
375
409
 
376
410
  Returns:
377
411
  - stream=False: the answer string
378
412
  - stream=True: iterator of text chunks
379
413
  """
380
414
  result = self.chat_completions(messages, stream=stream,
381
- doc_id=doc_id, model=model)
415
+ doc_id=doc_id, model=model,
416
+ reasoning_effort=reasoning_effort)
382
417
  if stream:
383
418
  return cast(Iterator[str], result)
384
419
  envelope = cast(dict[str, Any], result)
@@ -394,20 +429,24 @@ class PageIndexClient:
394
429
  enable_citations: bool = False,
395
430
  model: Optional[str] = None,
396
431
  max_turns: Optional[int] = None,
432
+ top_p: Optional[float] = None,
433
+ max_tokens: Optional[int] = None,
434
+ reasoning_effort: Optional[str] = None,
435
+ extra_body: Optional[dict[str, Any]] = None,
397
436
  ) -> Union[dict[str, Any], Iterator[str], Iterator[dict[str, Any]]]:
398
437
  """
399
438
  PageIndex Chat Completions: document QA in one call.
400
439
 
401
440
  Cloud: the hosted chat endpoint. Local: a managed document-QA agent
402
- run over the local tools against your own LLM backend's
403
- /chat/completions (requires ``pageindex[openai]``; the OpenAI SDK's
404
- usual env config OPENAI_API_KEY, OPENAI_BASE_URL selects the
405
- backend, so any OpenAI-compatible server works; a ``/`` in the
406
- model name means LiteLLM provider routing, so prefix ``openai/``
407
- when the backend itself serves slashed ids, e.g.
408
- ``openai/Qwen/...`` on vLLM; LiteLLM-routed Claude models
409
- Anthropic direct, Bedrock, Vertex get the managed prompt
410
- prefix cache-marked automatically). The non-stream
441
+ run over the local tools against your own LLM backend, routed
442
+ through LiteLLM model names mean what LiteLLM says they mean.
443
+ Bare names are OpenAI-compatible shorthand (the OpenAI SDK's usual
444
+ env config OPENAI_API_KEY, OPENAI_BASE_URL selects the
445
+ backend, so any OpenAI-compatible server works; write
446
+ ``openai/Qwen/...`` when the server itself serves slashed ids),
447
+ provider-prefixed names ``anthropic/…``, ``bedrock/…`` reach
448
+ that provider, and LiteLLM-routed Claude models get the managed
449
+ prompt prefix cache-marked automatically. The non-stream
411
450
  response carries the final answer only; streaming yields the
412
451
  agent's visible text as it is produced, including narration before
413
452
  tool calls. ``finish_reason`` reports loop completion ("stop") —
@@ -434,8 +473,23 @@ class PageIndexClient:
434
473
  enable_citations: Cloud-only — local mode raises (citations need
435
474
  block-level OCR data local mode does not store).
436
475
  model: Local only — backend model name (defaults to
437
- ``retrieve_model``). The cloud endpoint selects its own.
476
+ ``chat_model``). The cloud endpoint selects its own.
438
477
  max_turns: Local only — cap on agent turns per call.
478
+ top_p: Local only — nucleus sampling, passed through to the
479
+ model.
480
+ max_tokens: Local only — per-call output cap, passed through;
481
+ it bounds each backend call in the agent loop (the way
482
+ max_turns bounds the loop), not the whole run.
483
+ reasoning_effort: Local only — passed through verbatim as
484
+ LiteLLM's ``reasoning_effort``; each provider maps it to
485
+ its own thinking control, and the values mean what the
486
+ backend says they mean. Unset sends nothing (the
487
+ backend's default applies).
488
+ extra_body: Local only — extra request fields beyond this
489
+ method's parameters, merged last so they win.
490
+ OpenAI-compatible backends take them verbatim in the
491
+ request body; LiteLLM-routed providers take them as
492
+ LiteLLM's own params (mapped or refused per provider).
439
493
 
440
494
  Returns:
441
495
  - stream=False: complete response dict ({'id', 'object', 'created',
@@ -456,12 +510,16 @@ class PageIndexClient:
456
510
  self, messages, stream=stream, doc_id=doc_id,
457
511
  temperature=temperature, stream_metadata=stream_metadata,
458
512
  enable_citations=enable_citations, model=model,
459
- max_turns=max_turns,
513
+ max_turns=max_turns, top_p=top_p, max_tokens=max_tokens,
514
+ reasoning_effort=reasoning_effort, extra_body=extra_body,
460
515
  )
461
- if model is not None or max_turns is not None:
516
+ if (model is not None or max_turns is not None or top_p is not None
517
+ or max_tokens is not None or reasoning_effort is not None
518
+ or extra_body is not None):
462
519
  raise PageIndexAPIError(
463
- "model and max_turns are local-mode parameters the cloud "
464
- "chat endpoint selects its own model."
520
+ "model, max_turns, top_p, max_tokens, reasoning_effort and "
521
+ "extra_body are local-mode parameters the cloud chat "
522
+ "endpoint selects its own model."
465
523
  )
466
524
  return self._api.chat_completions(
467
525
  messages=messages, stream=stream, doc_id=doc_id,
@@ -479,6 +537,9 @@ class PageIndexClient:
479
537
  temperature: Optional[float] = None,
480
538
  top_p: Optional[float] = None,
481
539
  max_turns: Optional[int] = None,
540
+ max_output_tokens: Optional[int] = None,
541
+ reasoning: Optional[dict[str, Any]] = None,
542
+ extra_body: Optional[dict[str, Any]] = None,
482
543
  ) -> Union[dict[str, Any], Iterator[dict[str, Any]]]:
483
544
  """
484
545
  Document QA over the OpenAI Responses protocol — the agentic surface.
@@ -492,7 +553,7 @@ class PageIndexClient:
492
553
  to keep provider prompt-cache prefix continuity and the agent's
493
554
  memory of what it already read.
494
555
 
495
- Requires ``pageindex[openai]`` and a backend that supports the
556
+ Requires a backend that supports the
496
557
  Responses API; backends that only speak chat.completions should use
497
558
  ``chat_completions()``. Provider-prefixed models (``anthropic/…``)
498
559
  route through LiteLLM's chat.completions adapter and are therefore
@@ -502,7 +563,7 @@ class PageIndexClient:
502
563
  Args:
503
564
  input: A user message string, or a list of Responses input items
504
565
  (round-trip prior ``items`` here).
505
- model: Backend model name (defaults to ``retrieve_model``).
566
+ model: Backend model name (defaults to ``chat_model``).
506
567
  stream: Yield Responses stream events as dicts — one logical
507
568
  response per call: per-turn backend lifecycle events are
508
569
  collapsed, sequence numbers are reassigned monotonically,
@@ -517,6 +578,17 @@ class PageIndexClient:
517
578
  instructions: Appended to the managed system prompt.
518
579
  temperature / top_p: Passed through to the model.
519
580
  max_turns: Cap on agent turns per call.
581
+ max_output_tokens: Per-call output cap, passed through; it
582
+ bounds each backend call in the agent loop (the way
583
+ max_turns bounds the loop), not the whole run. Echoed in
584
+ the envelope.
585
+ reasoning: Responses reasoning options, forwarded verbatim
586
+ (e.g. ``{"effort": "low", "summary": "auto"}``) — the
587
+ values mean what the backend says they mean. Unset sends
588
+ nothing (the backend's default applies).
589
+ extra_body: Extra request fields beyond this method's
590
+ parameters, merged verbatim into the request body (last,
591
+ so they win).
520
592
  """
521
593
  from .cloud_api import CloudAPI
522
594
  if isinstance(self._api, CloudAPI):
@@ -528,7 +600,8 @@ class PageIndexClient:
528
600
  return run_responses(
529
601
  self, input, model=model, stream=stream, doc_id=doc_id,
530
602
  instructions=instructions, temperature=temperature, top_p=top_p,
531
- max_turns=max_turns,
603
+ max_turns=max_turns, max_output_tokens=max_output_tokens,
604
+ reasoning=reasoning, extra_body=extra_body,
532
605
  )
533
606
 
534
607
  def messages(
@@ -544,6 +617,8 @@ class PageIndexClient:
544
617
  top_k: Optional[int] = None,
545
618
  stop_sequences: Optional[list[str]] = None,
546
619
  max_turns: Optional[int] = None,
620
+ thinking: Optional[dict[str, Any]] = None,
621
+ extra_body: Optional[dict[str, Any]] = None,
547
622
  ) -> Union[dict[str, Any], Iterator[Any]]:
548
623
  """
549
624
  Document QA over the Anthropic Messages protocol — Claude-native.
@@ -578,6 +653,11 @@ class PageIndexClient:
578
653
  OpenAI surfaces). A truncated run reports
579
654
  ``stop_reason: "tool_use"`` and its ``messages`` remain
580
655
  valid for continuation.
656
+ thinking: Anthropic thinking configuration, forwarded verbatim
657
+ (e.g. ``{"type": "adaptive"}``) — the values and their
658
+ constraints are the backend's. Unset sends nothing.
659
+ extra_body: Extra request fields beyond this method's
660
+ parameters, merged verbatim into each request body.
581
661
  """
582
662
  from .cloud_api import CloudAPI
583
663
  if isinstance(self._api, CloudAPI):
@@ -591,6 +671,7 @@ class PageIndexClient:
591
671
  stream=stream, doc_id=doc_id, system=system,
592
672
  temperature=temperature, top_p=top_p, top_k=top_k,
593
673
  stop_sequences=stop_sequences, max_turns=max_turns,
674
+ thinking=thinking, extra_body=extra_body,
594
675
  )
595
676
 
596
677
  # ---------- DOCUMENT MANAGEMENT ----------
@@ -694,8 +775,7 @@ class PageIndexClient:
694
775
  Local: the in-process tools, any model backend; ``hosted`` does
695
776
  not apply.
696
777
 
697
- Requires ``openai-agents`` (``pip install 'pageindex[openai]'``),
698
- imported only when this method is called.
778
+ ``openai-agents`` is imported only when this method is called.
699
779
 
700
780
  Args:
701
781
  include_management (bool): Also expose tools that modify the
@@ -743,7 +823,7 @@ class PageIndexClient:
743
823
  Sugar over the explicit form — ``agent_instructions`` (with
744
824
  ``doc_id`` targeting) as the instructions and
745
825
  ``as_openai_tools`` as the tools; local clients also carry their
746
- configured ``retrieve_model`` (cloud omits ``model`` so the
826
+ configured ``chat_model`` (cloud omits ``model`` so the
747
827
  framework default applies). To customize further, switch to
748
828
  those methods directly.
749
829
 
@@ -764,7 +844,7 @@ class PageIndexClient:
764
844
  scoped=scope is not None),
765
845
  "tools": self.as_openai_tools(include_management, doc_id=scope),
766
846
  }
767
- model = model or getattr(self, "retrieve_model", None)
847
+ model = model or getattr(self, "chat_model", None)
768
848
  if model:
769
849
  config["model"] = model
770
850
  return config
@@ -1021,10 +1101,13 @@ class PageIndexLocalClient(PageIndexClient):
1021
1101
  def __init__(
1022
1102
  self,
1023
1103
  *,
1104
+ index_model: Optional[str] = None,
1105
+ chat_model: Optional[str] = None,
1024
1106
  model: Optional[str] = None,
1025
1107
  summary_model: Optional[str] = None,
1026
1108
  retrieve_model: Optional[str] = None,
1027
1109
  storage_path: Optional[str] = None,
1028
1110
  ):
1029
- super().__init__(None, model=model, summary_model=summary_model,
1111
+ super().__init__(None, index_model=index_model, chat_model=chat_model,
1112
+ model=model, summary_model=summary_model,
1030
1113
  retrieve_model=retrieve_model, storage_path=storage_path)
@@ -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"
@@ -25,7 +25,7 @@ def build_openai_tools(client, include_management: bool = False,
25
25
  except ImportError as exc:
26
26
  raise PageIndexAPIError(
27
27
  "as_openai_tools requires the OpenAI Agents SDK — "
28
- "pip install openai-agents (or pip install 'pageindex[openai]')."
28
+ "pip install openai-agents."
29
29
  ) from exc
30
30
  from ..agent_tools import (_dumps, _failure, _require_local_scope,
31
31
  _tool_specs)