Skip to content

Commit b29b6d9

Browse files
committed
feat(indexer): expose pageindex_max_concurrency to cap indexing concurrency
PageIndex fans out one indexing LLM call per structure node; on a large document that can open a socket per node and exhaust the process file-descriptor limit ("[Errno 24] Too many open files"). This wires an OpenKB config knob through to PageIndex's IndexConfig.max_concurrency cap. - New `pageindex_max_concurrency` KB config key (default None = let PageIndex apply its own default). - indexer forwards it via `_build_index_config` only when set AND the installed PageIndex's IndexConfig declares the field, so OpenKB keeps working against a pinned PageIndex that predates it (IndexConfig forbids unknown kwargs).
1 parent d267db2 commit b29b6d9

4 files changed

Lines changed: 63 additions & 6 deletions

File tree

openkb/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
"model": "gpt-5.4",
1818
"language": "en",
1919
"pageindex_threshold": 20,
20+
# Cap on concurrent indexing LLM calls PageIndex makes for a single long
21+
# document. None = let PageIndex apply its own default. Raise it to index
22+
# faster, lower it if you hit provider rate limits or "too many open files".
23+
"pageindex_max_concurrency": None,
2024
}
2125

2226
# Default entity-type vocabulary. Overridable per-KB via the optional

openkb/indexer.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,27 @@ def _write_long_doc_artifacts(
153153
return summary_path
154154

155155

156+
def _build_index_config(config: dict[str, Any]) -> IndexConfig:
157+
"""Build the PageIndex ``IndexConfig`` for local indexing.
158+
159+
Forwards the optional ``pageindex_max_concurrency`` KB setting to PageIndex,
160+
which caps how many indexing LLM calls run at once (guarding against the
161+
"too many open files" fd exhaustion on large documents). The value is only
162+
passed when set *and* the installed PageIndex's ``IndexConfig`` declares the
163+
field, so OpenKB keeps working against a pinned PageIndex that predates it
164+
(``IndexConfig`` forbids unknown kwargs).
165+
"""
166+
kwargs: dict[str, Any] = {
167+
"if_add_node_text": True,
168+
"if_add_node_summary": True,
169+
"if_add_doc_description": True,
170+
}
171+
max_concurrency = config.get("pageindex_max_concurrency")
172+
if max_concurrency is not None and "max_concurrency" in IndexConfig.model_fields:
173+
kwargs["max_concurrency"] = max_concurrency
174+
return IndexConfig(**kwargs)
175+
176+
156177
def index_long_document(pdf_path: Path, kb_dir: Path, doc_name: str | None = None) -> IndexResult:
157178
"""Index a long PDF document using PageIndex and write wiki pages.
158179
@@ -166,11 +187,7 @@ def index_long_document(pdf_path: Path, kb_dir: Path, doc_name: str | None = Non
166187
model: str = config.get("model", "gpt-5.4")
167188
pageindex_api_key = os.environ.get("PAGEINDEX_API_KEY", "")
168189

169-
index_config = IndexConfig(
170-
if_add_node_text=True,
171-
if_add_node_summary=True,
172-
if_add_doc_description=True,
173-
)
190+
index_config = _build_index_config(config)
174191

175192
client = PageIndexClient(
176193
api_key=pageindex_api_key or None,

tests/test_config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ def test_default_config_values():
2626
assert DEFAULT_CONFIG["pageindex_threshold"] == 20
2727

2828

29+
def test_pageindex_max_concurrency_defaults_to_none():
30+
assert DEFAULT_CONFIG["pageindex_max_concurrency"] is None
31+
32+
33+
def test_load_pageindex_max_concurrency_override(tmp_path):
34+
config_path = tmp_path / "config.yaml"
35+
config_path.write_text("pageindex_max_concurrency: 12\n", encoding="utf-8")
36+
assert load_config(config_path)["pageindex_max_concurrency"] == 12
37+
38+
2939
def test_load_missing_file_returns_defaults(tmp_path):
3040
missing = tmp_path / "nonexistent" / "config.yaml"
3141
config = load_config(missing)

tests/test_indexer.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,34 @@
55
from unittest.mock import MagicMock, patch
66

77
import pytest
8+
from pageindex import IndexConfig
9+
10+
from openkb.indexer import (
11+
IndexResult,
12+
_build_index_config,
13+
_normalize_page_content,
14+
index_long_document,
15+
)
16+
17+
18+
class TestBuildIndexConfig:
19+
def test_sets_base_flags(self):
20+
cfg = _build_index_config({})
21+
assert cfg.if_add_node_text is True
22+
assert cfg.if_add_node_summary is True
23+
assert cfg.if_add_doc_description is True
24+
25+
def test_forwards_max_concurrency_when_supported(self):
26+
cfg = _build_index_config({"pageindex_max_concurrency": 8})
27+
if "max_concurrency" in IndexConfig.model_fields:
28+
assert cfg.max_concurrency == 8
29+
else:
30+
# A PageIndex predating the field: forwarded as a no-op, never raised.
31+
assert not hasattr(cfg, "max_concurrency")
832

9-
from openkb.indexer import IndexResult, _normalize_page_content, index_long_document
33+
def test_none_value_is_left_to_pageindex_default(self):
34+
cfg = _build_index_config({"pageindex_max_concurrency": None})
35+
assert getattr(cfg, "max_concurrency", None) is None
1036

1137

1238
class TestNormalizePageContent:

0 commit comments

Comments
 (0)