"""Document 도구 — Document Server REST API (read-only).""" from __future__ import annotations import logging import httpx from config import settings logger = logging.getLogger(__name__) TOOL_NAME = "document" MAX_RESULTS = 5 def _make_result(ok: bool, operation: str, data=None, summary: str = "", error: str | None = None) -> dict: return {"ok": ok, "tool": TOOL_NAME, "operation": operation, "data": data or [], "summary": summary, "error": error} def _headers() -> dict: return {"Authorization": f"Bearer {settings.document_api_token}"} if settings.document_api_token else {} async def search(query: str) -> dict: """문서 하이브리드 검색.""" if not settings.document_api_url: return _make_result(False, "search", error="Document Server 설정이 없습니다.") try: async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get( f"{settings.document_api_url}/search/", params={"q": query, "mode": "hybrid"}, headers=_headers(), ) if resp.status_code != 200: return _make_result(False, "search", error=f"API 응답 오류 ({resp.status_code})") results = resp.json() if isinstance(results, dict): results = results.get("results", results.get("data", [])) # top-N 제한 results = results[:MAX_RESULTS] items = [] for doc in results: items.append({ "id": doc.get("id", ""), "title": doc.get("title", "(제목 없음)"), "domain": doc.get("domain", ""), "preview": str(doc.get("content", doc.get("snippet", "")))[:200], }) summary = f"'{query}' 검색 결과 {len(items)}건" return _make_result(True, "search", data=items, summary=summary) except Exception as e: logger.exception("Document search failed") return _make_result(False, "search", error=str(e)) async def read(doc_id: str) -> dict: """문서 내용 조회.""" if not settings.document_api_url: return _make_result(False, "read", error="Document Server 설정이 없습니다.") try: async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get( f"{settings.document_api_url}/documents/{doc_id}", headers=_headers(), ) if resp.status_code == 404: return _make_result(False, "read", error=f"문서 {doc_id}를 찾을 수 없습니다.") if resp.status_code != 200: return _make_result(False, "read", error=f"API 응답 오류 ({resp.status_code})") doc = resp.json() data = { "id": doc.get("id", ""), "title": doc.get("title", ""), "domain": doc.get("domain", ""), "content": str(doc.get("content", doc.get("markdown_content", "")))[:2000], } return _make_result(True, "read", data=data, summary=f"문서: {data['title']}") except Exception as e: logger.exception("Document read failed") return _make_result(False, "read", error=str(e))