plugin_api.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """Authenticated Dashboard API, mounted at /api/plugins/yaoyao-bot-bridge."""
  2. import asyncio
  3. import importlib.util
  4. from pathlib import Path
  5. from fastapi import APIRouter, Request
  6. from fastapi.responses import JSONResponse
  7. _spec = importlib.util.spec_from_file_location(
  8. "_yaoyao_bot_bridge_bootstrap", Path(__file__).parents[1] / "bootstrap.py"
  9. )
  10. if _spec is None or _spec.loader is None:
  11. raise ImportError("夭夭工具桥加载失败")
  12. _bootstrap = importlib.util.module_from_spec(_spec)
  13. _spec.loader.exec_module(_bootstrap)
  14. bridge = _bootstrap.runtime()
  15. router = APIRouter()
  16. def _error(exc):
  17. return JSONResponse(
  18. status_code=exc.status,
  19. content={"detail": str(exc), "code": exc.code},
  20. )
  21. async def _body(request, limit=16384):
  22. raw = await request.body()
  23. if len(raw) > limit:
  24. raise bridge.BridgeError("绑定请求过大", 413, "invalid_request")
  25. try:
  26. body = await request.json()
  27. except Exception:
  28. raise bridge.BridgeError("绑定请求必须是 JSON 对象", 400, "invalid_request") from None
  29. if not isinstance(body, dict):
  30. raise bridge.BridgeError("绑定请求必须是 JSON 对象", 400, "invalid_request")
  31. return body
  32. @router.post("/computer-file")
  33. async def computer_file(request: Request):
  34. try:
  35. identity = bridge.request_identity(request)
  36. body = await _body(request, 36 * 1024 * 1024)
  37. return await asyncio.to_thread(bridge.computer_file, identity, body)
  38. except bridge.BridgeError as exc:
  39. return _error(exc)
  40. except Exception:
  41. return JSONResponse(status_code=502, content={"code": "computer_file_failed", "detail": "Hermes 文件传输失败"})
  42. @router.post("/memory-extract")
  43. async def memory_extract(request: Request):
  44. try:
  45. bridge.request_identity(request)
  46. body = await _body(request, 512 * 1024)
  47. return await bridge.extract_memory(body.get("profile"), body.get("prompt"))
  48. except bridge.BridgeError as exc:
  49. return _error(exc)
  50. except Exception:
  51. return JSONResponse(status_code=502, content={"code": "memory_extraction_failed", "detail": "Hermes 记忆提炼失败"})
  52. @router.get("/capabilities")
  53. def capabilities(request: Request, profile: str = "default"):
  54. try:
  55. bridge.request_identity(request)
  56. return bridge.capabilities(profile)
  57. except bridge.BridgeError as exc:
  58. return _error(exc)
  59. @router.post("/bind")
  60. async def bind(request: Request):
  61. try:
  62. identity = bridge.request_identity(request)
  63. body = await _body(request)
  64. return await asyncio.to_thread(bridge.bind, identity, body)
  65. except bridge.BridgeError as exc:
  66. return _error(exc)
  67. @router.post("/unbind")
  68. async def unbind(request: Request):
  69. try:
  70. identity = bridge.request_identity(request)
  71. return bridge.unbind(identity, await _body(request))
  72. except bridge.BridgeError as exc:
  73. return _error(exc)
  74. @router.post("/sampling")
  75. async def sampling(request: Request):
  76. try:
  77. identity = bridge.request_identity(request)
  78. return await bridge.sampling_request(identity, await _body(request))
  79. except bridge.BridgeError as exc:
  80. return _error(exc)
  81. except Exception:
  82. return JSONResponse(status_code=502, content={"code": "sampling_failed", "detail": "Hermes MCP 采样失败"})