mirror of
https://github.com/VectifyAI/PageIndex.git
synced 2026-05-13 08:42:37 +02:00
feat:compatible with Pageindex SDK (#238)
* feat:compatible with Pageindex SDK
* corner cases fixed
* fix: mock behavior of old SDK
* fix: close streaming response and warn on empty api_key
- LegacyCloudAPI: close response in `finally` for both _stream_chat_response
variants so abandoned iterators no longer leak the TCP connection.
- PageIndexClient: emit a warning instead of silently falling back to local
when api_key is the empty string, surfacing typical env-var-unset misconfig.
- FakeResponse: add close()/closed to match the real requests.Response API.
- Add unit coverage for stream close (both paths) and the empty-api_key warning.
- Add scripts/e2e_legacy_sdk.py to smoke-test the legacy SDK contract end-to-end
against api.pageindex.ai.
* chore: mark legacy SDK methods with @deprecated and docstring pointers
- Decorate the 12 PageIndexClient cloud-SDK compat methods with
@typing_extensions.deprecated(..., category=PendingDeprecationWarning):
- IDE/type-checkers render them with a strikethrough hint
- runtime warnings stay silent by default (no spam for existing callers),
surfaceable via `python -W default::PendingDeprecationWarning`
- Add a one-line docstring on each pointing to the Collection-based equivalent.
- Promote typing-extensions to a direct dependency (was transitive via litellm).
---------
Co-authored-by: XinyanZhou <xinyanzhou@XinyanZhoudeMacBook-Pro.local>
Co-authored-by: saccharin98 <xinyanzhou938@gmail.com>
Co-authored-by: mountain <kose2livs@gmail.com>
This commit is contained in:
parent
6d29886892
commit
595895cf28
10 changed files with 1030 additions and 20 deletions
106
tests/test_legacy_utils_contract.py
Normal file
106
tests/test_legacy_utils_contract.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import sys
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
|
||||
from pageindex import utils
|
||||
|
||||
|
||||
def test_remove_fields_keeps_legacy_max_len():
|
||||
data = {
|
||||
"title": "A long title",
|
||||
"text": "hidden",
|
||||
"nodes": [{"summary": "abcdefghijklmnopqrstuvwxyz"}],
|
||||
}
|
||||
|
||||
result = utils.remove_fields(data, fields=["text"], max_len=5)
|
||||
|
||||
assert "text" not in result
|
||||
assert result["title"] == "A lon..."
|
||||
assert result["nodes"][0]["summary"] == "abcde..."
|
||||
|
||||
|
||||
def test_create_node_mapping_keeps_legacy_page_ranges():
|
||||
tree = [
|
||||
{
|
||||
"node_id": "0001",
|
||||
"title": "Root",
|
||||
"page_index": 1,
|
||||
"nodes": [
|
||||
{"node_id": "0002", "title": "Child", "page_index": 3, "nodes": []},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
plain = utils.create_node_mapping(tree)
|
||||
ranged = utils.create_node_mapping(tree, include_page_ranges=True, max_page=8)
|
||||
|
||||
assert plain["0001"]["title"] == "Root"
|
||||
assert ranged["0001"]["start_index"] == 1
|
||||
assert ranged["0001"]["end_index"] == 3
|
||||
assert ranged["0002"]["start_index"] == 3
|
||||
assert ranged["0002"]["end_index"] == 8
|
||||
|
||||
|
||||
def test_create_node_mapping_prefers_existing_start_end_ranges():
|
||||
tree = [
|
||||
{
|
||||
"node_id": "0001",
|
||||
"title": "Root",
|
||||
"start_index": 1,
|
||||
"end_index": 10,
|
||||
"nodes": [
|
||||
{"node_id": "0002", "title": "Child", "start_index": 3, "end_index": 5},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
ranged = utils.create_node_mapping(tree, include_page_ranges=True, max_page=12)
|
||||
|
||||
assert ranged["0001"]["start_index"] == 1
|
||||
assert ranged["0001"]["end_index"] == 10
|
||||
assert ranged["0002"]["start_index"] == 3
|
||||
assert ranged["0002"]["end_index"] == 5
|
||||
|
||||
|
||||
def test_print_tree_keeps_legacy_exclude_fields(capsys):
|
||||
tree = [{"node_id": "0001", "title": "Root", "text": "hidden", "page_index": 1}]
|
||||
|
||||
utils.print_tree(tree)
|
||||
|
||||
out = capsys.readouterr().out
|
||||
assert "Root" in out
|
||||
assert "hidden" not in out
|
||||
assert "page_index" not in out
|
||||
|
||||
|
||||
def test_call_llm_keeps_legacy_async_openai_contract(monkeypatch):
|
||||
calls = []
|
||||
|
||||
class FakeCompletions:
|
||||
async def create(self, **kwargs):
|
||||
calls.append(kwargs)
|
||||
message = SimpleNamespace(content=" answer ")
|
||||
choice = SimpleNamespace(message=message)
|
||||
return SimpleNamespace(choices=[choice])
|
||||
|
||||
class FakeAsyncOpenAI:
|
||||
def __init__(self, api_key):
|
||||
self.api_key = api_key
|
||||
self.chat = SimpleNamespace(completions=FakeCompletions())
|
||||
|
||||
fake_openai = SimpleNamespace(AsyncOpenAI=FakeAsyncOpenAI)
|
||||
monkeypatch.setitem(sys.modules, "openai", fake_openai)
|
||||
|
||||
result = asyncio.run(utils.call_llm(
|
||||
"hello",
|
||||
api_key="sk-test",
|
||||
model="gpt-test",
|
||||
temperature=0.2,
|
||||
))
|
||||
|
||||
assert result == "answer"
|
||||
assert calls == [{
|
||||
"model": "gpt-test",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"temperature": 0.2,
|
||||
}]
|
||||
Loading…
Add table
Add a link
Reference in a new issue