fix: six P2 correctness/robustness cleanups from the SDK review

- legacy call_llm: open AsyncOpenAI via `async with` so the client (and
  its HTTP connection pool) is closed instead of leaked.
- LocalBackend.add_document: fail fast with CollectionNotFoundError when
  the collection doesn't exist, before the expensive parse + LLM index
  (previously the missing FK only tripped at save time, after paying for
  the LLM work). Also raise builtin FileNotFoundError for a missing path
  instead of FileTypeError (which now means only "unsupported extension").
- Collection.query(doc_ids=None): the empty-collection guard now always
  runs — previously it was skipped once PAGEINDEX_EXPERIMENTAL_MULTIDOC
  was set. A single list_documents call serves both the guard and the
  multi-doc warning (no separate call just to decide whether to warn).
- CloudBackend.query_stream: on early consumer break / GeneratorExit,
  signal the background SSE thread to stop and force-close the response,
  so it no longer drains the whole stream in the background.

Adds regression tests for each (client closed, fail-fast on unknown
collection, FileNotFoundError, empty-check under the multidoc env flag,
single list call, early-break thread stop).

Claude-Session: https://claude.ai/code/session_01Kx5DgKbhK1N8autqXH8SmS
This commit is contained in:
mountain 2026-07-07 11:34:13 +08:00
parent 6c948a332b
commit fe36e25773
8 changed files with 128 additions and 19 deletions

View file

@ -75,6 +75,7 @@ def test_print_tree_keeps_legacy_exclude_fields(capsys):
def test_call_llm_keeps_legacy_async_openai_contract(monkeypatch):
calls = []
closed = []
class FakeCompletions:
async def create(self, **kwargs):
@ -88,6 +89,15 @@ def test_call_llm_keeps_legacy_async_openai_contract(monkeypatch):
self.api_key = api_key
self.chat = SimpleNamespace(completions=FakeCompletions())
# call_llm must open the client as an async context manager so it is
# closed (no leaked HTTP connection pool).
async def __aenter__(self):
return self
async def __aexit__(self, *exc):
closed.append(True)
return False
fake_openai = SimpleNamespace(AsyncOpenAI=FakeAsyncOpenAI)
monkeypatch.setitem(sys.modules, "openai", fake_openai)
@ -99,6 +109,7 @@ def test_call_llm_keeps_legacy_async_openai_contract(monkeypatch):
))
assert result == "answer"
assert closed == [True] # client was closed
assert calls == [{
"model": "gpt-test",
"messages": [{"role": "user", "content": "hello"}],