letta.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. from __future__ import annotations
  2. from typing import Any
  3. from ..config import settings
  4. from ..schemas import ChatResponse, SystemDescriptor
  5. from .base import AdapterUnavailable, MemoryAgent
  6. class LettaAgent(MemoryAgent):
  7. id = "letta"
  8. def __init__(self) -> None:
  9. super().__init__()
  10. self._client: Any | None = None
  11. self._agent_id: str | None = None
  12. self._import_error: str | None = None
  13. try:
  14. from letta_client import Letta # type: ignore
  15. self._client_class = Letta
  16. except Exception as exc:
  17. self._client_class = None
  18. self._import_error = str(exc)
  19. ready = bool(self._client_class and settings.letta_enabled)
  20. self.descriptor = SystemDescriptor(
  21. id="letta",
  22. name="Letta",
  23. paradigm="有状态 Agent 与虚拟内存",
  24. description="用 Core、Archival、Recall 三层状态,让 Agent 在有限上下文和外部记忆间协作。",
  25. available=ready,
  26. mode="real-sdk" if ready else "unavailable",
  27. status="ready" if ready else "not-configured",
  28. package="letta-client",
  29. setup_hint=None if ready else (
  30. "当前仅保留 Letta 适配骨架,请保持 LETTA_ENABLED=false。"
  31. "完成 Agent ID 恢复、分层记忆投影与删除闭环后再启用。"
  32. ),
  33. )
  34. def _ensure_client(self) -> Any:
  35. if not self._client_class or not settings.letta_enabled:
  36. raise AdapterUnavailable(self.descriptor.setup_hint or "Letta SDK 不可用")
  37. if self._client is None:
  38. kwargs = {"base_url": settings.letta_base_url}
  39. if settings.letta_api_key:
  40. kwargs["token"] = settings.letta_api_key
  41. self._client = self._client_class(**kwargs)
  42. return self._client
  43. async def chat(self, message: str) -> ChatResponse:
  44. client = self._ensure_client()
  45. try:
  46. if self._agent_id is None:
  47. agent = client.agents.create(
  48. name="memory-agents-solutions",
  49. memory_blocks=[
  50. {"label": "persona", "value": "你是一个严谨的 AI 编程助手。"},
  51. {"label": "workspace", "value": f"工作区:{settings.workspace_id}"},
  52. ],
  53. )
  54. self._agent_id = agent.id
  55. response = client.agents.messages.create(
  56. agent_id=self._agent_id,
  57. messages=[{"role": "user", "content": message}],
  58. )
  59. if hasattr(response, "__await__"):
  60. response = await response
  61. answer = str(response)
  62. except Exception as exc:
  63. await self._audit("LETTA/message", status="error", details={"error": str(exc)})
  64. raise AdapterUnavailable(f"Letta 调用失败:{exc}") from exc
  65. await self._audit("LETTA/message", details={"agent_id": self._agent_id})
  66. return ChatResponse(
  67. system="letta", answer=answer, mode="real-sdk",
  68. memory_context=await self.memories(), memory_events=[{"event": "agent_message", "agent_id": self._agent_id}],
  69. audit_events=await self.audit(),
  70. )