@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,164 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
5
+
6
+ from elizaos.generated.spec_helpers import require_action_spec
7
+ from elizaos.types import Action, ActionExample, ActionResult, Content
8
+
9
+ if TYPE_CHECKING:
10
+ from elizaos.types import HandlerCallback, HandlerOptions, IAgentRuntime, Memory, State
11
+
12
+ # Get text content from centralized specs
13
+ _spec = require_action_spec("UNMUTE_ROOM")
14
+
15
+
16
+ def _convert_spec_examples() -> list[list[ActionExample]]:
17
+ """Convert spec examples to ActionExample format."""
18
+ spec_examples = _spec.get("examples", [])
19
+ if not isinstance(spec_examples, list):
20
+ return []
21
+ result: list[list[ActionExample]] = []
22
+ for example in spec_examples:
23
+ if not isinstance(example, list):
24
+ continue
25
+ row: list[ActionExample] = []
26
+ for msg in example:
27
+ if not isinstance(msg, dict):
28
+ continue
29
+ content = msg.get("content", {})
30
+ text = ""
31
+ actions: list[str] | None = None
32
+ if isinstance(content, dict):
33
+ text_val = content.get("text", "")
34
+ text = str(text_val) if text_val else ""
35
+ actions_val = content.get("actions")
36
+ if isinstance(actions_val, list) and all(isinstance(a, str) for a in actions_val):
37
+ actions = list(actions_val)
38
+ row.append(
39
+ ActionExample(
40
+ name=str(msg.get("name", "")),
41
+ content=Content(text=text, actions=actions),
42
+ )
43
+ )
44
+ if row:
45
+ result.append(row)
46
+ return result
47
+
48
+
49
+ @dataclass
50
+ class UnmuteRoomAction:
51
+ name: str = _spec["name"]
52
+ similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
53
+ description: str = _spec["description"]
54
+
55
+ async def validate(
56
+ self, runtime: IAgentRuntime, message: Memory, _state: State | None = None
57
+ ) -> bool:
58
+ room_id = message.room_id
59
+ if not room_id:
60
+ return False
61
+
62
+ room = await runtime.get_room(room_id)
63
+ if room is None:
64
+ return False
65
+
66
+ world_id = room.world_id
67
+ if world_id:
68
+ world = await runtime.get_world(world_id)
69
+ if world and world.metadata:
70
+ muted_rooms = world.metadata.get("mutedRooms", [])
71
+ if str(room_id) in muted_rooms:
72
+ return True
73
+
74
+ return False
75
+
76
+ async def handler(
77
+ self,
78
+ runtime: IAgentRuntime,
79
+ message: Memory,
80
+ state: State | None = None,
81
+ options: HandlerOptions | None = None,
82
+ callback: HandlerCallback | None = None,
83
+ responses: list[Memory] | None = None,
84
+ ) -> ActionResult:
85
+ room_id = message.room_id
86
+ if not room_id:
87
+ return ActionResult(
88
+ text="No room specified to unmute",
89
+ values={"success": False, "error": "no_room_id"},
90
+ data={"actionName": "UNMUTE_ROOM"},
91
+ success=False,
92
+ )
93
+
94
+ room = await runtime.get_room(room_id)
95
+ if room is None:
96
+ return ActionResult(
97
+ text="Room not found",
98
+ values={"success": False, "error": "room_not_found"},
99
+ data={"actionName": "UNMUTE_ROOM"},
100
+ success=False,
101
+ )
102
+
103
+ room_name = str(room.name) if room.name else "Unknown Room"
104
+
105
+ world_id = room.world_id
106
+ if world_id:
107
+ world = await runtime.get_world(world_id)
108
+ if world and world.metadata:
109
+ muted_rooms = list(world.metadata.get("mutedRooms", []))
110
+ room_id_str = str(room_id)
111
+
112
+ if room_id_str in muted_rooms:
113
+ muted_rooms.remove(room_id_str)
114
+ world.metadata["mutedRooms"] = muted_rooms
115
+ await runtime.update_world(world)
116
+
117
+ await runtime.create_memory(
118
+ content=Content(
119
+ text=f"Unmuted room: {room_name}",
120
+ actions=["UNMUTE_ROOM"],
121
+ ),
122
+ room_id=room_id,
123
+ entity_id=runtime.agent_id,
124
+ memory_type="action",
125
+ metadata={"type": "UNMUTE_ROOM", "roomName": room_name},
126
+ )
127
+
128
+ response_content = Content(
129
+ text=f"I have unmuted {room_name}. I will now respond to messages there.",
130
+ actions=["UNMUTE_ROOM"],
131
+ )
132
+
133
+ if callback:
134
+ await callback(response_content)
135
+
136
+ return ActionResult(
137
+ text=f"Unmuted room: {room_name}",
138
+ values={
139
+ "success": True,
140
+ "unmuted": True,
141
+ "roomId": str(room_id),
142
+ "roomName": room_name,
143
+ },
144
+ data={
145
+ "actionName": "UNMUTE_ROOM",
146
+ "roomId": str(room_id),
147
+ "roomName": room_name,
148
+ },
149
+ success=True,
150
+ )
151
+
152
+ @property
153
+ def examples(self) -> list[list[ActionExample]]:
154
+ return _convert_spec_examples()
155
+
156
+
157
+ unmute_room_action = Action(
158
+ name=UnmuteRoomAction.name,
159
+ similes=UnmuteRoomAction().similes,
160
+ description=UnmuteRoomAction.description,
161
+ validate=UnmuteRoomAction().validate,
162
+ handler=UnmuteRoomAction().handler,
163
+ examples=UnmuteRoomAction().examples,
164
+ )
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
5
+
6
+ from elizaos.bootstrap.utils.xml import parse_key_value_xml
7
+ from elizaos.generated.spec_helpers import require_action_spec
8
+ from elizaos.prompts import UPDATE_CONTACT_TEMPLATE
9
+ from elizaos.types import (
10
+ Action,
11
+ ActionExample,
12
+ ActionResult,
13
+ Content,
14
+ ModelType,
15
+ )
16
+ from elizaos.utils.spec_examples import convert_spec_examples
17
+
18
+ if TYPE_CHECKING:
19
+ from elizaos.types import (
20
+ HandlerCallback,
21
+ HandlerOptions,
22
+ IAgentRuntime,
23
+ Memory,
24
+ State,
25
+ )
26
+
27
+ # Get text content from centralized specs
28
+ _spec = require_action_spec("UPDATE_CONTACT")
29
+
30
+
31
+ def _convert_spec_examples() -> list[list[ActionExample]]:
32
+ """Convert spec examples to ActionExample format."""
33
+ return convert_spec_examples(_spec)
34
+
35
+
36
+ @dataclass
37
+ class UpdateContactAction:
38
+ name: str = _spec["name"]
39
+ similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
40
+ description: str = _spec["description"]
41
+
42
+ async def validate(
43
+ self, runtime: IAgentRuntime, _message: Memory, _state: State | None = None
44
+ ) -> bool:
45
+ rolodex_service = runtime.get_service("rolodex")
46
+ return rolodex_service is not None
47
+
48
+ async def handler(
49
+ self,
50
+ runtime: IAgentRuntime,
51
+ message: Memory,
52
+ state: State | None = None,
53
+ options: HandlerOptions | None = None,
54
+ callback: HandlerCallback | None = None,
55
+ responses: list[Memory] | None = None,
56
+ ) -> ActionResult:
57
+ from elizaos.bootstrap.services.rolodex import RolodexService
58
+
59
+ rolodex_service = runtime.get_service("rolodex")
60
+ if not rolodex_service or not isinstance(rolodex_service, RolodexService):
61
+ return ActionResult(
62
+ text="Rolodex service not available",
63
+ success=False,
64
+ values={"error": True},
65
+ data={"error": "RolodexService not available"},
66
+ )
67
+
68
+ state = await runtime.compose_state(message, ["RECENT_MESSAGES", "ENTITIES"])
69
+
70
+ prompt = runtime.compose_prompt_from_state(
71
+ state=state,
72
+ template=UPDATE_CONTACT_TEMPLATE,
73
+ )
74
+
75
+ response = await runtime.use_model(ModelType.TEXT_SMALL, {"prompt": prompt})
76
+ parsed = parse_key_value_xml(response)
77
+
78
+ if not parsed or not parsed.get("contactName"):
79
+ return ActionResult(
80
+ text="Could not determine which contact to update",
81
+ success=False,
82
+ values={"error": True},
83
+ data={"error": "No contact name provided"},
84
+ )
85
+
86
+ contact_name = str(parsed.get("contactName", ""))
87
+ operation = str(parsed.get("operation", "replace"))
88
+
89
+ contacts = await rolodex_service.search_contacts(search_term=contact_name)
90
+
91
+ if not contacts:
92
+ return ActionResult(
93
+ text=f"Could not find a contact named '{contact_name}'",
94
+ success=False,
95
+ values={"error": True},
96
+ data={"error": "Contact not found"},
97
+ )
98
+
99
+ contact = contacts[0]
100
+
101
+ categories = None
102
+ tags = None
103
+
104
+ if parsed.get("categories"):
105
+ new_categories = [c.strip() for c in str(parsed["categories"]).split(",") if c.strip()]
106
+ if operation == "add_to" and contact.categories:
107
+ categories = list(set(contact.categories + new_categories))
108
+ else:
109
+ categories = new_categories
110
+
111
+ if parsed.get("tags"):
112
+ new_tags = [t.strip() for t in str(parsed["tags"]).split(",") if t.strip()]
113
+ if operation == "add_to" and contact.tags:
114
+ tags = list(set(contact.tags + new_tags))
115
+ else:
116
+ tags = new_tags
117
+
118
+ updated = await rolodex_service.update_contact(
119
+ entity_id=contact.entity_id,
120
+ categories=categories,
121
+ tags=tags,
122
+ )
123
+
124
+ if updated:
125
+ response_text = f"I've updated {contact_name}'s contact information."
126
+ if categories:
127
+ response_text += f" Categories: {', '.join(categories)}."
128
+ if tags:
129
+ response_text += f" Tags: {', '.join(tags)}."
130
+
131
+ if callback:
132
+ await callback(Content(text=response_text, actions=["UPDATE_CONTACT"]))
133
+
134
+ return ActionResult(
135
+ text=response_text,
136
+ success=True,
137
+ values={
138
+ "contactId": str(contact.entity_id),
139
+ "categoriesStr": ",".join(categories) if categories else "",
140
+ "tagsStr": ",".join(tags) if tags else "",
141
+ },
142
+ data={"success": True},
143
+ )
144
+ else:
145
+ return ActionResult(
146
+ text="Failed to update contact",
147
+ success=False,
148
+ values={"error": True},
149
+ data={"error": "Update operation failed"},
150
+ )
151
+
152
+ @property
153
+ def examples(self) -> list[list[ActionExample]]:
154
+ return _convert_spec_examples()
155
+
156
+
157
+ update_contact_action = Action(
158
+ name=UpdateContactAction.name,
159
+ similes=UpdateContactAction().similes,
160
+ description=UpdateContactAction.description,
161
+ validate=UpdateContactAction().validate,
162
+ handler=UpdateContactAction().handler,
163
+ examples=UpdateContactAction().examples,
164
+ )
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
5
+ from uuid import UUID
6
+
7
+ from elizaos.bootstrap.utils.xml import parse_key_value_xml
8
+ from elizaos.generated.spec_helpers import require_action_spec
9
+ from elizaos.prompts import UPDATE_ENTITY_TEMPLATE
10
+ from elizaos.types import Action, ActionExample, ActionResult, Content, ModelType
11
+ from elizaos.utils.spec_examples import convert_spec_examples
12
+
13
+ if TYPE_CHECKING:
14
+ from elizaos.types import HandlerCallback, HandlerOptions, IAgentRuntime, Memory, State
15
+
16
+ # Get text content from centralized specs
17
+ _spec = require_action_spec("UPDATE_ENTITY")
18
+
19
+
20
+ def _convert_spec_examples() -> list[list[ActionExample]]:
21
+ """Convert spec examples to ActionExample format."""
22
+ return convert_spec_examples(_spec)
23
+
24
+
25
+ @dataclass
26
+ class UpdateEntityAction:
27
+ name: str = _spec["name"]
28
+ similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
29
+ description: str = _spec["description"]
30
+
31
+ async def validate(
32
+ self, runtime: IAgentRuntime, message: Memory, _state: State | None = None
33
+ ) -> bool:
34
+ return message.entity_id is not None
35
+
36
+ async def handler(
37
+ self,
38
+ runtime: IAgentRuntime,
39
+ message: Memory,
40
+ state: State | None = None,
41
+ options: HandlerOptions | None = None,
42
+ callback: HandlerCallback | None = None,
43
+ responses: list[Memory] | None = None,
44
+ ) -> ActionResult:
45
+ if state is None:
46
+ raise ValueError("State is required for UPDATE_ENTITY action")
47
+
48
+ entity_id = message.entity_id
49
+ if not entity_id:
50
+ return ActionResult(
51
+ text="No entity specified to update",
52
+ values={"success": False, "error": "no_entity_id"},
53
+ data={"actionName": "UPDATE_ENTITY"},
54
+ success=False,
55
+ )
56
+
57
+ entity = await runtime.get_entity(entity_id)
58
+ if entity is None:
59
+ return ActionResult(
60
+ text="Entity not found",
61
+ values={"success": False, "error": "entity_not_found"},
62
+ data={"actionName": "UPDATE_ENTITY"},
63
+ success=False,
64
+ )
65
+
66
+ state = await runtime.compose_state(
67
+ message, ["RECENT_MESSAGES", "ACTION_STATE", "ENTITY_INFO"]
68
+ )
69
+
70
+ entity_info = f"""
71
+ Entity ID: {entity.id}
72
+ Name: {entity.name or "Unknown"}
73
+ Type: {entity.entity_type or "Unknown"}
74
+ """
75
+ if entity.metadata:
76
+ entity_info += f"Metadata: {entity.metadata}"
77
+
78
+ template = (
79
+ runtime.character.templates.get("updateEntityTemplate")
80
+ if runtime.character.templates and "updateEntityTemplate" in runtime.character.templates
81
+ else UPDATE_ENTITY_TEMPLATE
82
+ )
83
+ prompt = runtime.compose_prompt(state=state, template=template)
84
+ prompt = prompt.replace("{{entityInfo}}", entity_info)
85
+
86
+ response_text = await runtime.use_model(ModelType.TEXT_LARGE, prompt=prompt)
87
+ parsed_xml = parse_key_value_xml(response_text)
88
+
89
+ if parsed_xml is None:
90
+ raise ValueError("Failed to parse XML response")
91
+
92
+ thought = str(parsed_xml.get("thought", ""))
93
+ target_entity_id_str = str(parsed_xml.get("entity_id", str(entity_id)))
94
+
95
+ target_entity_id = UUID(target_entity_id_str)
96
+
97
+ updates_raw = parsed_xml.get("updates", {})
98
+ updated_fields: list[str] = []
99
+
100
+ if isinstance(updates_raw, dict):
101
+ field_list = updates_raw.get("field", [])
102
+ if isinstance(field_list, dict):
103
+ field_list = [field_list]
104
+ for field_update in field_list:
105
+ if isinstance(field_update, dict):
106
+ field_name = str(field_update.get("name", ""))
107
+ field_value = str(field_update.get("value", ""))
108
+ if field_name and field_value:
109
+ if entity.metadata is None:
110
+ entity.metadata = {}
111
+ entity.metadata[field_name] = field_value
112
+ updated_fields.append(field_name)
113
+
114
+ if not updated_fields:
115
+ return ActionResult(
116
+ text="No fields to update",
117
+ values={"success": True, "noChanges": True},
118
+ data={"actionName": "UPDATE_ENTITY", "thought": thought},
119
+ success=True,
120
+ )
121
+
122
+ await runtime.update_entity(entity)
123
+
124
+ response_content = Content(
125
+ text=f"Updated entity fields: {', '.join(updated_fields)}",
126
+ actions=["UPDATE_ENTITY"],
127
+ )
128
+
129
+ if callback:
130
+ await callback(response_content)
131
+
132
+ return ActionResult(
133
+ text=f"Updated entity: {', '.join(updated_fields)}",
134
+ values={
135
+ "success": True,
136
+ "entityUpdated": True,
137
+ "entityId": str(target_entity_id),
138
+ "updatedFields": ", ".join(updated_fields),
139
+ },
140
+ data={
141
+ "actionName": "UPDATE_ENTITY",
142
+ "entityId": str(target_entity_id),
143
+ "updatedFields": updated_fields,
144
+ "thought": thought,
145
+ },
146
+ success=True,
147
+ )
148
+
149
+ @property
150
+ def examples(self) -> list[list[ActionExample]]:
151
+ return _convert_spec_examples()
152
+
153
+
154
+ update_entity_action = Action(
155
+ name=UpdateEntityAction.name,
156
+ similes=UpdateEntityAction().similes,
157
+ description=UpdateEntityAction.description,
158
+ validate=UpdateEntityAction().validate,
159
+ handler=UpdateEntityAction().handler,
160
+ examples=UpdateEntityAction().examples,
161
+ )
@@ -0,0 +1,18 @@
1
+ """Advanced Evaluators - Extended evaluators for agent operation.
2
+
3
+ Evaluators that can be enabled with `advanced_capabilities=True`.
4
+ """
5
+
6
+ from .reflection import reflection_evaluator
7
+ from .relationship_extraction import relationship_extraction_evaluator
8
+
9
+ __all__ = [
10
+ "reflection_evaluator",
11
+ "relationship_extraction_evaluator",
12
+ "advanced_evaluators",
13
+ ]
14
+
15
+ advanced_evaluators = [
16
+ reflection_evaluator,
17
+ relationship_extraction_evaluator,
18
+ ]
@@ -0,0 +1,134 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Awaitable, Callable
4
+ from typing import TYPE_CHECKING
5
+
6
+ from elizaos.bootstrap.types import EvaluatorResult
7
+ from elizaos.bootstrap.utils.xml import parse_key_value_xml
8
+ from elizaos.generated.spec_helpers import require_evaluator_spec
9
+ from elizaos.prompts import REFLECTION_TEMPLATE
10
+ from elizaos.types import ActionResult, Evaluator, HandlerOptions, ModelType
11
+
12
+ if TYPE_CHECKING:
13
+ from elizaos.types import Content, IAgentRuntime, Memory, State
14
+
15
+ # Get text content from centralized specs
16
+ _spec = require_evaluator_spec("REFLECTION")
17
+
18
+
19
+ async def evaluate_reflection(
20
+ runtime: IAgentRuntime,
21
+ message: Memory,
22
+ state: State | None = None,
23
+ ) -> EvaluatorResult:
24
+ if state is None:
25
+ return EvaluatorResult(
26
+ score=50,
27
+ passed=True,
28
+ reason="No state for reflection",
29
+ details={},
30
+ )
31
+
32
+ recent_interactions: list[str] = []
33
+ room_id = message.room_id
34
+
35
+ if room_id:
36
+ recent_messages = await runtime.get_memories(
37
+ room_id=room_id,
38
+ limit=10,
39
+ order_by="created_at",
40
+ order_direction="desc",
41
+ )
42
+
43
+ for msg in recent_messages:
44
+ if msg.content and msg.content.text:
45
+ sender = "Unknown"
46
+ if msg.entity_id:
47
+ if str(msg.entity_id) == str(runtime.agent_id):
48
+ sender = runtime.character.name
49
+ else:
50
+ entity = await runtime.get_entity(msg.entity_id)
51
+ if entity and entity.name:
52
+ sender = entity.name
53
+ recent_interactions.append(f"{sender}: {msg.content.text}")
54
+
55
+ if not recent_interactions:
56
+ return EvaluatorResult(
57
+ score=50,
58
+ passed=True,
59
+ reason="No recent interactions to reflect on",
60
+ details={"noInteractions": True},
61
+ )
62
+
63
+ interactions_text = "\n".join(recent_interactions)
64
+
65
+ template = (
66
+ runtime.character.templates.get("reflectionTemplate")
67
+ if runtime.character.templates and "reflectionTemplate" in runtime.character.templates
68
+ else REFLECTION_TEMPLATE
69
+ )
70
+ prompt = runtime.compose_prompt(state=state, template=template)
71
+ prompt = prompt.replace("{{recentInteractions}}", interactions_text)
72
+
73
+ response_text = await runtime.use_model(ModelType.TEXT_LARGE, prompt=prompt)
74
+ parsed_xml = parse_key_value_xml(response_text)
75
+
76
+ if parsed_xml is None:
77
+ raise ValueError("Failed to parse reflection response")
78
+
79
+ quality_str = str(parsed_xml.get("quality_score", "50"))
80
+ quality_score = max(0, min(100, int(quality_str)))
81
+
82
+ thought = str(parsed_xml.get("thought", ""))
83
+ strengths = str(parsed_xml.get("strengths", ""))
84
+ improvements = str(parsed_xml.get("improvements", ""))
85
+ learnings = str(parsed_xml.get("learnings", ""))
86
+
87
+ passed = quality_score >= 50
88
+
89
+ return EvaluatorResult(
90
+ score=quality_score,
91
+ passed=passed,
92
+ reason=f"Strengths: {strengths}\nImprovements: {improvements}",
93
+ details={
94
+ "thought": thought,
95
+ "strengths": strengths,
96
+ "improvements": improvements,
97
+ "learnings": learnings,
98
+ "interactionCount": len(recent_interactions),
99
+ },
100
+ )
101
+
102
+
103
+ async def validate_reflection(
104
+ runtime: IAgentRuntime,
105
+ message: Memory,
106
+ _state: State | None = None,
107
+ ) -> bool:
108
+ return True
109
+
110
+
111
+ async def _reflection_handler(
112
+ runtime: IAgentRuntime,
113
+ message: Memory,
114
+ state: State | None = None,
115
+ options: HandlerOptions | None = None,
116
+ callback: Callable[[Content], Awaitable[list[Memory]]] | None = None,
117
+ responses: list[Memory] | None = None,
118
+ ) -> ActionResult | None:
119
+ """Wrapper handler that matches the expected signature."""
120
+ _ = options, callback, responses # Unused parameters
121
+ result = await evaluate_reflection(runtime, message, state)
122
+ # Return ActionResult - evaluators don't typically return action results
123
+ return ActionResult(text=result.reason, success=result.passed, values={}, data={})
124
+
125
+
126
+ reflection_evaluator = Evaluator(
127
+ name=str(_spec["name"]),
128
+ description=str(_spec["description"]),
129
+ similes=list(_spec.get("similes", [])) if _spec.get("similes") else [],
130
+ validate=validate_reflection,
131
+ handler=_reflection_handler,
132
+ always_run=bool(_spec.get("alwaysRun", False)),
133
+ examples=[],
134
+ )