Fix Feishu wiki child node retrieval

This commit is contained in:
2026-07-08 12:08:23 +08:00
parent 7be5dde1db
commit fb0bbc346b

View File

@@ -80,9 +80,17 @@ def _feishu_get(path: str, params: dict | None = None, config: FeishuRetrievalCo
url = f"{FEISHU_OPEN_API}{path}" url = f"{FEISHU_OPEN_API}{path}"
headers = {"Authorization": f"Bearer {token}"} headers = {"Authorization": f"Bearer {token}"}
resp = httpx.get(url, headers=headers, params=params, timeout=config.timeout_seconds) try:
resp.raise_for_status() resp = httpx.get(url, headers=headers, params=params, timeout=config.timeout_seconds)
data = resp.json() resp.raise_for_status()
data = resp.json()
except httpx.HTTPStatusError as exc:
raise ExternalServiceError(
f"飞书HTTP请求失败 [{path}]{exc.response.status_code} {_response_error_detail(exc.response)}",
provider="feishu",
) from exc
except httpx.HTTPError as exc:
raise ExternalServiceError(f"飞书网络请求失败 [{path}]{exc}", provider="feishu") from exc
if data.get("code") != 0: if data.get("code") != 0:
raise ExternalServiceError( raise ExternalServiceError(
@@ -100,9 +108,17 @@ def _feishu_post(path: str, payload: dict | None = None, config: FeishuRetrieval
url = f"{FEISHU_OPEN_API}{path}" url = f"{FEISHU_OPEN_API}{path}"
headers = {"Authorization": f"Bearer {token}"} headers = {"Authorization": f"Bearer {token}"}
resp = httpx.post(url, headers=headers, json=payload, timeout=config.timeout_seconds) try:
resp.raise_for_status() resp = httpx.post(url, headers=headers, json=payload, timeout=config.timeout_seconds)
data = resp.json() resp.raise_for_status()
data = resp.json()
except httpx.HTTPStatusError as exc:
raise ExternalServiceError(
f"飞书HTTP请求失败 [{path}]{exc.response.status_code} {_response_error_detail(exc.response)}",
provider="feishu",
) from exc
except httpx.HTTPError as exc:
raise ExternalServiceError(f"飞书网络请求失败 [{path}]{exc}", provider="feishu") from exc
if data.get("code") != 0: if data.get("code") != 0:
raise ExternalServiceError( raise ExternalServiceError(
@@ -112,6 +128,15 @@ def _feishu_post(path: str, payload: dict | None = None, config: FeishuRetrieval
return data return data
def _response_error_detail(response: httpx.Response) -> str:
try:
body = response.json()
except ValueError:
return response.text[:200]
message = body.get("msg") or body.get("message") or body.get("error") or body
return str(message)[:300]
def _extract_text(elements: list[dict]) -> str: def _extract_text(elements: list[dict]) -> str:
"""从 elements 列表中提取纯文本""" """从 elements 列表中提取纯文本"""
texts = [] texts = []
@@ -208,8 +233,16 @@ def _get_wiki_node_info(node_token: str, config: FeishuRetrievalConfig) -> dict:
} }
def _get_wiki_children(node_token: str, config: FeishuRetrievalConfig, page_size: int = 50) -> list[dict]: def _get_wiki_children(
space_id: str,
node_token: str,
config: FeishuRetrievalConfig,
page_size: int = 50,
) -> list[dict]:
"""获取 Wiki 节点的子节点列表""" """获取 Wiki 节点的子节点列表"""
if not space_id:
raise ExternalServiceError("飞书知识库缺少 SpaceID无法读取子节点", provider="feishu")
all_children = [] all_children = []
page_token = "" page_token = ""
while True: while True:
@@ -220,7 +253,7 @@ def _get_wiki_children(node_token: str, config: FeishuRetrievalConfig, page_size
if page_token: if page_token:
params["page_token"] = page_token params["page_token"] = page_token
data = _feishu_get("/open-apis/wiki/v2/spaces/nodes/get_child_nodes", params=params, config=config) data = _feishu_get(f"/open-apis/wiki/v2/spaces/{space_id}/nodes", params=params, config=config)
children = data.get("data", {}).get("items", []) children = data.get("data", {}).get("items", [])
all_children.extend(children) all_children.extend(children)
@@ -231,7 +264,7 @@ def _get_wiki_children(node_token: str, config: FeishuRetrievalConfig, page_size
return all_children return all_children
def _get_space_node_tree(space_id: str) -> list[dict]: def _get_space_node_tree(space_id: str, config: FeishuRetrievalConfig | None = None) -> list[dict]:
"""获取知识空间下所有根节点""" """获取知识空间下所有根节点"""
all_nodes = [] all_nodes = []
page_token = "" page_token = ""
@@ -243,7 +276,7 @@ def _get_space_node_tree(space_id: str) -> list[dict]:
if page_token: if page_token:
params["page_token"] = page_token params["page_token"] = page_token
data = _feishu_get("/open-apis/wiki/v2/spaces/nodes", params=params) data = _feishu_get(f"/open-apis/wiki/v2/spaces/{space_id}/nodes", params=params, config=config)
items = data.get("data", {}).get("items", []) items = data.get("data", {}).get("items", [])
all_nodes.extend(items) all_nodes.extend(items)
@@ -398,7 +431,7 @@ class FeishuKnowledgeService:
return [(title, content, source_url)] return [(title, content, source_url)]
# 如果节点是文件夹,获取子节点 # 如果节点是文件夹,获取子节点
children = cls._get_wiki_children_with_cache(node_id, config) children = cls._get_wiki_children_with_cache(space_id, node_id, config)
documents = [] documents = []
for child in children: for child in children:
@@ -457,9 +490,14 @@ class FeishuKnowledgeService:
return content return content
@classmethod @classmethod
def _get_wiki_children_with_cache(cls, node_token: str, config: FeishuRetrievalConfig) -> list[dict]: def _get_wiki_children_with_cache(
cls,
space_id: str,
node_token: str,
config: FeishuRetrievalConfig,
) -> list[dict]:
"""带缓存获取子节点""" """带缓存获取子节点"""
cache_key = f"{config.app_id}:children:{node_token}" cache_key = f"{config.app_id}:children:{space_id}:{node_token}"
now = time.time() now = time.time()
if cache_key in cls._content_cache: if cache_key in cls._content_cache:
@@ -467,7 +505,7 @@ class FeishuKnowledgeService:
if now - ts < cls._CACHE_TTL: if now - ts < cls._CACHE_TTL:
return eval(cached_data) # noqa: S307 return eval(cached_data) # noqa: S307
children = _get_wiki_children(node_token, config) children = _get_wiki_children(space_id, node_token, config)
cls._content_cache[cache_key] = (str(children), now) cls._content_cache[cache_key] = (str(children), now)
return children return children