Spaces:
Running
Running
| """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() | |