test_liblibai_tool.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """测试 liblibai_controlnet 工具 — 通过 Router API 调用
  2. 用法:
  3. uv run python tests/test_liblibai_tool.py # 运行测试
  4. uv run python tests/test_liblibai_tool.py --search # 仅搜索工具
  5. """
  6. import argparse
  7. import json
  8. import os
  9. import sys
  10. from typing import Any
  11. if sys.platform == "win32":
  12. sys.stdout.reconfigure(encoding="utf-8")
  13. import httpx
  14. BASE_URL = os.environ.get("TOOL_AGENT_ROUTER_URL", "http://127.0.0.1:8001")
  15. DEFAULT_TIMEOUT = 30.0
  16. CALL_TIMEOUT = 300.0
  17. IMAGE_URL = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
  18. def check_connection():
  19. try:
  20. httpx.get(f"{BASE_URL}/health", timeout=3)
  21. except httpx.ConnectError:
  22. print(f"ERROR: Cannot connect to {BASE_URL}")
  23. print("Please start the service first:")
  24. print(" uv run python -m tool_agent")
  25. sys.exit(1)
  26. def _run_tool(
  27. tool_id: str, params: dict[str, Any], timeout: float = CALL_TIMEOUT
  28. ) -> tuple[bool, str | None, Any]:
  29. """POST /run_tool。成功返回 (True, None, result);失败 (False, message, None)。"""
  30. resp = httpx.post(
  31. f"{BASE_URL}/run_tool",
  32. json={"tool_id": tool_id, "params": params},
  33. timeout=timeout,
  34. )
  35. print(f" Status : {resp.status_code}")
  36. if resp.status_code != 200:
  37. return False, f"HTTP {resp.status_code}: {resp.text[:300]}", None
  38. try:
  39. data = resp.json()
  40. except Exception as e:
  41. return False, f"Invalid JSON: {e}", None
  42. if data.get("status") != "success":
  43. return False, data.get("error") or str(data), None
  44. result = data.get("result")
  45. if isinstance(result, dict) and result.get("status") == "error":
  46. return False, str(result.get("error", result)), None
  47. return True, None, result
  48. def test_health():
  49. print("=== Health Check ===")
  50. resp = httpx.get(f"{BASE_URL}/health", timeout=DEFAULT_TIMEOUT)
  51. print(f" Status : {resp.status_code}")
  52. print(f" Body : {json.dumps(resp.json(), ensure_ascii=False, indent=4)}")
  53. assert resp.status_code == 200
  54. print(" [PASS]")
  55. def test_search_tools():
  56. print("=== Search Tools (keyword='liblib') ===")
  57. resp = httpx.get(f"{BASE_URL}/tools", timeout=DEFAULT_TIMEOUT)
  58. if resp.status_code != 200:
  59. print(f" ERROR: GET /tools failed: {resp.status_code}")
  60. print(" [FAIL]")
  61. return
  62. data = resp.json()
  63. tools = [
  64. t for t in data.get("tools", [])
  65. if "liblib" in t.get("tool_id", "").lower()
  66. or "liblib" in t.get("name", "").lower()
  67. ]
  68. print(f" Found : {len(tools)}")
  69. for t in tools:
  70. print(f"\n [{t['tool_id']}]")
  71. print(f" name : {t['name']}")
  72. print(f" state : {t['state']}")
  73. print(f" category: {t.get('category', '')}")
  74. print(f" runtime : {t.get('backend_runtime', '')}")
  75. props = t.get("input_schema", {}).get("properties", {})
  76. required = t.get("input_schema", {}).get("required", [])
  77. print(f" params ({len(props)}):")
  78. for name, info in props.items():
  79. req = "*" if name in required else " "
  80. desc = info.get("description", "")
  81. default_str = f" default={info['default']}" if info.get("default") is not None else ""
  82. print(f" {req} {name:<20} {info.get('type','any'):<10} {desc}{default_str}")
  83. print("\n [PASS]")
  84. def test_controlnet():
  85. """POST /run_tool → liblibai_controlnet"""
  86. print("=== Test liblibai_controlnet ===")
  87. # 确认工具已注册
  88. tid = "liblibai_controlnet"
  89. try:
  90. tr = httpx.get(f"{BASE_URL}/tools", timeout=DEFAULT_TIMEOUT)
  91. tr.raise_for_status()
  92. ids = {t["tool_id"] for t in tr.json().get("tools", [])}
  93. if tid not in ids:
  94. print(f" ERROR : 注册表中无 {tid!r}")
  95. print(" [FAIL]")
  96. return
  97. print(f" tool_id: {tid} (已注册)")
  98. except Exception as e:
  99. print(f" ERROR : GET /tools 失败: {e}")
  100. print(" [FAIL]")
  101. return
  102. prompt = os.environ.get(
  103. "LIBLIBAI_TEST_PROMPT",
  104. "simple white line art, cat, black background",
  105. )
  106. params: dict[str, Any] = {
  107. "image": IMAGE_URL,
  108. "prompt": prompt,
  109. "negative_prompt": "lowres, bad anatomy, text, error",
  110. "width": 512,
  111. "height": 512,
  112. "steps": 20,
  113. "cfg_scale": 7,
  114. "img_count": 1,
  115. "control_weight": 1.0,
  116. "preprocessor": 1,
  117. "canny_low": 100,
  118. "canny_high": 200,
  119. }
  120. print(f" image : {IMAGE_URL}")
  121. print(f" prompt : {prompt}")
  122. print(f" calling {tid} ...")
  123. try:
  124. ok, err, result = _run_tool(tid, params, timeout=CALL_TIMEOUT)
  125. if not ok:
  126. print(f" ERROR : {err}")
  127. print(" [FAIL]")
  128. return
  129. if not isinstance(result, dict):
  130. print(f" ERROR : 非 dict 结果: {type(result)}")
  131. print(" [FAIL]")
  132. return
  133. print(f" Result :")
  134. print(f" status : {result.get('status')}")
  135. print(f" task_id: {result.get('task_id')}")
  136. images = result.get("images", [])
  137. if images:
  138. print(f" images : {len(images)}")
  139. for i, url in enumerate(images):
  140. print(f" [{i+1}] {url}")
  141. print(" [PASS]")
  142. else:
  143. print(" WARN : 未返回图片,任务可能仍在处理中")
  144. print(f" keys : {list(result.keys())}")
  145. print(f" 截断 : {str(result)[:400]}...")
  146. print(" [PASS] (submitted)")
  147. except httpx.TimeoutException:
  148. print(f" ERROR : Request timeout ({CALL_TIMEOUT:.0f}s)")
  149. print(" [FAIL]")
  150. except Exception as e:
  151. print(f" ERROR : {e}")
  152. print(" [FAIL]")
  153. def main():
  154. parser = argparse.ArgumentParser(description="LibLibAI ControlNet Tool Test")
  155. parser.add_argument("--search", action="store_true",
  156. help="only search and list liblib tools")
  157. args = parser.parse_args()
  158. print(f"Target: {BASE_URL}\n")
  159. check_connection()
  160. test_health()
  161. if args.search:
  162. print()
  163. test_search_tools()
  164. else:
  165. print()
  166. test_search_tools()
  167. print()
  168. test_controlnet()
  169. print("\n=== DONE ===")
  170. if __name__ == "__main__":
  171. main()