mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-11-07 19:20:34 +00:00
fix(community): load retrieval TLS trust off the event loop (#7738)
Co-authored-by: Cordis798 <284918518+Cordis798@users.noreply.github.com>
This commit is contained in:
parent
2c8892c810
commit
92325bd126
@ -1703,7 +1703,9 @@ with bge-m3 and GLM-5.3; see that record for coverage limits.
|
||||
Configure `knowledge_search` with either
|
||||
`deerflow.community.ragflow.tools:knowledge_search_tool` or
|
||||
`deerflow.community.lightrag.tools:knowledge_search_tool` in `config.yaml`.
|
||||
Both support attributable retrieval evidence. LightRAG and RAGFlow responses
|
||||
Both support attributable retrieval evidence. Both clients load HTTP client
|
||||
TLS trust in a worker thread before asynchronous requests, keeping synchronous
|
||||
certificate loading off the event loop. LightRAG and RAGFlow responses
|
||||
without complete provider locators use the shared DeerFlow source contract;
|
||||
complete RAGFlow responses retain their existing citation snapshots and scope
|
||||
controls. Knowledge citations show the
|
||||
|
||||
@ -139,6 +139,8 @@ Read the nearest scoped `AGENTS.md` for subsystem ownership.
|
||||
|
||||
Read-only knowledge: [contract](docs/KNOWLEDGE.md) covers independent retrieval
|
||||
tools, scope intersection and extension-owned providers.
|
||||
RAGFlow/LightRAG clients offload HTTPX construction (synchronous TLS trust loading)
|
||||
with `asyncio.to_thread`; keep request execution and session cleanup on the loop.
|
||||
|
||||
### Harness / App Split
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
@ -99,7 +100,7 @@ class LightRAGClient:
|
||||
client_kwargs["transport"] = self._transport
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(**client_kwargs) as client:
|
||||
async with await asyncio.to_thread(httpx.AsyncClient, **client_kwargs) as client:
|
||||
response = await client.request(method, path, json=json)
|
||||
except httpx.TimeoutException:
|
||||
raise LightRAGConnectionError(f"LightRAG request timed out after {self.timeout:g} seconds.") from None
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
@ -78,7 +79,7 @@ class RAGFlowClient:
|
||||
client_kwargs["transport"] = self._transport
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(**client_kwargs) as client:
|
||||
async with await asyncio.to_thread(httpx.AsyncClient, **client_kwargs) as client:
|
||||
response = await client.request(method, path, params=params, json=json)
|
||||
except httpx.TimeoutException:
|
||||
raise RAGFlowConnectionError(f"RAGFlow request timed out after {self.timeout:g} seconds.") from None
|
||||
|
||||
52
backend/tests/blocking_io/test_retrieval_client_ssl.py
Normal file
52
backend/tests/blocking_io/test_retrieval_client_ssl.py
Normal file
@ -0,0 +1,52 @@
|
||||
"""Keep real HTTPX TLS setup; replace only the network response."""
|
||||
|
||||
import asyncio
|
||||
import ssl
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from deerflow.community.lightrag.client import LightRAGClient
|
||||
from deerflow.community.ragflow.client import RAGFlowClient
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("provider", ["ragflow", "lightrag"])
|
||||
async def test_retrieval_client_loads_tls_trust_off_loop(provider, monkeypatch):
|
||||
original = ssl.create_default_context
|
||||
contexts = []
|
||||
clients = []
|
||||
|
||||
def ssl_context(*args, **kwargs):
|
||||
try:
|
||||
asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
context = original(*args, **kwargs)
|
||||
contexts.append(context)
|
||||
return context
|
||||
raise AssertionError("TLS trust loading must run off the event loop")
|
||||
|
||||
async def send(self, request, **kwargs):
|
||||
clients.append(self)
|
||||
if provider == "ragflow":
|
||||
assert request.url.path == "/api/v1/datasets"
|
||||
assert request.url.params["ids"] == "synthetic-dataset"
|
||||
assert request.headers["Authorization"] == "Bearer synthetic-key"
|
||||
payload = {"code": 0, "data": [{"id": "synthetic-dataset"}]}
|
||||
else:
|
||||
assert request.url.path == "/query/data"
|
||||
assert request.headers["X-API-Key"] == "synthetic-key"
|
||||
payload = {"status": "success", "data": {"chunks": []}}
|
||||
return httpx.Response(200, request=request, json=payload)
|
||||
|
||||
monkeypatch.setattr(ssl, "create_default_context", ssl_context)
|
||||
monkeypatch.setattr(httpx.AsyncClient, "send", send)
|
||||
if provider == "ragflow":
|
||||
client = RAGFlowClient(base_url="https://ragflow.invalid", api_key="synthetic-key")
|
||||
assert await client.list_datasets(dataset_id="synthetic-dataset") == [{"id": "synthetic-dataset"}]
|
||||
else:
|
||||
client = LightRAGClient(base_url="https://lightrag.invalid", api_key="synthetic-key")
|
||||
assert await client.query_data("synthetic query") == {"chunks": []}
|
||||
assert contexts
|
||||
assert len(clients) == 1
|
||||
assert clients[0].is_closed
|
||||
Loading…
x
Reference in New Issue
Block a user