@elizaos/python 2.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +26 -0
- package/README.md +239 -0
- package/elizaos/__init__.py +280 -0
- package/elizaos/action_docs.py +149 -0
- package/elizaos/advanced_capabilities/__init__.py +85 -0
- package/elizaos/advanced_capabilities/actions/__init__.py +54 -0
- package/elizaos/advanced_capabilities/actions/add_contact.py +139 -0
- package/elizaos/advanced_capabilities/actions/follow_room.py +151 -0
- package/elizaos/advanced_capabilities/actions/image_generation.py +148 -0
- package/elizaos/advanced_capabilities/actions/mute_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/remove_contact.py +145 -0
- package/elizaos/advanced_capabilities/actions/roles.py +207 -0
- package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +154 -0
- package/elizaos/advanced_capabilities/actions/search_contacts.py +145 -0
- package/elizaos/advanced_capabilities/actions/send_message.py +187 -0
- package/elizaos/advanced_capabilities/actions/settings.py +151 -0
- package/elizaos/advanced_capabilities/actions/unfollow_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/unmute_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/update_contact.py +164 -0
- package/elizaos/advanced_capabilities/actions/update_entity.py +161 -0
- package/elizaos/advanced_capabilities/evaluators/__init__.py +18 -0
- package/elizaos/advanced_capabilities/evaluators/reflection.py +134 -0
- package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +203 -0
- package/elizaos/advanced_capabilities/providers/__init__.py +36 -0
- package/elizaos/advanced_capabilities/providers/agent_settings.py +60 -0
- package/elizaos/advanced_capabilities/providers/contacts.py +77 -0
- package/elizaos/advanced_capabilities/providers/facts.py +82 -0
- package/elizaos/advanced_capabilities/providers/follow_ups.py +113 -0
- package/elizaos/advanced_capabilities/providers/knowledge.py +83 -0
- package/elizaos/advanced_capabilities/providers/relationships.py +112 -0
- package/elizaos/advanced_capabilities/providers/roles.py +97 -0
- package/elizaos/advanced_capabilities/providers/settings.py +51 -0
- package/elizaos/advanced_capabilities/services/__init__.py +18 -0
- package/elizaos/advanced_capabilities/services/follow_up.py +138 -0
- package/elizaos/advanced_capabilities/services/rolodex.py +244 -0
- package/elizaos/advanced_memory/__init__.py +3 -0
- package/elizaos/advanced_memory/evaluators.py +97 -0
- package/elizaos/advanced_memory/memory_service.py +556 -0
- package/elizaos/advanced_memory/plugin.py +30 -0
- package/elizaos/advanced_memory/prompts.py +12 -0
- package/elizaos/advanced_memory/providers.py +90 -0
- package/elizaos/advanced_memory/types.py +65 -0
- package/elizaos/advanced_planning/__init__.py +10 -0
- package/elizaos/advanced_planning/actions.py +145 -0
- package/elizaos/advanced_planning/message_classifier.py +127 -0
- package/elizaos/advanced_planning/planning_service.py +712 -0
- package/elizaos/advanced_planning/plugin.py +40 -0
- package/elizaos/advanced_planning/prompts.py +4 -0
- package/elizaos/basic_capabilities/__init__.py +66 -0
- package/elizaos/basic_capabilities/actions/__init__.py +24 -0
- package/elizaos/basic_capabilities/actions/choice.py +140 -0
- package/elizaos/basic_capabilities/actions/ignore.py +66 -0
- package/elizaos/basic_capabilities/actions/none.py +56 -0
- package/elizaos/basic_capabilities/actions/reply.py +120 -0
- package/elizaos/basic_capabilities/providers/__init__.py +54 -0
- package/elizaos/basic_capabilities/providers/action_state.py +113 -0
- package/elizaos/basic_capabilities/providers/actions.py +263 -0
- package/elizaos/basic_capabilities/providers/attachments.py +76 -0
- package/elizaos/basic_capabilities/providers/capabilities.py +62 -0
- package/elizaos/basic_capabilities/providers/character.py +113 -0
- package/elizaos/basic_capabilities/providers/choice.py +73 -0
- package/elizaos/basic_capabilities/providers/context_bench.py +44 -0
- package/elizaos/basic_capabilities/providers/current_time.py +58 -0
- package/elizaos/basic_capabilities/providers/entities.py +99 -0
- package/elizaos/basic_capabilities/providers/evaluators.py +54 -0
- package/elizaos/basic_capabilities/providers/providers_list.py +55 -0
- package/elizaos/basic_capabilities/providers/recent_messages.py +85 -0
- package/elizaos/basic_capabilities/providers/time.py +45 -0
- package/elizaos/basic_capabilities/providers/world.py +93 -0
- package/elizaos/basic_capabilities/services/__init__.py +18 -0
- package/elizaos/basic_capabilities/services/embedding.py +122 -0
- package/elizaos/basic_capabilities/services/task.py +178 -0
- package/elizaos/bootstrap/__init__.py +12 -0
- package/elizaos/bootstrap/actions/__init__.py +68 -0
- package/elizaos/bootstrap/actions/add_contact.py +149 -0
- package/elizaos/bootstrap/actions/choice.py +147 -0
- package/elizaos/bootstrap/actions/follow_room.py +151 -0
- package/elizaos/bootstrap/actions/ignore.py +80 -0
- package/elizaos/bootstrap/actions/image_generation.py +135 -0
- package/elizaos/bootstrap/actions/mute_room.py +151 -0
- package/elizaos/bootstrap/actions/none.py +71 -0
- package/elizaos/bootstrap/actions/remove_contact.py +159 -0
- package/elizaos/bootstrap/actions/reply.py +140 -0
- package/elizaos/bootstrap/actions/roles.py +193 -0
- package/elizaos/bootstrap/actions/schedule_follow_up.py +164 -0
- package/elizaos/bootstrap/actions/search_contacts.py +159 -0
- package/elizaos/bootstrap/actions/send_message.py +173 -0
- package/elizaos/bootstrap/actions/settings.py +165 -0
- package/elizaos/bootstrap/actions/unfollow_room.py +151 -0
- package/elizaos/bootstrap/actions/unmute_room.py +151 -0
- package/elizaos/bootstrap/actions/update_contact.py +178 -0
- package/elizaos/bootstrap/actions/update_entity.py +175 -0
- package/elizaos/bootstrap/autonomy/__init__.py +18 -0
- package/elizaos/bootstrap/autonomy/action.py +197 -0
- package/elizaos/bootstrap/autonomy/providers.py +165 -0
- package/elizaos/bootstrap/autonomy/routes.py +171 -0
- package/elizaos/bootstrap/autonomy/service.py +562 -0
- package/elizaos/bootstrap/autonomy/types.py +18 -0
- package/elizaos/bootstrap/evaluators/__init__.py +19 -0
- package/elizaos/bootstrap/evaluators/reflection.py +118 -0
- package/elizaos/bootstrap/evaluators/relationship_extraction.py +192 -0
- package/elizaos/bootstrap/plugin.py +140 -0
- package/elizaos/bootstrap/providers/__init__.py +80 -0
- package/elizaos/bootstrap/providers/action_state.py +71 -0
- package/elizaos/bootstrap/providers/actions.py +256 -0
- package/elizaos/bootstrap/providers/agent_settings.py +63 -0
- package/elizaos/bootstrap/providers/attachments.py +76 -0
- package/elizaos/bootstrap/providers/capabilities.py +66 -0
- package/elizaos/bootstrap/providers/character.py +128 -0
- package/elizaos/bootstrap/providers/choice.py +77 -0
- package/elizaos/bootstrap/providers/contacts.py +78 -0
- package/elizaos/bootstrap/providers/context_bench.py +49 -0
- package/elizaos/bootstrap/providers/current_time.py +56 -0
- package/elizaos/bootstrap/providers/entities.py +99 -0
- package/elizaos/bootstrap/providers/evaluators.py +58 -0
- package/elizaos/bootstrap/providers/facts.py +86 -0
- package/elizaos/bootstrap/providers/follow_ups.py +116 -0
- package/elizaos/bootstrap/providers/knowledge.py +73 -0
- package/elizaos/bootstrap/providers/providers_list.py +59 -0
- package/elizaos/bootstrap/providers/recent_messages.py +85 -0
- package/elizaos/bootstrap/providers/relationships.py +106 -0
- package/elizaos/bootstrap/providers/roles.py +95 -0
- package/elizaos/bootstrap/providers/settings.py +55 -0
- package/elizaos/bootstrap/providers/time.py +45 -0
- package/elizaos/bootstrap/providers/world.py +97 -0
- package/elizaos/bootstrap/services/__init__.py +26 -0
- package/elizaos/bootstrap/services/embedding.py +122 -0
- package/elizaos/bootstrap/services/follow_up.py +138 -0
- package/elizaos/bootstrap/services/rolodex.py +244 -0
- package/elizaos/bootstrap/services/task.py +585 -0
- package/elizaos/bootstrap/types.py +54 -0
- package/elizaos/bootstrap/utils/__init__.py +7 -0
- package/elizaos/bootstrap/utils/xml.py +69 -0
- package/elizaos/character.py +149 -0
- package/elizaos/logger.py +179 -0
- package/elizaos/media/__init__.py +45 -0
- package/elizaos/media/mime.py +315 -0
- package/elizaos/media/search.py +161 -0
- package/elizaos/media/tests/__init__.py +1 -0
- package/elizaos/media/tests/test_mime.py +117 -0
- package/elizaos/media/tests/test_search.py +156 -0
- package/elizaos/plugin.py +191 -0
- package/elizaos/prompts.py +1071 -0
- package/elizaos/py.typed +0 -0
- package/elizaos/runtime.py +2572 -0
- package/elizaos/services/__init__.py +49 -0
- package/elizaos/services/hook_service.py +511 -0
- package/elizaos/services/message_service.py +1248 -0
- package/elizaos/settings.py +182 -0
- package/elizaos/streaming_context.py +159 -0
- package/elizaos/trajectory_context.py +18 -0
- package/elizaos/types/__init__.py +512 -0
- package/elizaos/types/agent.py +31 -0
- package/elizaos/types/components.py +208 -0
- package/elizaos/types/database.py +64 -0
- package/elizaos/types/environment.py +46 -0
- package/elizaos/types/events.py +47 -0
- package/elizaos/types/memory.py +45 -0
- package/elizaos/types/model.py +393 -0
- package/elizaos/types/plugin.py +188 -0
- package/elizaos/types/primitives.py +100 -0
- package/elizaos/types/runtime.py +460 -0
- package/elizaos/types/service.py +113 -0
- package/elizaos/types/service_interfaces.py +244 -0
- package/elizaos/types/state.py +188 -0
- package/elizaos/types/task.py +29 -0
- package/elizaos/utils/__init__.py +108 -0
- package/elizaos/utils/spec_examples.py +48 -0
- package/elizaos/utils/streaming.py +426 -0
- package/elizaos_atropos_shared/__init__.py +1 -0
- package/elizaos_atropos_shared/canonical_eliza.py +282 -0
- package/package.json +19 -0
- package/pyproject.toml +143 -0
- package/requirements-dev.in +11 -0
- package/requirements-dev.lock +134 -0
- package/requirements.in +9 -0
- package/requirements.lock +64 -0
- package/tests/__init__.py +0 -0
- package/tests/test_action_parameters.py +154 -0
- package/tests/test_actions_provider_examples.py +39 -0
- package/tests/test_advanced_memory_behavior.py +96 -0
- package/tests/test_advanced_memory_flag.py +30 -0
- package/tests/test_advanced_planning_behavior.py +225 -0
- package/tests/test_advanced_planning_flag.py +26 -0
- package/tests/test_autonomy.py +445 -0
- package/tests/test_bootstrap_initialize.py +37 -0
- package/tests/test_character.py +163 -0
- package/tests/test_character_provider.py +231 -0
- package/tests/test_dynamic_prompt_exec.py +561 -0
- package/tests/test_logger_redaction.py +43 -0
- package/tests/test_plugin.py +117 -0
- package/tests/test_runtime.py +422 -0
- package/tests/test_salt_production_enforcement.py +22 -0
- package/tests/test_settings_crypto.py +118 -0
- package/tests/test_streaming.py +295 -0
- package/tests/test_types.py +221 -0
- package/tests/test_uuid_parity.py +46 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from elizaos.types import Evaluator, HandlerOptions
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def _summarization_validate(runtime, message, _state=None) -> bool:
|
|
7
|
+
if not message.content or not message.content.text:
|
|
8
|
+
return False
|
|
9
|
+
svc = runtime.get_service("memory")
|
|
10
|
+
if svc is None:
|
|
11
|
+
return False
|
|
12
|
+
|
|
13
|
+
# Best-effort message count (works with adapter; otherwise stays 0)
|
|
14
|
+
count = await runtime.count_memories(message.room_id, unique=False, table_name="messages")
|
|
15
|
+
existing = await svc.get_current_session_summary(message.room_id)
|
|
16
|
+
cfg = svc.get_config()
|
|
17
|
+
|
|
18
|
+
if existing is None:
|
|
19
|
+
return count >= cfg.short_term_summarization_threshold
|
|
20
|
+
return (count - existing.last_message_offset) >= cfg.short_term_summarization_interval
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def _summarization_handler(
|
|
24
|
+
runtime, message, _state=None, _options: HandlerOptions | None = None, *_args
|
|
25
|
+
):
|
|
26
|
+
svc = runtime.get_service("memory")
|
|
27
|
+
if svc is None:
|
|
28
|
+
return None
|
|
29
|
+
# Pull a window of messages; adapters may vary, so be defensive.
|
|
30
|
+
messages = await runtime.get_memories(
|
|
31
|
+
{"tableName": "messages", "roomId": message.room_id, "count": 200, "unique": False}
|
|
32
|
+
)
|
|
33
|
+
await svc.summarize_from_messages(
|
|
34
|
+
room_id=message.room_id,
|
|
35
|
+
agent_id=runtime.agent_id,
|
|
36
|
+
agent_name=runtime.character.name,
|
|
37
|
+
messages=messages,
|
|
38
|
+
)
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def _long_term_validate(runtime, message, _state=None) -> bool:
|
|
43
|
+
if not message.content or not message.content.text:
|
|
44
|
+
return False
|
|
45
|
+
if message.entity_id == runtime.agent_id:
|
|
46
|
+
return False
|
|
47
|
+
svc = runtime.get_service("memory")
|
|
48
|
+
if svc is None:
|
|
49
|
+
return False
|
|
50
|
+
cfg = svc.get_config()
|
|
51
|
+
if not cfg.long_term_extraction_enabled:
|
|
52
|
+
return False
|
|
53
|
+
count = await runtime.count_memories(message.room_id, unique=False, table_name="messages")
|
|
54
|
+
return await svc.should_run_extraction(message.entity_id, message.room_id, count)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def _long_term_handler(
|
|
58
|
+
runtime, message, _state=None, _options: HandlerOptions | None = None, *_args
|
|
59
|
+
):
|
|
60
|
+
svc = runtime.get_service("memory")
|
|
61
|
+
if svc is None:
|
|
62
|
+
return None
|
|
63
|
+
messages = await runtime.get_memories(
|
|
64
|
+
{"tableName": "messages", "roomId": message.room_id, "count": 50, "unique": False}
|
|
65
|
+
)
|
|
66
|
+
await svc.extract_long_term_from_messages(
|
|
67
|
+
entity_id=message.entity_id,
|
|
68
|
+
room_id=message.room_id,
|
|
69
|
+
agent_id=runtime.agent_id,
|
|
70
|
+
agent_name=runtime.character.name,
|
|
71
|
+
messages=messages,
|
|
72
|
+
)
|
|
73
|
+
# Update checkpoint
|
|
74
|
+
count = await runtime.count_memories(message.room_id, unique=False, table_name="messages")
|
|
75
|
+
await svc.set_last_extraction_checkpoint(message.entity_id, message.room_id, count)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
summarization_evaluator = Evaluator(
|
|
80
|
+
name="MEMORY_SUMMARIZATION",
|
|
81
|
+
description="Automatically summarizes conversations to optimize context usage",
|
|
82
|
+
always_run=True,
|
|
83
|
+
similes=["CONVERSATION_SUMMARY", "CONTEXT_COMPRESSION", "MEMORY_OPTIMIZATION"],
|
|
84
|
+
examples=[],
|
|
85
|
+
validate=_summarization_validate,
|
|
86
|
+
handler=_summarization_handler,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
long_term_extraction_evaluator = Evaluator(
|
|
90
|
+
name="LONG_TERM_MEMORY_EXTRACTION",
|
|
91
|
+
description="Extracts long-term facts about users from conversations",
|
|
92
|
+
always_run=True,
|
|
93
|
+
similes=["MEMORY_EXTRACTION", "FACT_LEARNING", "USER_PROFILING"],
|
|
94
|
+
examples=[],
|
|
95
|
+
validate=_long_term_validate,
|
|
96
|
+
handler=_long_term_handler,
|
|
97
|
+
)
|
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import heapq
|
|
4
|
+
import re
|
|
5
|
+
import time
|
|
6
|
+
from typing import cast
|
|
7
|
+
from uuid import UUID, uuid4
|
|
8
|
+
|
|
9
|
+
from elizaos.types.model import ModelType
|
|
10
|
+
from elizaos.types.primitives import string_to_uuid
|
|
11
|
+
from elizaos.types.service import Service
|
|
12
|
+
|
|
13
|
+
from .prompts import (
|
|
14
|
+
INITIAL_SUMMARIZATION_TEMPLATE,
|
|
15
|
+
LONG_TERM_EXTRACTION_TEMPLATE,
|
|
16
|
+
UPDATE_SUMMARIZATION_TEMPLATE,
|
|
17
|
+
)
|
|
18
|
+
from .types import (
|
|
19
|
+
LongTermMemory,
|
|
20
|
+
LongTermMemoryCategory,
|
|
21
|
+
MemoryConfig,
|
|
22
|
+
MemoryExtraction,
|
|
23
|
+
SessionSummary,
|
|
24
|
+
SummaryResult,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
_TABLE_SESSION_SUMMARY = "session_summary"
|
|
28
|
+
_TABLE_LONG_TERM_MEMORY = "long_term_memory"
|
|
29
|
+
_GLOBAL_LONG_TERM_ROOM_ID = string_to_uuid("advanced-memory:long-term")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _parse_summary_xml(xml: str) -> SummaryResult:
|
|
33
|
+
summary_match = re.search(r"<text>([\s\S]*?)</text>", xml)
|
|
34
|
+
topics_match = re.search(r"<topics>([\s\S]*?)</topics>", xml)
|
|
35
|
+
key_points_matches = re.findall(r"<point>([\s\S]*?)</point>", xml)
|
|
36
|
+
|
|
37
|
+
summary = summary_match.group(1).strip() if summary_match else "Summary not available"
|
|
38
|
+
topics = (
|
|
39
|
+
[t.strip() for t in topics_match.group(1).split(",") if t.strip()] if topics_match else []
|
|
40
|
+
)
|
|
41
|
+
key_points = [p.strip() for p in key_points_matches]
|
|
42
|
+
return SummaryResult(summary=summary, topics=topics, key_points=key_points)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _parse_memory_extraction_xml(xml: str) -> list[MemoryExtraction]:
|
|
46
|
+
pattern = (
|
|
47
|
+
r"<memory>[\s\S]*?"
|
|
48
|
+
r"<category>(.*?)</category>[\s\S]*?"
|
|
49
|
+
r"<content>(.*?)</content>[\s\S]*?"
|
|
50
|
+
r"<confidence>(.*?)</confidence>[\s\S]*?"
|
|
51
|
+
r"</memory>"
|
|
52
|
+
)
|
|
53
|
+
out: list[MemoryExtraction] = []
|
|
54
|
+
for match in re.finditer(pattern, xml):
|
|
55
|
+
category_str = match.group(1).strip()
|
|
56
|
+
content = match.group(2).strip()
|
|
57
|
+
confidence_str = match.group(3).strip()
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
category = LongTermMemoryCategory(category_str)
|
|
61
|
+
except Exception:
|
|
62
|
+
continue
|
|
63
|
+
try:
|
|
64
|
+
confidence = float(confidence_str)
|
|
65
|
+
except Exception:
|
|
66
|
+
continue
|
|
67
|
+
if content:
|
|
68
|
+
out.append(MemoryExtraction(category=category, content=content, confidence=confidence))
|
|
69
|
+
return out
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _top_k_by_confidence(items: list[LongTermMemory], limit: int) -> list[LongTermMemory]:
|
|
73
|
+
if limit <= 0 or not items:
|
|
74
|
+
return []
|
|
75
|
+
if len(items) <= limit:
|
|
76
|
+
return sorted(items, key=lambda mm: mm.confidence, reverse=True)
|
|
77
|
+
return heapq.nlargest(limit, items, key=lambda mm: mm.confidence)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class MemoryService(Service):
|
|
81
|
+
service_type = "memory"
|
|
82
|
+
|
|
83
|
+
def __init__(self, runtime=None) -> None:
|
|
84
|
+
super().__init__(runtime=runtime)
|
|
85
|
+
self._config: MemoryConfig = MemoryConfig()
|
|
86
|
+
# Fallback storage for runtimes without a DB adapter (tests/benchmarks).
|
|
87
|
+
self._session_summaries: dict[str, SessionSummary] = {}
|
|
88
|
+
self._long_term: dict[str, list[LongTermMemory]] = {}
|
|
89
|
+
self._extraction_checkpoints: dict[str, int] = {}
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def capability_description(self) -> str:
|
|
93
|
+
return "Memory management with short-term summarization and long-term persistent facts"
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
async def start(cls, runtime):
|
|
97
|
+
svc = cls(runtime=runtime)
|
|
98
|
+
# read settings
|
|
99
|
+
settings = runtime.character.settings or {}
|
|
100
|
+
if (v := settings.get("MEMORY_SUMMARIZATION_THRESHOLD")) is not None and isinstance(
|
|
101
|
+
v, (int, float, str)
|
|
102
|
+
):
|
|
103
|
+
svc._config.short_term_summarization_threshold = int(v)
|
|
104
|
+
if (v := settings.get("MEMORY_RETAIN_RECENT")) is not None and isinstance(
|
|
105
|
+
v, (int, float, str)
|
|
106
|
+
):
|
|
107
|
+
svc._config.short_term_retain_recent = int(v)
|
|
108
|
+
if (v := settings.get("MEMORY_SUMMARIZATION_INTERVAL")) is not None and isinstance(
|
|
109
|
+
v, (int, float, str)
|
|
110
|
+
):
|
|
111
|
+
svc._config.short_term_summarization_interval = int(v)
|
|
112
|
+
if (v := settings.get("MEMORY_MAX_NEW_MESSAGES")) is not None and isinstance(
|
|
113
|
+
v, (int, float, str)
|
|
114
|
+
):
|
|
115
|
+
svc._config.summary_max_new_messages = int(v)
|
|
116
|
+
if (v := settings.get("MEMORY_LONG_TERM_ENABLED")) is not None:
|
|
117
|
+
if str(v).lower() == "false":
|
|
118
|
+
svc._config.long_term_extraction_enabled = False
|
|
119
|
+
elif str(v).lower() == "true":
|
|
120
|
+
svc._config.long_term_extraction_enabled = True
|
|
121
|
+
if (v := settings.get("MEMORY_CONFIDENCE_THRESHOLD")) is not None and isinstance(
|
|
122
|
+
v, (int, float, str)
|
|
123
|
+
):
|
|
124
|
+
svc._config.long_term_confidence_threshold = float(v)
|
|
125
|
+
if (v := settings.get("MEMORY_EXTRACTION_THRESHOLD")) is not None and isinstance(
|
|
126
|
+
v, (int, float, str)
|
|
127
|
+
):
|
|
128
|
+
svc._config.long_term_extraction_threshold = int(v)
|
|
129
|
+
if (v := settings.get("MEMORY_EXTRACTION_INTERVAL")) is not None and isinstance(
|
|
130
|
+
v, (int, float, str)
|
|
131
|
+
):
|
|
132
|
+
svc._config.long_term_extraction_interval = int(v)
|
|
133
|
+
|
|
134
|
+
runtime.logger.info("MemoryService started successfully", src="service:memory")
|
|
135
|
+
return svc
|
|
136
|
+
|
|
137
|
+
async def stop(self) -> None:
|
|
138
|
+
self._session_summaries.clear()
|
|
139
|
+
self._long_term.clear()
|
|
140
|
+
self._extraction_checkpoints.clear()
|
|
141
|
+
|
|
142
|
+
def get_config(self) -> MemoryConfig:
|
|
143
|
+
return MemoryConfig(**self._config.__dict__)
|
|
144
|
+
|
|
145
|
+
def _checkpoint_key(self, entity_id: UUID, room_id: UUID) -> str:
|
|
146
|
+
return f"memory:extraction:{entity_id}:{room_id}"
|
|
147
|
+
|
|
148
|
+
async def get_last_extraction_checkpoint(self, entity_id: UUID, room_id: UUID) -> int:
|
|
149
|
+
runtime = self.runtime
|
|
150
|
+
key = self._checkpoint_key(entity_id, room_id)
|
|
151
|
+
if runtime is not None and getattr(runtime, "_adapter", None) is not None:
|
|
152
|
+
cached = await runtime.get_cache(key)
|
|
153
|
+
if cached is None:
|
|
154
|
+
return 0
|
|
155
|
+
try:
|
|
156
|
+
if isinstance(cached, (int, float, str)):
|
|
157
|
+
return int(cached)
|
|
158
|
+
return 0
|
|
159
|
+
except Exception:
|
|
160
|
+
return 0
|
|
161
|
+
return int(self._extraction_checkpoints.get(key, 0))
|
|
162
|
+
|
|
163
|
+
async def set_last_extraction_checkpoint(
|
|
164
|
+
self, entity_id: UUID, room_id: UUID, message_count: int
|
|
165
|
+
) -> None:
|
|
166
|
+
runtime = self.runtime
|
|
167
|
+
key = self._checkpoint_key(entity_id, room_id)
|
|
168
|
+
if runtime is not None and getattr(runtime, "_adapter", None) is not None:
|
|
169
|
+
_ = await runtime.set_cache(key, int(message_count))
|
|
170
|
+
return
|
|
171
|
+
self._extraction_checkpoints[key] = int(message_count)
|
|
172
|
+
|
|
173
|
+
async def should_run_extraction(
|
|
174
|
+
self, entity_id: UUID, room_id: UUID, current_message_count: int
|
|
175
|
+
) -> bool:
|
|
176
|
+
threshold = self._config.long_term_extraction_threshold
|
|
177
|
+
interval = self._config.long_term_extraction_interval
|
|
178
|
+
if current_message_count < threshold:
|
|
179
|
+
return False
|
|
180
|
+
last_cp = await self.get_last_extraction_checkpoint(entity_id, room_id)
|
|
181
|
+
current_cp = (current_message_count // interval) * interval
|
|
182
|
+
return current_cp > last_cp
|
|
183
|
+
|
|
184
|
+
async def get_current_session_summary(self, room_id: UUID) -> SessionSummary | None:
|
|
185
|
+
runtime = self.runtime
|
|
186
|
+
if runtime is None:
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
# Prefer DB-backed retrieval when available.
|
|
190
|
+
if getattr(runtime, "_adapter", None) is not None:
|
|
191
|
+
# Session summary is stored under the agent entity_id, scoped to the room.
|
|
192
|
+
mems = await runtime.get_memories(
|
|
193
|
+
{
|
|
194
|
+
"roomId": str(room_id),
|
|
195
|
+
"entityId": str(runtime.agent_id),
|
|
196
|
+
"agentId": str(runtime.agent_id),
|
|
197
|
+
"count": 10,
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
for m in mems:
|
|
201
|
+
if not isinstance(m, dict):
|
|
202
|
+
continue
|
|
203
|
+
meta = m.get("metadata")
|
|
204
|
+
if not isinstance(meta, dict):
|
|
205
|
+
meta = {}
|
|
206
|
+
if meta.get("type") != _TABLE_SESSION_SUMMARY:
|
|
207
|
+
continue
|
|
208
|
+
try:
|
|
209
|
+
return SessionSummary(
|
|
210
|
+
id=UUID(str(m.get("id"))),
|
|
211
|
+
agent_id=UUID(str(m.get("agentId") or runtime.agent_id)),
|
|
212
|
+
room_id=UUID(str(m.get("roomId") or room_id)),
|
|
213
|
+
entity_id=UUID(str(meta["entityId"])) if meta.get("entityId") else None,
|
|
214
|
+
summary=str((m.get("content") or {}).get("text") or ""),
|
|
215
|
+
message_count=int(meta.get("messageCount") or 0),
|
|
216
|
+
last_message_offset=int(meta.get("lastMessageOffset") or 0),
|
|
217
|
+
topics=[str(t) for t in (meta.get("topics") or [])],
|
|
218
|
+
metadata=dict(meta.get("metadata") or {}),
|
|
219
|
+
)
|
|
220
|
+
except Exception:
|
|
221
|
+
# Best-effort; ignore corrupt rows.
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
return self._session_summaries.get(str(room_id))
|
|
225
|
+
|
|
226
|
+
async def store_session_summary(
|
|
227
|
+
self,
|
|
228
|
+
agent_id: UUID,
|
|
229
|
+
room_id: UUID,
|
|
230
|
+
summary: str,
|
|
231
|
+
message_count: int,
|
|
232
|
+
last_message_offset: int,
|
|
233
|
+
entity_id: UUID | None = None,
|
|
234
|
+
topics: list[str] | None = None,
|
|
235
|
+
metadata: dict[str, object] | None = None,
|
|
236
|
+
) -> SessionSummary:
|
|
237
|
+
runtime = self.runtime
|
|
238
|
+
s = SessionSummary(
|
|
239
|
+
id=uuid4(),
|
|
240
|
+
agent_id=agent_id,
|
|
241
|
+
room_id=room_id,
|
|
242
|
+
entity_id=entity_id,
|
|
243
|
+
summary=summary,
|
|
244
|
+
message_count=message_count,
|
|
245
|
+
last_message_offset=last_message_offset,
|
|
246
|
+
topics=topics or [],
|
|
247
|
+
metadata=metadata or {},
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
if runtime is not None and getattr(runtime, "_adapter", None) is not None:
|
|
251
|
+
existing = await self.get_current_session_summary(room_id)
|
|
252
|
+
mem = {
|
|
253
|
+
"id": str(existing.id) if existing else str(s.id),
|
|
254
|
+
"entityId": str(runtime.agent_id),
|
|
255
|
+
"agentId": str(runtime.agent_id),
|
|
256
|
+
"roomId": str(room_id),
|
|
257
|
+
"worldId": None,
|
|
258
|
+
"content": {"text": summary},
|
|
259
|
+
"metadata": {
|
|
260
|
+
"type": _TABLE_SESSION_SUMMARY,
|
|
261
|
+
"messageCount": int(message_count),
|
|
262
|
+
"lastMessageOffset": int(last_message_offset),
|
|
263
|
+
"topics": list(s.topics),
|
|
264
|
+
"entityId": str(entity_id) if entity_id else None,
|
|
265
|
+
"metadata": dict(s.metadata),
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
if existing:
|
|
269
|
+
_ = await runtime.update_memory(mem)
|
|
270
|
+
return s
|
|
271
|
+
_ = await runtime.create_memory(
|
|
272
|
+
cast(dict[str, object], mem), _TABLE_SESSION_SUMMARY, unique=False
|
|
273
|
+
)
|
|
274
|
+
return s
|
|
275
|
+
|
|
276
|
+
self._session_summaries[str(room_id)] = s
|
|
277
|
+
return s
|
|
278
|
+
|
|
279
|
+
async def update_session_summary(
|
|
280
|
+
self, summary_id: UUID, room_id: UUID, **updates: object
|
|
281
|
+
) -> None:
|
|
282
|
+
runtime = self.runtime
|
|
283
|
+
if runtime is not None and getattr(runtime, "_adapter", None) is not None:
|
|
284
|
+
existing = await self.get_current_session_summary(room_id)
|
|
285
|
+
if not existing or existing.id != summary_id:
|
|
286
|
+
return
|
|
287
|
+
# Reuse store_session_summary to persist updated state.
|
|
288
|
+
summary_text = (
|
|
289
|
+
str(updates["summary"])
|
|
290
|
+
if "summary" in updates and isinstance(updates["summary"], str)
|
|
291
|
+
else existing.summary
|
|
292
|
+
)
|
|
293
|
+
msg_count_raw = updates.get("message_count")
|
|
294
|
+
msg_count = (
|
|
295
|
+
int(msg_count_raw)
|
|
296
|
+
if isinstance(msg_count_raw, (int, float, str))
|
|
297
|
+
else existing.message_count
|
|
298
|
+
)
|
|
299
|
+
last_off_raw = updates.get("last_message_offset")
|
|
300
|
+
last_off = (
|
|
301
|
+
int(last_off_raw)
|
|
302
|
+
if isinstance(last_off_raw, (int, float, str))
|
|
303
|
+
else existing.last_message_offset
|
|
304
|
+
)
|
|
305
|
+
topics = (
|
|
306
|
+
[str(t) for t in updates["topics"]]
|
|
307
|
+
if "topics" in updates and isinstance(updates["topics"], list)
|
|
308
|
+
else existing.topics
|
|
309
|
+
)
|
|
310
|
+
meta = (
|
|
311
|
+
{str(k): v for k, v in updates["metadata"].items()}
|
|
312
|
+
if "metadata" in updates and isinstance(updates["metadata"], dict)
|
|
313
|
+
else existing.metadata
|
|
314
|
+
)
|
|
315
|
+
await self.store_session_summary(
|
|
316
|
+
agent_id=existing.agent_id,
|
|
317
|
+
room_id=existing.room_id,
|
|
318
|
+
summary=summary_text,
|
|
319
|
+
message_count=msg_count,
|
|
320
|
+
last_message_offset=last_off,
|
|
321
|
+
entity_id=existing.entity_id,
|
|
322
|
+
topics=topics,
|
|
323
|
+
metadata=meta,
|
|
324
|
+
)
|
|
325
|
+
return
|
|
326
|
+
|
|
327
|
+
existing = self._session_summaries.get(str(room_id))
|
|
328
|
+
if not existing or existing.id != summary_id:
|
|
329
|
+
return
|
|
330
|
+
if "summary" in updates and isinstance(updates["summary"], str):
|
|
331
|
+
existing.summary = updates["summary"]
|
|
332
|
+
if "message_count" in updates and isinstance(updates["message_count"], (int, float, str)):
|
|
333
|
+
existing.message_count = int(updates["message_count"])
|
|
334
|
+
if "last_message_offset" in updates and isinstance(
|
|
335
|
+
updates["last_message_offset"], (int, float, str)
|
|
336
|
+
):
|
|
337
|
+
existing.last_message_offset = int(updates["last_message_offset"])
|
|
338
|
+
if "topics" in updates and isinstance(updates["topics"], list):
|
|
339
|
+
existing.topics = [str(t) for t in updates["topics"]]
|
|
340
|
+
if "metadata" in updates and isinstance(updates["metadata"], dict):
|
|
341
|
+
existing.metadata = {str(k): v for k, v in updates["metadata"].items()}
|
|
342
|
+
|
|
343
|
+
async def store_long_term_memory(
|
|
344
|
+
self,
|
|
345
|
+
agent_id: UUID,
|
|
346
|
+
entity_id: UUID,
|
|
347
|
+
category: LongTermMemoryCategory,
|
|
348
|
+
content: str,
|
|
349
|
+
confidence: float = 1.0,
|
|
350
|
+
source: str | None = None,
|
|
351
|
+
metadata: dict[str, object] | None = None,
|
|
352
|
+
) -> LongTermMemory:
|
|
353
|
+
runtime = self.runtime
|
|
354
|
+
m = LongTermMemory(
|
|
355
|
+
id=uuid4(),
|
|
356
|
+
agent_id=agent_id,
|
|
357
|
+
entity_id=entity_id,
|
|
358
|
+
category=category,
|
|
359
|
+
content=content,
|
|
360
|
+
confidence=float(confidence),
|
|
361
|
+
source=source,
|
|
362
|
+
metadata=metadata or {},
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
if runtime is not None and getattr(runtime, "_adapter", None) is not None:
|
|
366
|
+
mem = {
|
|
367
|
+
"id": str(m.id),
|
|
368
|
+
"entityId": str(entity_id),
|
|
369
|
+
"agentId": str(runtime.agent_id),
|
|
370
|
+
"roomId": str(_GLOBAL_LONG_TERM_ROOM_ID),
|
|
371
|
+
"worldId": None,
|
|
372
|
+
"content": {"text": content},
|
|
373
|
+
"metadata": {
|
|
374
|
+
"type": _TABLE_LONG_TERM_MEMORY,
|
|
375
|
+
"category": category.value,
|
|
376
|
+
"confidence": float(m.confidence),
|
|
377
|
+
"source": m.source,
|
|
378
|
+
"metadata": dict(m.metadata),
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
_ = await runtime.create_memory(
|
|
382
|
+
cast(dict[str, object], mem), _TABLE_LONG_TERM_MEMORY, unique=False
|
|
383
|
+
)
|
|
384
|
+
return m
|
|
385
|
+
|
|
386
|
+
self._long_term.setdefault(str(entity_id), []).append(m)
|
|
387
|
+
return m
|
|
388
|
+
|
|
389
|
+
async def get_long_term_memories(
|
|
390
|
+
self,
|
|
391
|
+
entity_id: UUID,
|
|
392
|
+
category: LongTermMemoryCategory | None = None,
|
|
393
|
+
limit: int = 25,
|
|
394
|
+
) -> list[LongTermMemory]:
|
|
395
|
+
if limit <= 0:
|
|
396
|
+
return []
|
|
397
|
+
runtime = self.runtime
|
|
398
|
+
if runtime is not None and getattr(runtime, "_adapter", None) is not None:
|
|
399
|
+
db_mems = await runtime.get_memories(
|
|
400
|
+
{
|
|
401
|
+
"roomId": str(_GLOBAL_LONG_TERM_ROOM_ID),
|
|
402
|
+
"entityId": str(entity_id),
|
|
403
|
+
"agentId": str(runtime.agent_id),
|
|
404
|
+
"count": 200,
|
|
405
|
+
}
|
|
406
|
+
)
|
|
407
|
+
out: list[LongTermMemory] = []
|
|
408
|
+
for m in db_mems:
|
|
409
|
+
if not isinstance(m, dict):
|
|
410
|
+
continue
|
|
411
|
+
meta = m.get("metadata")
|
|
412
|
+
if not isinstance(meta, dict):
|
|
413
|
+
meta = {}
|
|
414
|
+
if meta.get("type") != _TABLE_LONG_TERM_MEMORY:
|
|
415
|
+
continue
|
|
416
|
+
cat_raw = str(meta.get("category") or "")
|
|
417
|
+
try:
|
|
418
|
+
cat = LongTermMemoryCategory(cat_raw)
|
|
419
|
+
except Exception:
|
|
420
|
+
continue
|
|
421
|
+
if category is not None and cat != category:
|
|
422
|
+
continue
|
|
423
|
+
try:
|
|
424
|
+
out.append(
|
|
425
|
+
LongTermMemory(
|
|
426
|
+
id=UUID(str(m.get("id"))),
|
|
427
|
+
agent_id=UUID(str(m.get("agentId") or runtime.agent_id)),
|
|
428
|
+
entity_id=UUID(str(m.get("entityId") or entity_id)),
|
|
429
|
+
category=cat,
|
|
430
|
+
content=str((m.get("content") or {}).get("text") or ""),
|
|
431
|
+
confidence=float(meta.get("confidence") or 1.0),
|
|
432
|
+
source=str(meta.get("source"))
|
|
433
|
+
if meta.get("source") is not None
|
|
434
|
+
else None,
|
|
435
|
+
metadata=dict(meta.get("metadata") or {}),
|
|
436
|
+
)
|
|
437
|
+
)
|
|
438
|
+
except Exception:
|
|
439
|
+
continue
|
|
440
|
+
return _top_k_by_confidence(out, limit)
|
|
441
|
+
|
|
442
|
+
local_mems = self._long_term.get(str(entity_id), [])
|
|
443
|
+
if category is not None:
|
|
444
|
+
local_mems = [m for m in local_mems if m.category == category]
|
|
445
|
+
return _top_k_by_confidence(local_mems, limit)
|
|
446
|
+
|
|
447
|
+
async def get_formatted_long_term_memories(self, entity_id: UUID) -> str:
|
|
448
|
+
mems = await self.get_long_term_memories(entity_id, None, 20)
|
|
449
|
+
if not mems:
|
|
450
|
+
return ""
|
|
451
|
+
grouped: dict[LongTermMemoryCategory, list[LongTermMemory]] = {}
|
|
452
|
+
for m in mems:
|
|
453
|
+
grouped.setdefault(m.category, []).append(m)
|
|
454
|
+
sections: list[str] = []
|
|
455
|
+
for cat, items in grouped.items():
|
|
456
|
+
name = cat.value.replace("_", " ").title()
|
|
457
|
+
sections.append(f"**{name}**:\n" + "\n".join(f"- {x.content}" for x in items))
|
|
458
|
+
return "\n\n".join(sections)
|
|
459
|
+
|
|
460
|
+
async def summarize_from_messages(
|
|
461
|
+
self, room_id: UUID, agent_id: UUID, agent_name: str, messages: list[object]
|
|
462
|
+
) -> None:
|
|
463
|
+
# `messages` is a list of elizaos Memory objects; we only use common fields.
|
|
464
|
+
dialogue = []
|
|
465
|
+
for m in messages:
|
|
466
|
+
content = getattr(m, "content", None)
|
|
467
|
+
text = getattr(content, "text", None)
|
|
468
|
+
if not text:
|
|
469
|
+
continue
|
|
470
|
+
sender = agent_name if getattr(m, "entity_id", None) == agent_id else "User"
|
|
471
|
+
dialogue.append(f"{sender}: {text}")
|
|
472
|
+
if not dialogue:
|
|
473
|
+
return
|
|
474
|
+
|
|
475
|
+
existing = await self.get_current_session_summary(room_id)
|
|
476
|
+
if existing:
|
|
477
|
+
prompt = UPDATE_SUMMARIZATION_TEMPLATE.format(
|
|
478
|
+
existing_summary=existing.summary,
|
|
479
|
+
existing_topics=", ".join(existing.topics) if existing.topics else "None",
|
|
480
|
+
new_messages="\n".join(dialogue[-self._config.summary_max_new_messages :]),
|
|
481
|
+
)
|
|
482
|
+
else:
|
|
483
|
+
prompt = INITIAL_SUMMARIZATION_TEMPLATE.format(recent_messages="\n".join(dialogue))
|
|
484
|
+
|
|
485
|
+
response = await self.runtime.use_model(
|
|
486
|
+
ModelType.TEXT_LARGE,
|
|
487
|
+
{"prompt": prompt, "temperature": 0.2, "maxTokens": self._config.summary_max_tokens},
|
|
488
|
+
)
|
|
489
|
+
parsed = _parse_summary_xml(str(response))
|
|
490
|
+
if existing:
|
|
491
|
+
await self.update_session_summary(
|
|
492
|
+
existing.id,
|
|
493
|
+
room_id,
|
|
494
|
+
summary=parsed.summary,
|
|
495
|
+
message_count=existing.message_count + len(dialogue),
|
|
496
|
+
last_message_offset=existing.last_message_offset + len(dialogue),
|
|
497
|
+
topics=parsed.topics,
|
|
498
|
+
metadata={"keyPoints": parsed.key_points},
|
|
499
|
+
)
|
|
500
|
+
else:
|
|
501
|
+
await self.store_session_summary(
|
|
502
|
+
agent_id=agent_id,
|
|
503
|
+
room_id=room_id,
|
|
504
|
+
entity_id=None,
|
|
505
|
+
summary=parsed.summary,
|
|
506
|
+
message_count=len(dialogue),
|
|
507
|
+
last_message_offset=len(dialogue),
|
|
508
|
+
topics=parsed.topics,
|
|
509
|
+
metadata={"keyPoints": parsed.key_points},
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
async def extract_long_term_from_messages(
|
|
513
|
+
self,
|
|
514
|
+
entity_id: UUID,
|
|
515
|
+
room_id: UUID,
|
|
516
|
+
agent_id: UUID,
|
|
517
|
+
agent_name: str,
|
|
518
|
+
messages: list[object],
|
|
519
|
+
) -> None:
|
|
520
|
+
if not self._config.long_term_extraction_enabled:
|
|
521
|
+
return
|
|
522
|
+
formatted = []
|
|
523
|
+
for m in messages:
|
|
524
|
+
content = getattr(m, "content", None)
|
|
525
|
+
text = getattr(content, "text", None)
|
|
526
|
+
if not text:
|
|
527
|
+
continue
|
|
528
|
+
sender = agent_name if getattr(m, "entity_id", None) == agent_id else "User"
|
|
529
|
+
formatted.append(f"{sender}: {text}")
|
|
530
|
+
existing = await self.get_long_term_memories(entity_id, None, 30)
|
|
531
|
+
existing_text = (
|
|
532
|
+
"\n".join(
|
|
533
|
+
f"[{m.category.value}] {m.content} (confidence: {m.confidence})" for m in existing
|
|
534
|
+
)
|
|
535
|
+
if existing
|
|
536
|
+
else "None yet"
|
|
537
|
+
)
|
|
538
|
+
prompt = LONG_TERM_EXTRACTION_TEMPLATE.format(
|
|
539
|
+
recent_messages="\n".join(formatted[-20:]),
|
|
540
|
+
existing_memories=existing_text,
|
|
541
|
+
)
|
|
542
|
+
response = await self.runtime.use_model(
|
|
543
|
+
ModelType.TEXT_LARGE, {"prompt": prompt, "temperature": 0.3, "maxTokens": 2000}
|
|
544
|
+
)
|
|
545
|
+
extractions = _parse_memory_extraction_xml(str(response))
|
|
546
|
+
for ex in extractions:
|
|
547
|
+
if ex.confidence >= max(self._config.long_term_confidence_threshold, 0.85):
|
|
548
|
+
await self.store_long_term_memory(
|
|
549
|
+
agent_id=agent_id,
|
|
550
|
+
entity_id=entity_id,
|
|
551
|
+
category=ex.category,
|
|
552
|
+
content=ex.content,
|
|
553
|
+
confidence=ex.confidence,
|
|
554
|
+
source="conversation",
|
|
555
|
+
metadata={"roomId": str(room_id), "extractedAt": int(time.time() * 1000)},
|
|
556
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from elizaos.types import Plugin
|
|
4
|
+
|
|
5
|
+
from .evaluators import long_term_extraction_evaluator, summarization_evaluator
|
|
6
|
+
from .memory_service import MemoryService
|
|
7
|
+
from .providers import context_summary_provider, long_term_memory_provider
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_advanced_memory_plugin() -> Plugin:
|
|
11
|
+
async def init_plugin(_config, runtime) -> None:
|
|
12
|
+
runtime.logger.info(
|
|
13
|
+
"Advanced memory enabled",
|
|
14
|
+
src="plugin:advanced-memory",
|
|
15
|
+
agentId=str(runtime.agent_id),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
return Plugin(
|
|
19
|
+
name="memory",
|
|
20
|
+
description="Built-in advanced memory (summaries + long-term facts)",
|
|
21
|
+
init=init_plugin,
|
|
22
|
+
config={},
|
|
23
|
+
services=[MemoryService],
|
|
24
|
+
actions=[],
|
|
25
|
+
providers=[long_term_memory_provider, context_summary_provider],
|
|
26
|
+
evaluators=[summarization_evaluator, long_term_extraction_evaluator],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
advanced_memory_plugin = create_advanced_memory_plugin()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Re-export from centralized prompts
|
|
2
|
+
from elizaos.prompts import (
|
|
3
|
+
INITIAL_SUMMARIZATION_TEMPLATE,
|
|
4
|
+
LONG_TERM_EXTRACTION_TEMPLATE,
|
|
5
|
+
UPDATE_SUMMARIZATION_TEMPLATE,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"INITIAL_SUMMARIZATION_TEMPLATE",
|
|
10
|
+
"UPDATE_SUMMARIZATION_TEMPLATE",
|
|
11
|
+
"LONG_TERM_EXTRACTION_TEMPLATE",
|
|
12
|
+
]
|