install_builtin_packs.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/env python3
  2. """
  3. 安装仓库内置的 AgentPack 到本地 agentpacks 目录。
  4. 用法(server 必须先运行):
  5. python scripts/install_builtin_packs.py
  6. 也可以不启动 server,直接写入 agentpacks_dir:
  7. python scripts/install_builtin_packs.py --offline
  8. 内置 pack 位置:agentexample/agentpacks/
  9. 安装目标: ~/.agentpaas/data/agentpacks/ (desktop 模式)
  10. ~/LambdAgentDesktop/agentpacks/ (如果是 desktop 模式且未改数据目录)
  11. """
  12. from __future__ import annotations
  13. import argparse
  14. import json
  15. import os
  16. import sys
  17. import zipfile
  18. import tempfile
  19. from pathlib import Path
  20. REPO_ROOT = Path(__file__).parent.parent.resolve()
  21. PACKS_SRC = REPO_ROOT / "agentexample" / "agentpacks"
  22. BUILTIN_PACKS = [
  23. "research.top-journal-reviewer",
  24. "research.literature-mapper",
  25. "research.grant-planner",
  26. ]
  27. def _zip_pack(pack_id: str, dest_dir: Path) -> str:
  28. pack_dir = PACKS_SRC / pack_id
  29. if not pack_dir.is_dir():
  30. sys.exit(f"❌ Pack 目录不存在: {pack_dir}")
  31. zip_path = str(dest_dir / f"{pack_id}.zip")
  32. with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
  33. for p in pack_dir.rglob("*"):
  34. if p.is_file():
  35. zf.write(p, p.relative_to(pack_dir))
  36. return zip_path
  37. def _default_packs_dir() -> str:
  38. """读取 ~/.agentpaas/config.json 或按 desktop 模式默认值推断 agentpacks_dir。"""
  39. cfg = Path.home() / ".agentpaas" / "config.json"
  40. try:
  41. with open(cfg) as f:
  42. data = json.load(f) or {}
  43. data_dir = data.get("data_dir", "")
  44. except Exception:
  45. data_dir = ""
  46. if not data_dir:
  47. # desktop 默认
  48. data_dir = str(Path.home() / "LambdAgentDesktop")
  49. return os.path.join(data_dir, "agentpacks")
  50. # ── 离线模式:直接调用 install_from_zip ──────────────────────────────────────
  51. def install_offline(packs_dir: str, packs: list[str]) -> None:
  52. from agentpaas.engine.agentpack_store import install_from_zip, get_installed
  53. os.makedirs(packs_dir, exist_ok=True)
  54. print(f"\n安装目标目录: {packs_dir}\n")
  55. with tempfile.TemporaryDirectory() as td:
  56. for pack_id in packs:
  57. existing = get_installed(packs_dir, pack_id)
  58. if existing:
  59. print(f" ✓ {pack_id} v{existing.version} — 已安装,跳过")
  60. continue
  61. zip_path = _zip_pack(pack_id, Path(td))
  62. try:
  63. pack = install_from_zip(zip_path, packs_dir)
  64. print(f" ✓ {pack_id} v{pack.version} — 安装完成")
  65. except Exception as e:
  66. print(f" ✗ {pack_id} — 失败: {e}", file=sys.stderr)
  67. # ── 在线模式:通过 HTTP API 安装(需要 server 运行)───────────────────────────
  68. def install_online(base_url: str, api_key: str, packs: list[str]) -> None:
  69. try:
  70. import requests
  71. except ImportError:
  72. sys.exit("缺少依赖: pip install requests")
  73. session = requests.Session()
  74. if api_key:
  75. session.headers["Authorization"] = f"Bearer {api_key}"
  76. print(f"\n连接 server: {base_url}\n")
  77. # 先列出已安装
  78. try:
  79. r = session.get(f"{base_url}/api/v1/agentpacks", timeout=5)
  80. r.raise_for_status()
  81. already = {p["id"] for p in r.json().get("agentpacks", [])}
  82. except Exception as e:
  83. sys.exit(f"❌ 无法连接 server: {e}\n 请先启动: uvicorn agentpaas.api.app:app --port 8000")
  84. with tempfile.TemporaryDirectory() as td:
  85. for pack_id in packs:
  86. if pack_id in already:
  87. print(f" ✓ {pack_id} — 已安装,跳过")
  88. continue
  89. zip_path = _zip_pack(pack_id, Path(td))
  90. r = session.post(
  91. f"{base_url}/api/v1/agentpacks/install",
  92. json={"zip_path": zip_path},
  93. timeout=30,
  94. )
  95. if r.status_code == 200:
  96. pack = r.json()["installed"]
  97. print(f" ✓ {pack['id']} v{pack['version']} — 安装完成")
  98. else:
  99. print(f" ✗ {pack_id} — HTTP {r.status_code}: {r.text[:120]}", file=sys.stderr)
  100. # ── CLI ───────────────────────────────────────────────────────────────────────
  101. def main() -> None:
  102. parser = argparse.ArgumentParser(
  103. description="安装仓库内置 AgentPack (research.* 三个 pack)",
  104. formatter_class=argparse.RawDescriptionHelpFormatter,
  105. epilog="""
  106. 示例:
  107. # 离线安装(不需要 server 运行,推荐)
  108. python scripts/install_builtin_packs.py --offline
  109. # 在线安装(需要 server 先跑起来)
  110. python scripts/install_builtin_packs.py
  111. # 指定安装目录
  112. python scripts/install_builtin_packs.py --offline --packs-dir /data/agentpacks
  113. """,
  114. )
  115. parser.add_argument(
  116. "--offline", action="store_true",
  117. help="直接写入 agentpacks_dir,不需要 server 运行(推荐)",
  118. )
  119. parser.add_argument(
  120. "--packs-dir", default="",
  121. help="安装目标目录(默认:从 ~/.agentpaas/config.json 推断)",
  122. )
  123. parser.add_argument(
  124. "--url", default="http://127.0.0.1:8000",
  125. help="Server URL(在线模式,默认 http://127.0.0.1:8000)",
  126. )
  127. parser.add_argument(
  128. "--key", default="",
  129. help="API key(在线模式,默认从 ~/.agentpaas/config.json 读取)",
  130. )
  131. parser.add_argument(
  132. "--pack", action="append", dest="packs",
  133. help="只安装指定 pack(可多次指定,默认全部三个)",
  134. )
  135. args = parser.parse_args()
  136. packs = args.packs or BUILTIN_PACKS
  137. print(f"内置 Pack 源目录: {PACKS_SRC}")
  138. if args.offline:
  139. packs_dir = args.packs_dir or _default_packs_dir()
  140. install_offline(packs_dir, packs)
  141. else:
  142. # 读取 api_key
  143. api_key = args.key
  144. if not api_key:
  145. cfg = Path.home() / ".agentpaas" / "config.json"
  146. try:
  147. api_key = (json.load(open(cfg)) or {}).get("api_key", "")
  148. except Exception:
  149. pass
  150. install_online(args.url, api_key, packs)
  151. print('\n完成。在 webui 的"智能体包"页面可以看到已安装的 pack,点击"创建智能体"即可使用。\n')
  152. if __name__ == "__main__":
  153. main()