main.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. from __future__ import annotations
  2. from contextlib import asynccontextmanager
  3. from fastapi import FastAPI, HTTPException, Query
  4. from fastapi.middleware.cors import CORSMiddleware
  5. from .config import settings
  6. from .db import repository
  7. from .embeddings import embedding_client
  8. from .registry import agents
  9. from .schemas import ChatRequest, ChatResponse, ResetRequest
  10. from .adapters.base import AdapterUnavailable
  11. @asynccontextmanager
  12. async def lifespan(_: FastAPI):
  13. """启动时初始化存储连接,退出时释放连接池。"""
  14. await repository.initialize()
  15. yield
  16. if repository.pool:
  17. await repository.pool.close()
  18. app = FastAPI(title="Memory Agents Web API", version="0.1.0", lifespan=lifespan)
  19. app.add_middleware(
  20. CORSMiddleware,
  21. allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
  22. allow_origin_regex=(
  23. r"^https?://(?:192\.168\.\d+\.\d+|10\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[0-1])\.\d+\.\d+):3000$"
  24. ),
  25. allow_credentials=True,
  26. allow_methods=["*"],
  27. allow_headers=["*"],
  28. )
  29. @app.get("/api/health")
  30. async def health() -> dict:
  31. result = {
  32. "status": "ok",
  33. "storage": repository.storage_mode,
  34. "workspace_id": settings.workspace_id,
  35. "llm_configured": bool(settings.llm_api_key),
  36. "embedding_configured": embedding_client.configured,
  37. }
  38. if repository.initialization_error:
  39. result["storage_error"] = repository.initialization_error
  40. return result
  41. @app.get("/api/systems")
  42. async def systems():
  43. return {
  44. "storage": repository.storage_mode,
  45. "systems": [agent.status for agent in agents.values()],
  46. }
  47. @app.post("/api/chat", response_model=ChatResponse)
  48. async def chat(request: ChatRequest):
  49. """把对话请求交给指定记忆适配器处理。"""
  50. agent = agents[request.system]
  51. try:
  52. return await agent.chat(request.message)
  53. except AdapterUnavailable as exc:
  54. raise HTTPException(status_code=409, detail=str(exc)) from exc
  55. except Exception as exc:
  56. raise HTTPException(status_code=502, detail=f"{request.system} 执行失败:{exc}") from exc
  57. @app.get("/api/memories")
  58. async def memories(system: str = Query(...), query: str | None = None):
  59. if system not in agents:
  60. raise HTTPException(status_code=404, detail="未知记忆系统")
  61. return {"system": system, "memories": await agents[system].memories(query)}
  62. @app.delete("/api/memories/{memory_id}")
  63. async def delete_memory(memory_id: str, system: str = Query(...)):
  64. if system not in agents:
  65. raise HTTPException(status_code=404, detail="未知记忆系统")
  66. deleted = await agents[system].delete_memory(memory_id)
  67. if not deleted:
  68. raise HTTPException(status_code=404, detail="记忆不存在或已删除")
  69. return {"status": "ok", "system": system, "memory_id": memory_id}
  70. @app.get("/api/audit")
  71. async def audit(system: str | None = None):
  72. if system is not None and system not in agents:
  73. raise HTTPException(status_code=404, detail="未知记忆系统")
  74. if system:
  75. events = await agents[system].audit()
  76. else:
  77. events = await repository.list_audit()
  78. return {"system": system, "events": events}
  79. @app.post("/api/reset")
  80. async def reset(request: ResetRequest):
  81. if request.system is None:
  82. # 全量重置必须逐个调用适配器,ReMe 等实现还维护自己的文件和索引。
  83. for agent in agents.values():
  84. await agent.reset()
  85. elif request.system in agents:
  86. await agents[request.system].reset()
  87. else:
  88. raise HTTPException(status_code=404, detail="未知记忆系统")
  89. return {"status": "ok", "system": request.system}