Files
youlbot-webui/api_client.py
T

113 lines
3.3 KiB
Python

"""율봇 API 클라이언트 — youlbot REST API(Phase 22)를 httpx로 호출."""
import json
import os
from typing import AsyncIterator
import httpx
from dotenv import load_dotenv
load_dotenv()
_API_URL = os.getenv("YOULBOT_API_URL", "http://localhost:8000").rstrip("/")
_API_TOKEN = os.getenv("YOULBOT_API_TOKEN", "")
def _headers() -> dict:
if _API_TOKEN:
return {"Authorization": f"Bearer {_API_TOKEN}"}
return {}
async def chat(
message: str,
user_id: str = "default",
show_thinking: bool = False,
) -> AsyncIterator[tuple[str, str | None]]:
"""SSE 스트림을 읽어 (token, run_id) 튜플을 yield.
- 일반 토큰: (token_str, None)
- 스트림 종료: ("", run_id_or_None) ← __done 이벤트
"""
async with httpx.AsyncClient(timeout=180) as client:
async with client.stream(
"POST",
f"{_API_URL}/chat",
json={"message": message, "user_id": user_id, "show_thinking": show_thinking},
headers=_headers(),
) as response:
response.raise_for_status()
async for line in response.aiter_lines():
if not line.startswith("data: "):
continue
raw = line[6:]
try:
payload = json.loads(raw)
except json.JSONDecodeError:
yield str(raw), None
continue
if isinstance(payload, dict) and payload.get("__done"):
yield "", payload.get("run_id")
return
yield payload, None
async def reset(user_id: str = "default") -> None:
async with httpx.AsyncClient(timeout=30) as client:
r = await client.post(
f"{_API_URL}/reset",
params={"user_id": user_id},
headers=_headers(),
)
r.raise_for_status()
async def ingest(file_path: str) -> dict:
async with httpx.AsyncClient(timeout=300) as client:
with open(file_path, "rb") as f:
filename = os.path.basename(file_path)
r = await client.post(
f"{_API_URL}/ingest",
files={"file": (filename, f, "application/octet-stream")},
headers=_headers(),
)
r.raise_for_status()
return r.json()
async def list_documents() -> list[str]:
async with httpx.AsyncClient(timeout=30) as client:
r = await client.get(f"{_API_URL}/documents", headers=_headers())
r.raise_for_status()
return r.json().get("documents", [])
async def delete_document(source: str) -> None:
async with httpx.AsyncClient(timeout=30) as client:
r = await client.delete(
f"{_API_URL}/documents/{source}",
headers=_headers(),
)
r.raise_for_status()
async def save_feedback(
user_id: str,
user_msg: str,
asst_msg: str,
rating: int,
run_id: str | None = None,
) -> None:
async with httpx.AsyncClient(timeout=30) as client:
r = await client.post(
f"{_API_URL}/feedback",
json={
"user_id": user_id,
"user_msg": user_msg,
"asst_msg": asst_msg,
"rating": rating,
"run_id": run_id,
},
headers=_headers(),
)
r.raise_for_status()