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. await repository.initialize()
  14. yield
  15. if repository.pool:
  16. await repository.pool.close()
  17. app = FastAPI(title="Memory Agents Web API", version="0.1.0", lifespan=lifespan)
  18. app.add_middleware(
  19. CORSMiddleware,
  20. allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
  21. allow_origin_regex=(
  22. r"^https?://(?:192\.168\.\d+\.\d+|10\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[0-1])\.\d+\.\d+):3000$"
  23. ),
  24. allow_credentials=True,
  25. allow_methods=["*"],
  26. allow_headers=["*"],
  27. )
  28. @app.get("/api/health")
  29. async def health() -> dict:
  30. result = {
  31. "status": "ok",
  32. "storage": repository.storage_mode,
  33. "workspace_id": settings.workspace_id,
  34. "llm_configured": bool(settings.llm_api_key),
  35. "embedding_configured": embedding_client.configured,
  36. }
  37. if repository.initialization_error:
  38. result["storage_error"] = repository.initialization_error
  39. return result
  40. @app.get("/api/systems")
  41. async def systems():
  42. return {
  43. "storage": repository.storage_mode,
  44. "systems": [agent.status for agent in agents.values()],
  45. }
  46. @app.post("/api/chat", response_model=ChatResponse)
  47. async def chat(request: ChatRequest):
  48. agent = agents[request.system]
  49. try:
  50. return await agent.chat(request.message)
  51. except AdapterUnavailable as exc:
  52. raise HTTPException(status_code=409, detail=str(exc)) from exc
  53. except Exception as exc:
  54. raise HTTPException(status_code=502, detail=f"{request.system} 执行失败:{exc}") from exc
  55. @app.get("/api/memories")
  56. async def memories(system: str = Query(...), query: str | None = None):
  57. if system not in agents:
  58. raise HTTPException(status_code=404, detail="未知记忆系统")
  59. return {"system": system, "memories": await agents[system].memories(query)}
  60. @app.delete("/api/memories/{memory_id}")
  61. async def delete_memory(memory_id: str, system: str = Query(...)):
  62. if system not in agents:
  63. raise HTTPException(status_code=404, detail="未知记忆系统")
  64. deleted = await agents[system].delete_memory(memory_id)
  65. if not deleted:
  66. raise HTTPException(status_code=404, detail="记忆不存在或已删除")
  67. return {"status": "ok", "system": system, "memory_id": memory_id}
  68. @app.get("/api/audit")
  69. async def audit(system: str | None = None):
  70. if system is not None and system not in agents:
  71. raise HTTPException(status_code=404, detail="未知记忆系统")
  72. if system:
  73. events = await agents[system].audit()
  74. else:
  75. events = await repository.list_audit()
  76. return {"system": system, "events": events}
  77. @app.post("/api/reset")
  78. async def reset(request: ResetRequest):
  79. if request.system is None:
  80. # Some adapters own storage outside the common repository. Calling each
  81. # adapter keeps framework files/indexes (notably ReMe) in sync with the
  82. # PostgreSQL projection when the user requests a global reset.
  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}