@elizaos/python 2.0.0-alpha.3 → 2.0.0-alpha.31

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.
Files changed (167) hide show
  1. package/elizaos/__init__.py +0 -1
  2. package/elizaos/advanced_capabilities/__init__.py +6 -41
  3. package/elizaos/advanced_capabilities/actions/__init__.py +1 -21
  4. package/elizaos/advanced_capabilities/actions/add_contact.py +24 -13
  5. package/elizaos/advanced_capabilities/actions/follow_room.py +29 -29
  6. package/elizaos/advanced_capabilities/actions/image_generation.py +15 -28
  7. package/elizaos/advanced_capabilities/actions/mute_room.py +15 -28
  8. package/elizaos/advanced_capabilities/actions/remove_contact.py +17 -3
  9. package/elizaos/advanced_capabilities/actions/roles.py +17 -30
  10. package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +70 -15
  11. package/elizaos/advanced_capabilities/actions/search_contacts.py +17 -3
  12. package/elizaos/advanced_capabilities/actions/send_message.py +184 -51
  13. package/elizaos/advanced_capabilities/actions/settings.py +17 -3
  14. package/elizaos/advanced_capabilities/actions/unfollow_room.py +15 -28
  15. package/elizaos/advanced_capabilities/actions/unmute_room.py +15 -28
  16. package/elizaos/advanced_capabilities/actions/update_contact.py +17 -3
  17. package/elizaos/advanced_capabilities/actions/update_entity.py +17 -3
  18. package/elizaos/advanced_capabilities/evaluators/__init__.py +2 -9
  19. package/elizaos/advanced_capabilities/evaluators/reflection.py +3 -132
  20. package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +5 -201
  21. package/elizaos/advanced_capabilities/providers/__init__.py +1 -12
  22. package/elizaos/advanced_capabilities/providers/knowledge.py +23 -3
  23. package/elizaos/advanced_capabilities/services/__init__.py +2 -9
  24. package/elizaos/advanced_capabilities/services/rolodex.py +2 -2
  25. package/elizaos/advanced_memory/actions/reset_session.py +143 -0
  26. package/elizaos/advanced_memory/evaluators/reflection.py +134 -0
  27. package/elizaos/advanced_memory/evaluators/relationship_extraction.py +203 -0
  28. package/elizaos/advanced_memory/memory_service.py +69 -27
  29. package/elizaos/advanced_memory/plugin.py +2 -1
  30. package/elizaos/advanced_memory/test_advanced_memory.py +357 -0
  31. package/elizaos/advanced_memory/types.py +2 -2
  32. package/elizaos/advanced_planning/actions/schedule_follow_up.py +222 -0
  33. package/elizaos/advanced_planning/planning_service.py +26 -14
  34. package/elizaos/basic_capabilities/__init__.py +0 -2
  35. package/elizaos/basic_capabilities/providers/__init__.py +0 -3
  36. package/elizaos/basic_capabilities/providers/actions.py +118 -29
  37. package/elizaos/basic_capabilities/providers/agent_settings.py +64 -0
  38. package/elizaos/basic_capabilities/providers/character.py +19 -21
  39. package/elizaos/basic_capabilities/providers/contacts.py +79 -0
  40. package/elizaos/basic_capabilities/providers/current_time.py +7 -4
  41. package/elizaos/basic_capabilities/providers/facts.py +87 -0
  42. package/elizaos/basic_capabilities/providers/follow_ups.py +117 -0
  43. package/elizaos/basic_capabilities/providers/knowledge.py +96 -0
  44. package/elizaos/basic_capabilities/providers/recent_messages.py +5 -0
  45. package/elizaos/basic_capabilities/providers/relationships.py +113 -0
  46. package/elizaos/basic_capabilities/providers/roles.py +96 -0
  47. package/elizaos/basic_capabilities/providers/settings.py +56 -0
  48. package/elizaos/basic_capabilities/providers/time.py +7 -4
  49. package/elizaos/basic_capabilities/services/embedding.py +10 -7
  50. package/elizaos/basic_capabilities/services/task.py +3 -3
  51. package/elizaos/bootstrap/__init__.py +21 -2
  52. package/elizaos/bootstrap/actions/__init__.py +3 -0
  53. package/elizaos/bootstrap/actions/reset_session.py +3 -0
  54. package/elizaos/bootstrap/actions/roles.py +5 -4
  55. package/elizaos/bootstrap/actions/schedule_follow_up.py +65 -7
  56. package/elizaos/bootstrap/actions/send_message.py +162 -15
  57. package/elizaos/bootstrap/autonomy/__init__.py +5 -1
  58. package/elizaos/bootstrap/autonomy/action.py +161 -0
  59. package/elizaos/bootstrap/autonomy/evaluators.py +217 -0
  60. package/elizaos/bootstrap/autonomy/service.py +238 -28
  61. package/elizaos/bootstrap/plugin.py +7 -0
  62. package/elizaos/bootstrap/providers/actions.py +118 -27
  63. package/elizaos/bootstrap/providers/agent_settings.py +1 -0
  64. package/elizaos/bootstrap/providers/attachments.py +1 -0
  65. package/elizaos/bootstrap/providers/capabilities.py +1 -0
  66. package/elizaos/bootstrap/providers/character.py +1 -0
  67. package/elizaos/bootstrap/providers/choice.py +1 -0
  68. package/elizaos/bootstrap/providers/contacts.py +1 -0
  69. package/elizaos/bootstrap/providers/current_time.py +8 -2
  70. package/elizaos/bootstrap/providers/entities.py +1 -0
  71. package/elizaos/bootstrap/providers/evaluators.py +1 -0
  72. package/elizaos/bootstrap/providers/facts.py +1 -0
  73. package/elizaos/bootstrap/providers/follow_ups.py +1 -0
  74. package/elizaos/bootstrap/providers/knowledge.py +26 -3
  75. package/elizaos/bootstrap/providers/providers_list.py +1 -0
  76. package/elizaos/bootstrap/providers/recent_messages.py +5 -0
  77. package/elizaos/bootstrap/providers/relationships.py +20 -13
  78. package/elizaos/bootstrap/providers/roles.py +1 -0
  79. package/elizaos/bootstrap/providers/settings.py +1 -0
  80. package/elizaos/bootstrap/providers/time.py +8 -4
  81. package/elizaos/bootstrap/providers/world.py +1 -0
  82. package/elizaos/bootstrap/services/embedding.py +206 -8
  83. package/elizaos/bootstrap/services/rolodex.py +2 -2
  84. package/elizaos/bootstrap/services/task.py +3 -3
  85. package/elizaos/deterministic.py +193 -0
  86. package/elizaos/generated/__init__.py +1 -0
  87. package/elizaos/generated/action_docs.py +3181 -0
  88. package/elizaos/generated/spec_helpers.py +175 -0
  89. package/elizaos/media/mime.py +4 -4
  90. package/elizaos/media/search.py +23 -23
  91. package/elizaos/runtime.py +223 -64
  92. package/elizaos/services/hook_service.py +3 -3
  93. package/elizaos/services/message_service.py +175 -29
  94. package/elizaos/types/components.py +2 -2
  95. package/elizaos/types/generated/__init__.py +12 -0
  96. package/elizaos/types/generated/eliza/v1/agent_pb2.py +63 -0
  97. package/elizaos/types/generated/eliza/v1/agent_pb2.pyi +159 -0
  98. package/elizaos/types/generated/eliza/v1/components_pb2.py +65 -0
  99. package/elizaos/types/generated/eliza/v1/components_pb2.pyi +160 -0
  100. package/elizaos/types/generated/eliza/v1/database_pb2.py +78 -0
  101. package/elizaos/types/generated/eliza/v1/database_pb2.pyi +305 -0
  102. package/elizaos/types/generated/eliza/v1/environment_pb2.py +58 -0
  103. package/elizaos/types/generated/eliza/v1/environment_pb2.pyi +135 -0
  104. package/elizaos/types/generated/eliza/v1/events_pb2.py +82 -0
  105. package/elizaos/types/generated/eliza/v1/events_pb2.pyi +322 -0
  106. package/elizaos/types/generated/eliza/v1/ipc_pb2.py +113 -0
  107. package/elizaos/types/generated/eliza/v1/ipc_pb2.pyi +367 -0
  108. package/elizaos/types/generated/eliza/v1/knowledge_pb2.py +41 -0
  109. package/elizaos/types/generated/eliza/v1/knowledge_pb2.pyi +26 -0
  110. package/elizaos/types/generated/eliza/v1/memory_pb2.py +55 -0
  111. package/elizaos/types/generated/eliza/v1/memory_pb2.pyi +111 -0
  112. package/elizaos/types/generated/eliza/v1/message_service_pb2.py +48 -0
  113. package/elizaos/types/generated/eliza/v1/message_service_pb2.pyi +69 -0
  114. package/elizaos/types/generated/eliza/v1/messaging_pb2.py +51 -0
  115. package/elizaos/types/generated/eliza/v1/messaging_pb2.pyi +97 -0
  116. package/elizaos/types/generated/eliza/v1/model_pb2.py +84 -0
  117. package/elizaos/types/generated/eliza/v1/model_pb2.pyi +280 -0
  118. package/elizaos/types/generated/eliza/v1/payment_pb2.py +44 -0
  119. package/elizaos/types/generated/eliza/v1/payment_pb2.pyi +70 -0
  120. package/elizaos/types/generated/eliza/v1/plugin_pb2.py +68 -0
  121. package/elizaos/types/generated/eliza/v1/plugin_pb2.pyi +145 -0
  122. package/elizaos/types/generated/eliza/v1/primitives_pb2.py +48 -0
  123. package/elizaos/types/generated/eliza/v1/primitives_pb2.pyi +92 -0
  124. package/elizaos/types/generated/eliza/v1/prompts_pb2.py +52 -0
  125. package/elizaos/types/generated/eliza/v1/prompts_pb2.pyi +74 -0
  126. package/elizaos/types/generated/eliza/v1/service_interfaces_pb2.py +211 -0
  127. package/elizaos/types/generated/eliza/v1/service_interfaces_pb2.pyi +1296 -0
  128. package/elizaos/types/generated/eliza/v1/service_pb2.py +42 -0
  129. package/elizaos/types/generated/eliza/v1/service_pb2.pyi +69 -0
  130. package/elizaos/types/generated/eliza/v1/settings_pb2.py +58 -0
  131. package/elizaos/types/generated/eliza/v1/settings_pb2.pyi +85 -0
  132. package/elizaos/types/generated/eliza/v1/state_pb2.py +60 -0
  133. package/elizaos/types/generated/eliza/v1/state_pb2.pyi +114 -0
  134. package/elizaos/types/generated/eliza/v1/task_pb2.py +42 -0
  135. package/elizaos/types/generated/eliza/v1/task_pb2.pyi +58 -0
  136. package/elizaos/types/generated/eliza/v1/tee_pb2.py +52 -0
  137. package/elizaos/types/generated/eliza/v1/tee_pb2.pyi +90 -0
  138. package/elizaos/types/generated/eliza/v1/testing_pb2.py +39 -0
  139. package/elizaos/types/generated/eliza/v1/testing_pb2.pyi +23 -0
  140. package/elizaos/types/model.py +33 -3
  141. package/elizaos/types/primitives.py +3 -3
  142. package/elizaos/types/runtime.py +17 -3
  143. package/elizaos/types/state.py +2 -2
  144. package/elizaos/utils/streaming.py +3 -3
  145. package/elizaos/utils/validation.py +76 -0
  146. package/package.json +4 -3
  147. package/pyproject.toml +1 -2
  148. package/requirements-dev.lock +2 -2
  149. package/requirements.in +1 -2
  150. package/requirements.lock +2 -2
  151. package/tests/test_action_parameters.py +2 -3
  152. package/tests/test_actions_provider_examples.py +58 -1
  153. package/tests/test_advanced_memory_behavior.py +0 -2
  154. package/tests/test_advanced_memory_flag.py +0 -2
  155. package/tests/test_advanced_planning_behavior.py +11 -5
  156. package/tests/test_async_embedding.py +124 -0
  157. package/tests/test_autonomy.py +24 -3
  158. package/tests/test_history_compaction.py +104 -0
  159. package/tests/test_memory_bounds.py +115 -0
  160. package/tests/test_runtime.py +8 -17
  161. package/tests/test_schedule_follow_up_action.py +260 -0
  162. package/tests/test_send_message_action_targets.py +114 -0
  163. package/tests/test_settings_crypto.py +0 -2
  164. package/tests/test_validation.py +141 -0
  165. package/tests/verify_memory_architecture.py +192 -0
  166. package/uv.lock +1565 -0
  167. package/elizaos/basic_capabilities/providers/capabilities.py +0 -62
@@ -0,0 +1,117 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import UTC, datetime
4
+ from typing import TYPE_CHECKING
5
+
6
+ from elizaos.generated.spec_helpers import require_provider_spec
7
+ from elizaos.types import Provider, ProviderResult
8
+
9
+ if TYPE_CHECKING:
10
+ from elizaos.types import IAgentRuntime, Memory, State
11
+
12
+ # Get text content from centralized specs
13
+ _spec = require_provider_spec("FOLLOW_UPS")
14
+
15
+
16
+ async def get_follow_ups_context(
17
+ runtime: IAgentRuntime,
18
+ message: Memory,
19
+ state: State | None = None,
20
+ ) -> ProviderResult:
21
+ from elizaos.bootstrap.services.follow_up import FollowUpService
22
+
23
+ follow_up_service = runtime.get_service("follow_up")
24
+ if not follow_up_service or not isinstance(follow_up_service, FollowUpService):
25
+ return ProviderResult(text="", values={}, data={})
26
+
27
+ upcoming = await follow_up_service.get_upcoming_follow_ups(days_ahead=7, include_overdue=True)
28
+
29
+ if not upcoming:
30
+ return ProviderResult(
31
+ text="No upcoming follow-ups scheduled.", values={"followUpCount": 0}, data={}
32
+ )
33
+
34
+ now = datetime.now(UTC)
35
+
36
+ overdue_items: list[dict[str, str]] = []
37
+ upcoming_items: list[dict[str, str]] = []
38
+
39
+ for task in upcoming:
40
+ scheduled = datetime.fromisoformat(task.scheduled_at.replace("Z", "+00:00"))
41
+ entity = await runtime.get_entity(str(task.entity_id))
42
+ name = entity.name if entity and entity.name else "Unknown"
43
+
44
+ item = {
45
+ "entityId": str(task.entity_id),
46
+ "name": name,
47
+ "scheduledAt": task.scheduled_at,
48
+ "reason": task.reason,
49
+ "priority": task.priority,
50
+ }
51
+
52
+ if scheduled < now:
53
+ days_overdue = (now - scheduled).days
54
+ item["daysOverdue"] = str(days_overdue)
55
+ overdue_items.append(item)
56
+ else:
57
+ days_until = (scheduled - now).days
58
+ item["daysUntil"] = str(days_until)
59
+ upcoming_items.append(item)
60
+
61
+ text_summary = f"You have {len(upcoming)} follow-up(s) scheduled:\n"
62
+
63
+ if overdue_items:
64
+ text_summary += f"\nOverdue ({len(overdue_items)}):\n"
65
+ for item in overdue_items:
66
+ text_summary += f"- {item['name']} ({item['daysOverdue']} days overdue)"
67
+ if item.get("reason"):
68
+ text_summary += f" - {item['reason']}"
69
+ text_summary += "\n"
70
+
71
+ if upcoming_items:
72
+ text_summary += f"\nUpcoming ({len(upcoming_items)}):\n"
73
+ for item in upcoming_items:
74
+ days = int(item["daysUntil"])
75
+ if days == 0:
76
+ time_str = "today"
77
+ elif days == 1:
78
+ time_str = "tomorrow"
79
+ else:
80
+ time_str = f"in {days} days"
81
+ text_summary += f"- {item['name']} ({time_str})"
82
+ if item.get("reason"):
83
+ text_summary += f" - {item['reason']}"
84
+ text_summary += "\n"
85
+
86
+ suggestions = await follow_up_service.get_follow_up_suggestions()
87
+ if suggestions:
88
+ text_summary += "\nSuggested follow-ups:\n"
89
+ for s in suggestions[:3]:
90
+ text_summary += (
91
+ f"- {s.entity_name} ({s.days_since_last_contact} days since last contact)\n"
92
+ )
93
+
94
+ return ProviderResult(
95
+ text=text_summary.strip(),
96
+ values={
97
+ "followUpCount": len(upcoming),
98
+ "overdueCount": len(overdue_items),
99
+ "upcomingCount": len(upcoming_items),
100
+ "suggestionsCount": len(suggestions),
101
+ },
102
+ data={
103
+ "followUpCount": len(upcoming),
104
+ "overdueCount": len(overdue_items),
105
+ "upcomingCount": len(upcoming_items),
106
+ "suggestionsCount": len(suggestions),
107
+ },
108
+ )
109
+
110
+
111
+ follow_ups_provider = Provider(
112
+ name=_spec["name"],
113
+ description=_spec["description"],
114
+ get=get_follow_ups_context,
115
+ dynamic=_spec.get("dynamic", True),
116
+ position=_spec.get("position"),
117
+ )
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.generated.spec_helpers import require_provider_spec
6
+ from elizaos.types import Provider, ProviderResult
7
+
8
+ if TYPE_CHECKING:
9
+ from elizaos.types import IAgentRuntime, Memory, State
10
+
11
+ # Get text content from centralized specs
12
+ _spec = require_provider_spec("KNOWLEDGE")
13
+
14
+
15
+ async def get_knowledge_context(
16
+ runtime: IAgentRuntime,
17
+ message: Memory,
18
+ state: State | None = None,
19
+ ) -> ProviderResult:
20
+ sections: list[str] = []
21
+ knowledge_entries: list[dict[str, str]] = []
22
+
23
+ query_text = ""
24
+ if message.content and message.content.text:
25
+ query_text = message.content.text
26
+
27
+ if not query_text:
28
+ return ProviderResult(
29
+ text="", values={"knowledgeCount": 0, "hasKnowledge": False}, data={"entries": []}
30
+ )
31
+
32
+ # 1. Fetch recent messages to get embeddings
33
+ recent_messages = await runtime.get_memories(
34
+ room_id=message.room_id, limit=5, table_name="messages"
35
+ )
36
+
37
+ # 2. Extract valid embeddings
38
+ embeddings = [m.embedding for m in recent_messages if m and m.embedding]
39
+
40
+ relevant_knowledge = []
41
+ # 3. Search using the most recent embedding if available
42
+ if embeddings:
43
+ primary_embedding = embeddings[0]
44
+ params = {
45
+ "tableName": "knowledge",
46
+ "roomId": str(message.room_id),
47
+ "embedding": primary_embedding,
48
+ "matchThreshold": 0.75,
49
+ "matchCount": 5,
50
+ "unique": True,
51
+ }
52
+ relevant_knowledge = await runtime.search_memories(params)
53
+ elif query_text:
54
+ # Fallback to search_knowledge if no embeddings found?
55
+ # TS implementation might rely on search_memories logic handling missing embedding?
56
+ # No, TS skips if no embedding.
57
+ pass
58
+
59
+ for entry in relevant_knowledge:
60
+ if entry.content and entry.content.text:
61
+ knowledge_text = entry.content.text
62
+ if len(knowledge_text) > 500:
63
+ knowledge_text = knowledge_text[:500] + "..."
64
+
65
+ entry_dict = {
66
+ "id": str(entry.id) if entry.id else "",
67
+ "text": knowledge_text,
68
+ "source": str(entry.metadata.get("source", "unknown"))
69
+ if entry.metadata
70
+ else "unknown",
71
+ }
72
+ knowledge_entries.append(entry_dict)
73
+ sections.append(f"- {knowledge_text}")
74
+
75
+ context_text = "# Relevant Knowledge\n" + "\n".join(sections) if sections else ""
76
+
77
+ return ProviderResult(
78
+ text=context_text,
79
+ values={
80
+ "knowledgeCount": len(knowledge_entries),
81
+ "hasKnowledge": len(knowledge_entries) > 0,
82
+ },
83
+ data={
84
+ "entries": knowledge_entries,
85
+ "query": query_text,
86
+ },
87
+ )
88
+
89
+
90
+ knowledge_provider = Provider(
91
+ name=_spec["name"],
92
+ description=_spec["description"],
93
+ get=get_knowledge_context,
94
+ dynamic=_spec.get("dynamic", True),
95
+ position=_spec.get("position"),
96
+ )
@@ -25,12 +25,17 @@ async def get_recent_messages_context(
25
25
 
26
26
  sections: list[str] = []
27
27
  message_list: list[dict[str, str | int]] = []
28
+ room = await runtime.get_room(room_id)
29
+ last_compaction_at = None
30
+ if room and getattr(room, "metadata", None):
31
+ last_compaction_at = room.metadata.get("lastCompactionAt")
28
32
 
29
33
  recent_messages = await runtime.get_memories(
30
34
  room_id=room_id,
31
35
  limit=20,
32
36
  order_by="created_at",
33
37
  order_direction="desc",
38
+ start=last_compaction_at,
34
39
  )
35
40
 
36
41
  recent_messages = list(reversed(recent_messages))
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.generated.spec_helpers import require_provider_spec
6
+ from elizaos.types import Provider, ProviderResult
7
+
8
+ if TYPE_CHECKING:
9
+ from elizaos.types import IAgentRuntime, Memory, State
10
+
11
+ # Get text content from centralized specs
12
+ _spec = require_provider_spec("RELATIONSHIPS")
13
+
14
+
15
+ def format_relationship(
16
+ relationship: dict[str, str | int | list[str] | dict[str, str]],
17
+ target_name: str,
18
+ ) -> str:
19
+ tags = relationship.get("tags", [])
20
+ tags_str = (", ".join(tags) if tags else "none") if isinstance(tags, list) else str(tags)
21
+
22
+ interactions = relationship.get("metadata", {})
23
+ interaction_count = interactions.get("interactions", 0) if isinstance(interactions, dict) else 0
24
+
25
+ return f"- {target_name}: tags=[{tags_str}], interactions={interaction_count}"
26
+
27
+
28
+ async def get_relationships(
29
+ runtime: IAgentRuntime,
30
+ message: Memory,
31
+ state: State | None = None,
32
+ ) -> ProviderResult:
33
+ entity_id = message.entity_id
34
+ if not entity_id:
35
+ return ProviderResult(
36
+ text="No relationships found.",
37
+ values={"relationshipCount": 0},
38
+ data={"relationships": []},
39
+ )
40
+
41
+ try:
42
+ relationships = await runtime.get_relationships({"entityId": str(entity_id)})
43
+ except Exception as e:
44
+ runtime.logger.debug(
45
+ f"Failed to get relationships: src=provider:relationships agentId={runtime.agent_id} error={e}"
46
+ )
47
+ relationships = []
48
+
49
+ if not relationships:
50
+ return ProviderResult(
51
+ text="No relationships found.",
52
+ values={"relationshipCount": 0},
53
+ data={"relationships": []},
54
+ )
55
+
56
+ def _get_interactions(relationship: object) -> int:
57
+ if isinstance(relationship, dict):
58
+ metadata = relationship.get("metadata", {})
59
+ if isinstance(metadata, dict):
60
+ value = metadata.get("interactions", 0)
61
+ return int(value) if isinstance(value, (int, float)) else 0
62
+ return 0
63
+
64
+ sorted_relationships = sorted(relationships, key=_get_interactions, reverse=True)[:30]
65
+
66
+ formatted_relationships: list[str] = []
67
+ entity_cache: dict[str, str] = {}
68
+ for rel in sorted_relationships:
69
+ if not isinstance(rel, dict):
70
+ continue
71
+ target_id = rel.get("targetEntityId")
72
+ if not target_id:
73
+ continue
74
+
75
+ target_id_str = str(target_id)
76
+ target_name = entity_cache.get(target_id_str)
77
+ if target_name is None:
78
+ target_entity = await runtime.get_entity(target_id_str)
79
+ target_name = target_entity.name if target_entity else target_id_str[:8]
80
+ entity_cache[target_id_str] = target_name
81
+
82
+ formatted_relationships.append(format_relationship(rel, target_name))
83
+
84
+ if not formatted_relationships:
85
+ return ProviderResult(
86
+ text="No relationships found.",
87
+ values={"relationshipCount": 0},
88
+ data={"relationships": []},
89
+ )
90
+
91
+ sender_name = message.content.sender_name if message.content else "Unknown"
92
+ text = f"# {runtime.character.name} has observed {sender_name} interacting with:\n" + "\n".join(
93
+ formatted_relationships
94
+ )
95
+
96
+ return ProviderResult(
97
+ text=text,
98
+ values={
99
+ "relationshipCount": len(sorted_relationships),
100
+ },
101
+ data={
102
+ "relationships": sorted_relationships,
103
+ },
104
+ )
105
+
106
+
107
+ relationships_provider = Provider(
108
+ name=_spec["name"],
109
+ description=_spec["description"],
110
+ get=get_relationships,
111
+ dynamic=_spec.get("dynamic", True),
112
+ position=_spec.get("position"),
113
+ )
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.generated.spec_helpers import require_provider_spec
6
+ from elizaos.types import Provider, ProviderResult
7
+
8
+ if TYPE_CHECKING:
9
+ from elizaos.types import IAgentRuntime, Memory, State
10
+
11
+ # Get text content from centralized specs
12
+ _spec = require_provider_spec("ROLES")
13
+
14
+
15
+ def format_role_info(entity_name: str, role: str) -> str:
16
+ return f"- {entity_name}: {role}"
17
+
18
+
19
+ async def get_roles(
20
+ runtime: IAgentRuntime,
21
+ message: Memory,
22
+ state: State | None = None,
23
+ ) -> ProviderResult:
24
+ role_info: list[dict[str, str]] = []
25
+
26
+ world_id = None
27
+ if state and hasattr(state, "world"):
28
+ world = state.world
29
+ if world and hasattr(world, "id"):
30
+ world_id = world.id
31
+
32
+ if not world_id and message.room_id:
33
+ room = await runtime.get_room(message.room_id)
34
+ if room and hasattr(room, "world_id"):
35
+ world_id = room.world_id
36
+
37
+ if world_id:
38
+ world = await runtime.get_world(world_id)
39
+ if world and hasattr(world, "metadata"):
40
+ roles = world.metadata.get("roles", {})
41
+ if isinstance(roles, dict):
42
+ for entity_id, role in roles.items():
43
+ entity = await runtime.get_entity(entity_id)
44
+ entity_name = entity.name if entity else str(entity_id)[:8]
45
+
46
+ role_info.append(
47
+ {
48
+ "entityId": str(entity_id),
49
+ "entityName": entity_name,
50
+ "role": str(role),
51
+ }
52
+ )
53
+
54
+ if message.entity_id:
55
+ entity = await runtime.get_entity(message.entity_id)
56
+ if entity and hasattr(entity, "metadata"):
57
+ sender_role = entity.metadata.get("role")
58
+ if sender_role:
59
+ existing = next(
60
+ (r for r in role_info if r["entityId"] == str(message.entity_id)), None
61
+ )
62
+ if not existing:
63
+ role_info.append(
64
+ {
65
+ "entityId": str(message.entity_id),
66
+ "entityName": entity.name or "Unknown",
67
+ "role": str(sender_role),
68
+ }
69
+ )
70
+
71
+ if not role_info:
72
+ return ProviderResult(text="", values={"roleCount": 0}, data={"roles": []})
73
+
74
+ formatted_roles = "\n".join(format_role_info(r["entityName"], r["role"]) for r in role_info)
75
+
76
+ text = f"# Entity Roles\n{formatted_roles}"
77
+
78
+ return ProviderResult(
79
+ text=text,
80
+ values={
81
+ "roleCount": len(role_info),
82
+ "roles": {r["entityName"]: r["role"] for r in role_info},
83
+ },
84
+ data={
85
+ "roles": role_info,
86
+ },
87
+ )
88
+
89
+
90
+ roles_provider = Provider(
91
+ name=_spec["name"],
92
+ description=_spec["description"],
93
+ get=get_roles,
94
+ dynamic=_spec.get("dynamic", True),
95
+ position=_spec.get("position"),
96
+ )
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.bootstrap.providers.agent_settings import SENSITIVE_KEY_PATTERNS
6
+ from elizaos.generated.spec_helpers import require_provider_spec
7
+ from elizaos.types import Provider, ProviderResult
8
+
9
+ if TYPE_CHECKING:
10
+ from elizaos.types import IAgentRuntime, Memory, State
11
+
12
+ # Get text content from centralized specs
13
+ _spec = require_provider_spec("SETTINGS")
14
+
15
+
16
+ async def get_settings_context(
17
+ runtime: IAgentRuntime,
18
+ message: Memory,
19
+ state: State | None = None,
20
+ ) -> ProviderResult:
21
+ _ = message, state
22
+
23
+ all_settings = runtime.get_all_settings()
24
+
25
+ safe_settings: dict[str, str] = {}
26
+ for key, value in all_settings.items():
27
+ if not any(pattern in key.lower() for pattern in SENSITIVE_KEY_PATTERNS):
28
+ safe_settings[key] = str(value)
29
+
30
+ lines: list[str] = []
31
+ if safe_settings:
32
+ lines.append("## Current Configuration")
33
+ for key, value in safe_settings.items():
34
+ display_value = value if len(value) <= 50 else value[:50] + "..."
35
+ lines.append(f"- {key}: {display_value}")
36
+
37
+ context_text = "\n".join(lines) if lines else ""
38
+
39
+ return ProviderResult(
40
+ text=context_text,
41
+ values={
42
+ "settings": context_text,
43
+ "settingsCount": len(safe_settings),
44
+ "hasSettings": len(safe_settings) > 0,
45
+ },
46
+ data={"settings": safe_settings},
47
+ )
48
+
49
+
50
+ settings_provider = Provider(
51
+ name=_spec["name"],
52
+ description=_spec["description"],
53
+ get=get_settings_context,
54
+ dynamic=_spec.get("dynamic", True),
55
+ position=_spec.get("position"),
56
+ )
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from datetime import UTC, datetime
4
3
  from typing import TYPE_CHECKING
5
4
 
5
+ from elizaos.deterministic import get_prompt_reference_datetime
6
6
  from elizaos.generated.spec_helpers import require_provider_spec
7
7
  from elizaos.types import Provider, ProviderResult
8
8
 
@@ -18,9 +18,12 @@ async def get_time_context(
18
18
  message: Memory,
19
19
  state: State | None = None,
20
20
  ) -> ProviderResult:
21
- _ = runtime, message, state
22
-
23
- now = datetime.now(UTC)
21
+ now = get_prompt_reference_datetime(
22
+ runtime,
23
+ message,
24
+ state,
25
+ "provider:time",
26
+ )
24
27
  iso_string = now.isoformat()
25
28
  timestamp_ms = int(now.timestamp() * 1000)
26
29
  human_readable = now.strftime("%A, %B %d, %Y at %H:%M:%S UTC")
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections import OrderedDict
3
4
  from typing import TYPE_CHECKING
4
5
 
5
6
  from elizaos.types import ModelType, Service, ServiceType
@@ -18,7 +19,7 @@ class EmbeddingService(Service):
18
19
 
19
20
  def __init__(self) -> None:
20
21
  self._runtime: IAgentRuntime | None = None
21
- self._cache: dict[str, list[float]] = {}
22
+ self._cache: OrderedDict[str, list[float]] = OrderedDict()
22
23
  self._cache_enabled: bool = True
23
24
  self._max_cache_size: int = 1000
24
25
 
@@ -51,7 +52,9 @@ class EmbeddingService(Service):
51
52
  raise ValueError("Embedding service not started - no runtime available")
52
53
 
53
54
  if self._cache_enabled and text in self._cache:
54
- return self._cache[text]
55
+ embedding = self._cache.pop(text)
56
+ self._cache[text] = embedding
57
+ return embedding
55
58
 
56
59
  # Truncate to stay within embedding model token limits
57
60
  embed_text = text
@@ -87,9 +90,10 @@ class EmbeddingService(Service):
87
90
  return embeddings
88
91
 
89
92
  def _add_to_cache(self, text: str, embedding: list[float]) -> None:
90
- if len(self._cache) >= self._max_cache_size:
91
- oldest_key = next(iter(self._cache))
92
- del self._cache[oldest_key]
93
+ if text in self._cache:
94
+ self._cache.pop(text)
95
+ elif len(self._cache) >= self._max_cache_size:
96
+ self._cache.popitem(last=False)
93
97
  self._cache[text] = embedding
94
98
 
95
99
  def clear_cache(self) -> None:
@@ -105,8 +109,7 @@ class EmbeddingService(Service):
105
109
  raise ValueError("Cache size must be positive")
106
110
  self._max_cache_size = size
107
111
  while len(self._cache) > self._max_cache_size:
108
- oldest_key = next(iter(self._cache))
109
- del self._cache[oldest_key]
112
+ self._cache.popitem(last=False)
110
113
 
111
114
  async def similarity(self, text1: str, text2: str) -> float:
112
115
  embedding1 = await self.embed(text1)
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
4
  from datetime import UTC, datetime
5
- from enum import Enum
5
+ from enum import StrEnum
6
6
  from typing import TYPE_CHECKING
7
7
  from uuid import UUID, uuid4
8
8
 
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
12
12
  from elizaos.types import IAgentRuntime
13
13
 
14
14
 
15
- class TaskStatus(str, Enum):
15
+ class TaskStatus(StrEnum):
16
16
  PENDING = "PENDING"
17
17
  IN_PROGRESS = "IN_PROGRESS"
18
18
  COMPLETED = "COMPLETED"
@@ -20,7 +20,7 @@ class TaskStatus(str, Enum):
20
20
  CANCELLED = "CANCELLED"
21
21
 
22
22
 
23
- class TaskPriority(str, Enum):
23
+ class TaskPriority(StrEnum):
24
24
  LOW = "LOW"
25
25
  MEDIUM = "MEDIUM"
26
26
  HIGH = "HIGH"
@@ -1,7 +1,12 @@
1
1
  """elizaOS Bootstrap Plugin - Python implementation."""
2
2
 
3
- from .plugin import bootstrap_plugin, create_bootstrap_plugin
4
- from .types import CapabilityConfig
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from .plugin import bootstrap_plugin, create_bootstrap_plugin
9
+ from .types import CapabilityConfig
5
10
 
6
11
  __version__ = "2.0.0-alpha.0"
7
12
  __all__ = [
@@ -10,3 +15,17 @@ __all__ = [
10
15
  "CapabilityConfig",
11
16
  "__version__",
12
17
  ]
18
+
19
+
20
+ def __getattr__(name: str) -> object:
21
+ if name in {"bootstrap_plugin", "create_bootstrap_plugin"}:
22
+ from .plugin import bootstrap_plugin, create_bootstrap_plugin
23
+
24
+ if name == "bootstrap_plugin":
25
+ return bootstrap_plugin
26
+ return create_bootstrap_plugin
27
+ if name == "CapabilityConfig":
28
+ from .types import CapabilityConfig
29
+
30
+ return CapabilityConfig
31
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@@ -7,6 +7,7 @@ from .mute_room import mute_room_action
7
7
  from .none import none_action
8
8
  from .remove_contact import remove_contact_action
9
9
  from .reply import reply_action
10
+ from .reset_session import reset_session_action
10
11
  from .roles import update_role_action
11
12
  from .schedule_follow_up import schedule_follow_up_action
12
13
  from .search_contacts import search_contacts_action
@@ -26,6 +27,7 @@ __all__ = [
26
27
  "mute_room_action",
27
28
  "none_action",
28
29
  "remove_contact_action",
30
+ "reset_session_action",
29
31
  "reply_action",
30
32
  "schedule_follow_up_action",
31
33
  "search_contacts_action",
@@ -54,6 +56,7 @@ EXTENDED_ACTIONS = [
54
56
  generate_image_action,
55
57
  mute_room_action,
56
58
  remove_contact_action,
59
+ reset_session_action,
57
60
  schedule_follow_up_action,
58
61
  search_contacts_action,
59
62
  send_message_action,
@@ -0,0 +1,3 @@
1
+ from elizaos.advanced_memory.actions.reset_session import reset_session_action
2
+
3
+ __all__ = ["reset_session_action"]