@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.
Files changed (197) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +239 -0
  3. package/elizaos/__init__.py +280 -0
  4. package/elizaos/action_docs.py +149 -0
  5. package/elizaos/advanced_capabilities/__init__.py +85 -0
  6. package/elizaos/advanced_capabilities/actions/__init__.py +54 -0
  7. package/elizaos/advanced_capabilities/actions/add_contact.py +139 -0
  8. package/elizaos/advanced_capabilities/actions/follow_room.py +151 -0
  9. package/elizaos/advanced_capabilities/actions/image_generation.py +148 -0
  10. package/elizaos/advanced_capabilities/actions/mute_room.py +164 -0
  11. package/elizaos/advanced_capabilities/actions/remove_contact.py +145 -0
  12. package/elizaos/advanced_capabilities/actions/roles.py +207 -0
  13. package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +154 -0
  14. package/elizaos/advanced_capabilities/actions/search_contacts.py +145 -0
  15. package/elizaos/advanced_capabilities/actions/send_message.py +187 -0
  16. package/elizaos/advanced_capabilities/actions/settings.py +151 -0
  17. package/elizaos/advanced_capabilities/actions/unfollow_room.py +164 -0
  18. package/elizaos/advanced_capabilities/actions/unmute_room.py +164 -0
  19. package/elizaos/advanced_capabilities/actions/update_contact.py +164 -0
  20. package/elizaos/advanced_capabilities/actions/update_entity.py +161 -0
  21. package/elizaos/advanced_capabilities/evaluators/__init__.py +18 -0
  22. package/elizaos/advanced_capabilities/evaluators/reflection.py +134 -0
  23. package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +203 -0
  24. package/elizaos/advanced_capabilities/providers/__init__.py +36 -0
  25. package/elizaos/advanced_capabilities/providers/agent_settings.py +60 -0
  26. package/elizaos/advanced_capabilities/providers/contacts.py +77 -0
  27. package/elizaos/advanced_capabilities/providers/facts.py +82 -0
  28. package/elizaos/advanced_capabilities/providers/follow_ups.py +113 -0
  29. package/elizaos/advanced_capabilities/providers/knowledge.py +83 -0
  30. package/elizaos/advanced_capabilities/providers/relationships.py +112 -0
  31. package/elizaos/advanced_capabilities/providers/roles.py +97 -0
  32. package/elizaos/advanced_capabilities/providers/settings.py +51 -0
  33. package/elizaos/advanced_capabilities/services/__init__.py +18 -0
  34. package/elizaos/advanced_capabilities/services/follow_up.py +138 -0
  35. package/elizaos/advanced_capabilities/services/rolodex.py +244 -0
  36. package/elizaos/advanced_memory/__init__.py +3 -0
  37. package/elizaos/advanced_memory/evaluators.py +97 -0
  38. package/elizaos/advanced_memory/memory_service.py +556 -0
  39. package/elizaos/advanced_memory/plugin.py +30 -0
  40. package/elizaos/advanced_memory/prompts.py +12 -0
  41. package/elizaos/advanced_memory/providers.py +90 -0
  42. package/elizaos/advanced_memory/types.py +65 -0
  43. package/elizaos/advanced_planning/__init__.py +10 -0
  44. package/elizaos/advanced_planning/actions.py +145 -0
  45. package/elizaos/advanced_planning/message_classifier.py +127 -0
  46. package/elizaos/advanced_planning/planning_service.py +712 -0
  47. package/elizaos/advanced_planning/plugin.py +40 -0
  48. package/elizaos/advanced_planning/prompts.py +4 -0
  49. package/elizaos/basic_capabilities/__init__.py +66 -0
  50. package/elizaos/basic_capabilities/actions/__init__.py +24 -0
  51. package/elizaos/basic_capabilities/actions/choice.py +140 -0
  52. package/elizaos/basic_capabilities/actions/ignore.py +66 -0
  53. package/elizaos/basic_capabilities/actions/none.py +56 -0
  54. package/elizaos/basic_capabilities/actions/reply.py +120 -0
  55. package/elizaos/basic_capabilities/providers/__init__.py +54 -0
  56. package/elizaos/basic_capabilities/providers/action_state.py +113 -0
  57. package/elizaos/basic_capabilities/providers/actions.py +263 -0
  58. package/elizaos/basic_capabilities/providers/attachments.py +76 -0
  59. package/elizaos/basic_capabilities/providers/capabilities.py +62 -0
  60. package/elizaos/basic_capabilities/providers/character.py +113 -0
  61. package/elizaos/basic_capabilities/providers/choice.py +73 -0
  62. package/elizaos/basic_capabilities/providers/context_bench.py +44 -0
  63. package/elizaos/basic_capabilities/providers/current_time.py +58 -0
  64. package/elizaos/basic_capabilities/providers/entities.py +99 -0
  65. package/elizaos/basic_capabilities/providers/evaluators.py +54 -0
  66. package/elizaos/basic_capabilities/providers/providers_list.py +55 -0
  67. package/elizaos/basic_capabilities/providers/recent_messages.py +85 -0
  68. package/elizaos/basic_capabilities/providers/time.py +45 -0
  69. package/elizaos/basic_capabilities/providers/world.py +93 -0
  70. package/elizaos/basic_capabilities/services/__init__.py +18 -0
  71. package/elizaos/basic_capabilities/services/embedding.py +122 -0
  72. package/elizaos/basic_capabilities/services/task.py +178 -0
  73. package/elizaos/bootstrap/__init__.py +12 -0
  74. package/elizaos/bootstrap/actions/__init__.py +68 -0
  75. package/elizaos/bootstrap/actions/add_contact.py +149 -0
  76. package/elizaos/bootstrap/actions/choice.py +147 -0
  77. package/elizaos/bootstrap/actions/follow_room.py +151 -0
  78. package/elizaos/bootstrap/actions/ignore.py +80 -0
  79. package/elizaos/bootstrap/actions/image_generation.py +135 -0
  80. package/elizaos/bootstrap/actions/mute_room.py +151 -0
  81. package/elizaos/bootstrap/actions/none.py +71 -0
  82. package/elizaos/bootstrap/actions/remove_contact.py +159 -0
  83. package/elizaos/bootstrap/actions/reply.py +140 -0
  84. package/elizaos/bootstrap/actions/roles.py +193 -0
  85. package/elizaos/bootstrap/actions/schedule_follow_up.py +164 -0
  86. package/elizaos/bootstrap/actions/search_contacts.py +159 -0
  87. package/elizaos/bootstrap/actions/send_message.py +173 -0
  88. package/elizaos/bootstrap/actions/settings.py +165 -0
  89. package/elizaos/bootstrap/actions/unfollow_room.py +151 -0
  90. package/elizaos/bootstrap/actions/unmute_room.py +151 -0
  91. package/elizaos/bootstrap/actions/update_contact.py +178 -0
  92. package/elizaos/bootstrap/actions/update_entity.py +175 -0
  93. package/elizaos/bootstrap/autonomy/__init__.py +18 -0
  94. package/elizaos/bootstrap/autonomy/action.py +197 -0
  95. package/elizaos/bootstrap/autonomy/providers.py +165 -0
  96. package/elizaos/bootstrap/autonomy/routes.py +171 -0
  97. package/elizaos/bootstrap/autonomy/service.py +562 -0
  98. package/elizaos/bootstrap/autonomy/types.py +18 -0
  99. package/elizaos/bootstrap/evaluators/__init__.py +19 -0
  100. package/elizaos/bootstrap/evaluators/reflection.py +118 -0
  101. package/elizaos/bootstrap/evaluators/relationship_extraction.py +192 -0
  102. package/elizaos/bootstrap/plugin.py +140 -0
  103. package/elizaos/bootstrap/providers/__init__.py +80 -0
  104. package/elizaos/bootstrap/providers/action_state.py +71 -0
  105. package/elizaos/bootstrap/providers/actions.py +256 -0
  106. package/elizaos/bootstrap/providers/agent_settings.py +63 -0
  107. package/elizaos/bootstrap/providers/attachments.py +76 -0
  108. package/elizaos/bootstrap/providers/capabilities.py +66 -0
  109. package/elizaos/bootstrap/providers/character.py +128 -0
  110. package/elizaos/bootstrap/providers/choice.py +77 -0
  111. package/elizaos/bootstrap/providers/contacts.py +78 -0
  112. package/elizaos/bootstrap/providers/context_bench.py +49 -0
  113. package/elizaos/bootstrap/providers/current_time.py +56 -0
  114. package/elizaos/bootstrap/providers/entities.py +99 -0
  115. package/elizaos/bootstrap/providers/evaluators.py +58 -0
  116. package/elizaos/bootstrap/providers/facts.py +86 -0
  117. package/elizaos/bootstrap/providers/follow_ups.py +116 -0
  118. package/elizaos/bootstrap/providers/knowledge.py +73 -0
  119. package/elizaos/bootstrap/providers/providers_list.py +59 -0
  120. package/elizaos/bootstrap/providers/recent_messages.py +85 -0
  121. package/elizaos/bootstrap/providers/relationships.py +106 -0
  122. package/elizaos/bootstrap/providers/roles.py +95 -0
  123. package/elizaos/bootstrap/providers/settings.py +55 -0
  124. package/elizaos/bootstrap/providers/time.py +45 -0
  125. package/elizaos/bootstrap/providers/world.py +97 -0
  126. package/elizaos/bootstrap/services/__init__.py +26 -0
  127. package/elizaos/bootstrap/services/embedding.py +122 -0
  128. package/elizaos/bootstrap/services/follow_up.py +138 -0
  129. package/elizaos/bootstrap/services/rolodex.py +244 -0
  130. package/elizaos/bootstrap/services/task.py +585 -0
  131. package/elizaos/bootstrap/types.py +54 -0
  132. package/elizaos/bootstrap/utils/__init__.py +7 -0
  133. package/elizaos/bootstrap/utils/xml.py +69 -0
  134. package/elizaos/character.py +149 -0
  135. package/elizaos/logger.py +179 -0
  136. package/elizaos/media/__init__.py +45 -0
  137. package/elizaos/media/mime.py +315 -0
  138. package/elizaos/media/search.py +161 -0
  139. package/elizaos/media/tests/__init__.py +1 -0
  140. package/elizaos/media/tests/test_mime.py +117 -0
  141. package/elizaos/media/tests/test_search.py +156 -0
  142. package/elizaos/plugin.py +191 -0
  143. package/elizaos/prompts.py +1071 -0
  144. package/elizaos/py.typed +0 -0
  145. package/elizaos/runtime.py +2572 -0
  146. package/elizaos/services/__init__.py +49 -0
  147. package/elizaos/services/hook_service.py +511 -0
  148. package/elizaos/services/message_service.py +1248 -0
  149. package/elizaos/settings.py +182 -0
  150. package/elizaos/streaming_context.py +159 -0
  151. package/elizaos/trajectory_context.py +18 -0
  152. package/elizaos/types/__init__.py +512 -0
  153. package/elizaos/types/agent.py +31 -0
  154. package/elizaos/types/components.py +208 -0
  155. package/elizaos/types/database.py +64 -0
  156. package/elizaos/types/environment.py +46 -0
  157. package/elizaos/types/events.py +47 -0
  158. package/elizaos/types/memory.py +45 -0
  159. package/elizaos/types/model.py +393 -0
  160. package/elizaos/types/plugin.py +188 -0
  161. package/elizaos/types/primitives.py +100 -0
  162. package/elizaos/types/runtime.py +460 -0
  163. package/elizaos/types/service.py +113 -0
  164. package/elizaos/types/service_interfaces.py +244 -0
  165. package/elizaos/types/state.py +188 -0
  166. package/elizaos/types/task.py +29 -0
  167. package/elizaos/utils/__init__.py +108 -0
  168. package/elizaos/utils/spec_examples.py +48 -0
  169. package/elizaos/utils/streaming.py +426 -0
  170. package/elizaos_atropos_shared/__init__.py +1 -0
  171. package/elizaos_atropos_shared/canonical_eliza.py +282 -0
  172. package/package.json +19 -0
  173. package/pyproject.toml +143 -0
  174. package/requirements-dev.in +11 -0
  175. package/requirements-dev.lock +134 -0
  176. package/requirements.in +9 -0
  177. package/requirements.lock +64 -0
  178. package/tests/__init__.py +0 -0
  179. package/tests/test_action_parameters.py +154 -0
  180. package/tests/test_actions_provider_examples.py +39 -0
  181. package/tests/test_advanced_memory_behavior.py +96 -0
  182. package/tests/test_advanced_memory_flag.py +30 -0
  183. package/tests/test_advanced_planning_behavior.py +225 -0
  184. package/tests/test_advanced_planning_flag.py +26 -0
  185. package/tests/test_autonomy.py +445 -0
  186. package/tests/test_bootstrap_initialize.py +37 -0
  187. package/tests/test_character.py +163 -0
  188. package/tests/test_character_provider.py +231 -0
  189. package/tests/test_dynamic_prompt_exec.py +561 -0
  190. package/tests/test_logger_redaction.py +43 -0
  191. package/tests/test_plugin.py +117 -0
  192. package/tests/test_runtime.py +422 -0
  193. package/tests/test_salt_production_enforcement.py +22 -0
  194. package/tests/test_settings_crypto.py +118 -0
  195. package/tests/test_streaming.py +295 -0
  196. package/tests/test_types.py +221 -0
  197. package/tests/test_uuid_parity.py +46 -0
@@ -0,0 +1,85 @@
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("RECENT_MESSAGES")
13
+
14
+
15
+ async def get_recent_messages_context(
16
+ runtime: IAgentRuntime,
17
+ message: Memory,
18
+ state: State | None = None,
19
+ ) -> ProviderResult:
20
+ room_id = message.room_id
21
+ if not room_id:
22
+ return ProviderResult(
23
+ text="", values={"messageCount": 0, "hasHistory": False}, data={"messages": []}
24
+ )
25
+
26
+ sections: list[str] = []
27
+ message_list: list[dict[str, str | int]] = []
28
+
29
+ recent_messages = await runtime.get_memories(
30
+ room_id=room_id,
31
+ limit=20,
32
+ order_by="created_at",
33
+ order_direction="desc",
34
+ )
35
+
36
+ recent_messages = list(reversed(recent_messages))
37
+
38
+ for msg in recent_messages:
39
+ if not msg.content or not msg.content.text:
40
+ continue
41
+
42
+ sender_name = "Unknown"
43
+ if msg.entity_id:
44
+ entity = await runtime.get_entity(msg.entity_id)
45
+ if entity and entity.name:
46
+ sender_name = entity.name
47
+ elif str(msg.entity_id) == str(runtime.agent_id):
48
+ sender_name = runtime.character.name
49
+
50
+ message_text = msg.content.text
51
+ if len(message_text) > 300:
52
+ message_text = message_text[:300] + "..."
53
+
54
+ msg_dict = {
55
+ "id": str(msg.id) if msg.id else "",
56
+ "sender": sender_name,
57
+ "text": message_text,
58
+ "timestamp": msg.created_at or 0,
59
+ }
60
+ message_list.append(msg_dict)
61
+ sections.append(f"**{sender_name}**: {message_text}")
62
+
63
+ context_text = "# Recent Messages\n" + "\n".join(sections) if sections else ""
64
+
65
+ return ProviderResult(
66
+ text=context_text,
67
+ values={
68
+ "messageCount": len(message_list),
69
+ "hasHistory": len(message_list) > 0,
70
+ "roomId": str(room_id),
71
+ },
72
+ data={
73
+ "messages": message_list,
74
+ "roomId": str(room_id),
75
+ },
76
+ )
77
+
78
+
79
+ recent_messages_provider = Provider(
80
+ name=_spec["name"],
81
+ description=_spec["description"],
82
+ get=get_recent_messages_context,
83
+ position=_spec.get("position"),
84
+ dynamic=_spec.get("dynamic", True),
85
+ )
@@ -0,0 +1,106 @@
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(entity_id=entity_id)
43
+ except Exception as e:
44
+ runtime.logger.debug(
45
+ {"src": "provider:relationships", "agentId": runtime.agent_id, "error": str(e)},
46
+ "Failed to get relationships",
47
+ )
48
+ relationships = []
49
+
50
+ if not relationships:
51
+ return ProviderResult(
52
+ text="No relationships found.",
53
+ values={"relationshipCount": 0},
54
+ data={"relationships": []},
55
+ )
56
+
57
+ sorted_relationships = sorted(
58
+ relationships,
59
+ key=lambda r: (r.get("metadata", {}) or {}).get("interactions", 0),
60
+ reverse=True,
61
+ )[:30]
62
+
63
+ formatted_relationships: list[str] = []
64
+ entity_cache: dict[object, str] = {}
65
+ for rel in sorted_relationships:
66
+ target_id = rel.get("targetEntityId")
67
+ if not target_id:
68
+ continue
69
+
70
+ target_name = entity_cache.get(target_id)
71
+ if target_name is None:
72
+ target_entity = await runtime.get_entity(target_id)
73
+ target_name = target_entity.name if target_entity else str(target_id)[:8]
74
+ entity_cache[target_id] = target_name
75
+
76
+ formatted_relationships.append(format_relationship(rel, target_name))
77
+
78
+ if not formatted_relationships:
79
+ return ProviderResult(
80
+ text="No relationships found.",
81
+ values={"relationshipCount": 0},
82
+ data={"relationships": []},
83
+ )
84
+
85
+ sender_name = message.content.sender_name if message.content else "Unknown"
86
+ text = f"# {runtime.character.name} has observed {sender_name} interacting with:\n" + "\n".join(
87
+ formatted_relationships
88
+ )
89
+
90
+ return ProviderResult(
91
+ text=text,
92
+ values={
93
+ "relationshipCount": len(sorted_relationships),
94
+ },
95
+ data={
96
+ "relationships": sorted_relationships,
97
+ },
98
+ )
99
+
100
+
101
+ relationships_provider = Provider(
102
+ name=_spec["name"],
103
+ description=_spec["description"],
104
+ get=get_relationships,
105
+ dynamic=_spec.get("dynamic", True),
106
+ )
@@ -0,0 +1,95 @@
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
+ )
@@ -0,0 +1,55 @@
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
+ )
@@ -0,0 +1,45 @@
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("TIME")
14
+
15
+
16
+ async def get_time_context(
17
+ runtime: IAgentRuntime,
18
+ message: Memory,
19
+ state: State | None = None,
20
+ ) -> ProviderResult:
21
+ _ = runtime, message, state
22
+
23
+ now = datetime.now(UTC)
24
+ iso_string = now.isoformat()
25
+ timestamp_ms = int(now.timestamp() * 1000)
26
+ human_readable = now.strftime("%A, %B %d, %Y at %H:%M:%S UTC")
27
+
28
+ text = (
29
+ f"The current date and time is {human_readable}. "
30
+ "Please use this as your reference for any time-based operations or responses."
31
+ )
32
+
33
+ return ProviderResult(
34
+ text=text,
35
+ values={"time": human_readable},
36
+ data={"timestamp": timestamp_ms, "isoString": iso_string},
37
+ )
38
+
39
+
40
+ time_provider = Provider(
41
+ name=_spec["name"],
42
+ description=_spec["description"],
43
+ get=get_time_context,
44
+ dynamic=_spec.get("dynamic", True),
45
+ )
@@ -0,0 +1,97 @@
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("WORLD")
13
+
14
+
15
+ async def get_world_context(
16
+ runtime: IAgentRuntime,
17
+ message: Memory,
18
+ state: State | None = None,
19
+ ) -> ProviderResult:
20
+ room_id = message.room_id
21
+ if not room_id:
22
+ return ProviderResult(text="", values={"hasWorld": False}, data={})
23
+
24
+ room = await runtime.get_room(room_id)
25
+ if not room or not room.world_id:
26
+ return ProviderResult(
27
+ text="",
28
+ values={"hasWorld": False, "roomId": str(room_id)},
29
+ data={"roomId": str(room_id)},
30
+ )
31
+
32
+ world = await runtime.get_world(room.world_id)
33
+ if not world:
34
+ return ProviderResult(
35
+ text="",
36
+ values={"hasWorld": False, "roomId": str(room_id)},
37
+ data={"roomId": str(room_id)},
38
+ )
39
+
40
+ sections: list[str] = []
41
+
42
+ sections.append(f"# World: {world.name or 'Unknown'}")
43
+ if world.metadata and world.metadata.get("description"):
44
+ sections.append(f"\n{world.metadata['description']}")
45
+
46
+ sections.append(f"\n## Current Room: {room.name or 'Unknown'}")
47
+ if room.metadata and room.metadata.get("topic"):
48
+ sections.append(f"Topic: {room.metadata['topic']}")
49
+
50
+ member_count = 0
51
+ if world.metadata and world.metadata.get("members"):
52
+ member_count = len(world.metadata["members"])
53
+ sections.append(f"\n## Members: {member_count}")
54
+
55
+ if world.metadata and world.metadata.get("settings"):
56
+ safe_settings = {
57
+ k: v
58
+ for k, v in world.metadata["settings"].items()
59
+ if not k.lower().endswith(("key", "secret", "password", "token"))
60
+ }
61
+ if safe_settings:
62
+ settings_list = [f"- {k}: {v}" for k, v in safe_settings.items()]
63
+ sections.append("\n## Settings\n" + "\n".join(settings_list))
64
+
65
+ context_text = "\n".join(sections)
66
+
67
+ return ProviderResult(
68
+ text=context_text,
69
+ values={
70
+ "hasWorld": True,
71
+ "worldId": str(world.id),
72
+ "worldName": world.name or "Unknown",
73
+ "roomId": str(room_id),
74
+ "roomName": room.name or "Unknown",
75
+ "memberCount": member_count,
76
+ },
77
+ data={
78
+ "world": {
79
+ "id": str(world.id),
80
+ "name": world.name,
81
+ "metadata": world.metadata,
82
+ },
83
+ "room": {
84
+ "id": str(room_id),
85
+ "name": room.name,
86
+ "metadata": room.metadata,
87
+ },
88
+ },
89
+ )
90
+
91
+
92
+ world_provider = Provider(
93
+ name=_spec["name"],
94
+ description=_spec["description"],
95
+ get=get_world_context,
96
+ dynamic=_spec.get("dynamic", True),
97
+ )
@@ -0,0 +1,26 @@
1
+ from .embedding import EmbeddingService
2
+ from .follow_up import FollowUpService
3
+ from .rolodex import RolodexService
4
+ from .task import TaskService
5
+
6
+ __all__ = [
7
+ "EmbeddingService",
8
+ "FollowUpService",
9
+ "RolodexService",
10
+ "TaskService",
11
+ "BASIC_SERVICES",
12
+ "EXTENDED_SERVICES",
13
+ "ALL_SERVICES",
14
+ ]
15
+
16
+ BASIC_SERVICES = [
17
+ TaskService,
18
+ EmbeddingService,
19
+ ]
20
+
21
+ EXTENDED_SERVICES: list[type] = [
22
+ RolodexService,
23
+ FollowUpService,
24
+ ]
25
+
26
+ ALL_SERVICES = BASIC_SERVICES + EXTENDED_SERVICES
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.types import ModelType, Service, ServiceType
6
+
7
+ if TYPE_CHECKING:
8
+ from elizaos.types import IAgentRuntime
9
+
10
+
11
+ class EmbeddingService(Service):
12
+ name = "embedding"
13
+ service_type = ServiceType.UNKNOWN
14
+
15
+ @property
16
+ def capability_description(self) -> str:
17
+ return "Text embedding service for generating and caching text embeddings."
18
+
19
+ def __init__(self) -> None:
20
+ self._runtime: IAgentRuntime | None = None
21
+ self._cache: dict[str, list[float]] = {}
22
+ self._cache_enabled: bool = True
23
+ self._max_cache_size: int = 1000
24
+
25
+ @classmethod
26
+ async def start(cls, runtime: IAgentRuntime) -> EmbeddingService:
27
+ service = cls()
28
+ service._runtime = runtime
29
+ runtime.logger.info(
30
+ "Embedding service started",
31
+ src="service:embedding",
32
+ agentId=str(runtime.agent_id),
33
+ )
34
+ return service
35
+
36
+ async def stop(self) -> None:
37
+ if self._runtime:
38
+ self._runtime.logger.info(
39
+ "Embedding service stopped",
40
+ src="service:embedding",
41
+ agentId=str(self._runtime.agent_id),
42
+ )
43
+ self._cache.clear()
44
+ self._runtime = None
45
+
46
+ # Max characters for embedding input (~8K tokens at ~4 chars/token)
47
+ MAX_EMBEDDING_CHARS = 32_000
48
+
49
+ async def embed(self, text: str) -> list[float]:
50
+ if self._runtime is None:
51
+ raise ValueError("Embedding service not started - no runtime available")
52
+
53
+ if self._cache_enabled and text in self._cache:
54
+ return self._cache[text]
55
+
56
+ # Truncate to stay within embedding model token limits
57
+ embed_text = text
58
+ if len(embed_text) > self.MAX_EMBEDDING_CHARS:
59
+ self._runtime.logger.warning(
60
+ "Truncating embedding input from %d to %d chars",
61
+ len(embed_text),
62
+ self.MAX_EMBEDDING_CHARS,
63
+ src="service:embedding",
64
+ )
65
+ embed_text = embed_text[: self.MAX_EMBEDDING_CHARS]
66
+
67
+ embedding = await self._runtime.use_model(
68
+ ModelType.TEXT_EMBEDDING,
69
+ text=embed_text,
70
+ )
71
+
72
+ if not isinstance(embedding, list):
73
+ raise ValueError(f"Expected list for embedding, got {type(embedding)}")
74
+
75
+ embedding = [float(x) for x in embedding]
76
+
77
+ if self._cache_enabled:
78
+ self._add_to_cache(text, embedding)
79
+
80
+ return embedding
81
+
82
+ async def embed_batch(self, texts: list[str]) -> list[list[float]]:
83
+ embeddings: list[list[float]] = []
84
+ for text in texts:
85
+ embedding = await self.embed(text)
86
+ embeddings.append(embedding)
87
+ return embeddings
88
+
89
+ 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
+ self._cache[text] = embedding
94
+
95
+ def clear_cache(self) -> None:
96
+ self._cache.clear()
97
+
98
+ def set_cache_enabled(self, enabled: bool) -> None:
99
+ self._cache_enabled = enabled
100
+ if not enabled:
101
+ self._cache.clear()
102
+
103
+ def set_max_cache_size(self, size: int) -> None:
104
+ if size <= 0:
105
+ raise ValueError("Cache size must be positive")
106
+ self._max_cache_size = size
107
+ while len(self._cache) > self._max_cache_size:
108
+ oldest_key = next(iter(self._cache))
109
+ del self._cache[oldest_key]
110
+
111
+ async def similarity(self, text1: str, text2: str) -> float:
112
+ embedding1 = await self.embed(text1)
113
+ embedding2 = await self.embed(text2)
114
+
115
+ dot_product = sum(a * b for a, b in zip(embedding1, embedding2, strict=True))
116
+ magnitude1 = sum(a * a for a in embedding1) ** 0.5
117
+ magnitude2 = sum(b * b for b in embedding2) ** 0.5
118
+
119
+ if magnitude1 == 0 or magnitude2 == 0:
120
+ return 0.0
121
+
122
+ return dot_product / (magnitude1 * magnitude2)