meo-chatbot / scripts /test_api.py
Monmoonluna's picture
add scripts/test_api.py — CLI client to test /chat without a frontend
971546f
Raw
History Blame Contribute Delete
3.73 kB
"""Test client cho /chat API — KHÔNG cần frontend.
Dùng urllib (không phụ thuộc requests) + UTF-8 chuẩn để gửi tiếng Việt không bị
lỗi như curl trên Windows.
Cách chạy (server phải đang chạy: python -m uvicorn app.main:app):
python scripts/test_api.py "Mèo của tôi nôn liên tục, phải làm gì?"
python scripts/test_api.py "Mèo Anh lông ngắn ăn gì?" --topic nutrition --level beginner
python scripts/test_api.py --health # chỉ ping GET /health
Tuỳ chọn:
--base URL gốc (mặc định http://localhost:8000)
--topic auto|breed|nutrition|health|care|behavior (mặc định auto)
--level auto|beginner|advanced (mặc định auto)
--top-k số chunk lấy về (1-20, mặc định 5)
--api-key gửi header X-API-Key (nếu server đặt env API_KEY)
"""
import argparse
import json
import sys
import urllib.error
import urllib.request
sys.stdout.reconfigure(encoding="utf-8")
def call(base: str, path: str, payload: dict | None, api_key: str | None) -> tuple[int, dict]:
url = base.rstrip("/") + path
headers = {"Content-Type": "application/json; charset=utf-8"}
if api_key:
headers["X-API-Key"] = api_key
data = json.dumps(payload).encode("utf-8") if payload is not None else None
method = "POST" if data is not None else "GET"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(req, timeout=120) as r:
return r.status, json.loads(r.read().decode("utf-8"))
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", "replace")
try:
body = json.loads(body)
except Exception:
pass
return e.code, {"error": body, "retry_after": e.headers.get("Retry-After")}
except urllib.error.URLError as e:
print(f"❌ Không kết nối được {url} — server đã chạy chưa? ({e.reason})")
sys.exit(1)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("question", nargs="?", help="Câu hỏi tiếng Việt")
ap.add_argument("--base", default="http://localhost:8000")
ap.add_argument("--topic", default="auto")
ap.add_argument("--level", default="auto")
ap.add_argument("--top-k", type=int, default=5)
ap.add_argument("--api-key", default=None)
ap.add_argument("--health", action="store_true", help="Chỉ gọi GET /health")
args = ap.parse_args()
if args.health:
status, body = call(args.base, "/health", None, args.api_key)
print(f"GET /health → {status} {body}")
return
if not args.question:
ap.error("cần truyền câu hỏi, hoặc dùng --health")
payload = {
"messages": [{"role": "user", "content": args.question}],
"topic_filter": args.topic,
"user_level": args.level,
"top_k": args.top_k,
}
print(f"POST {args.base}/chat\n→ Q: {args.question}\n")
status, body = call(args.base, "/chat", payload, args.api_key)
if status != 200:
print(f"❌ HTTP {status}")
print(json.dumps(body, ensure_ascii=False, indent=2))
return
print(f"✅ HTTP 200 | needs_vet={body.get('needs_vet')} | "
f"topic_detected={body.get('topic_detected')} | session={body.get('session_id')}")
print("\n--- REPLY ---")
print(body.get("reply", ""))
cites = body.get("citations", [])
print(f"\n--- CITATIONS ({len(cites)}) ---")
for c in cites:
print(f" [{c['index']}] {c.get('source_name')}{c.get('section_title') or ''}")
print(f" {c.get('source_url')}")
if __name__ == "__main__":
main()