@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,263 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from google.protobuf.json_format import MessageToDict
6
+
7
+ from elizaos.action_docs import get_canonical_action_example_calls
8
+ from elizaos.generated.spec_helpers import require_provider_spec
9
+ from elizaos.types import Provider, ProviderResult
10
+ from elizaos.types.components import ActionExample
11
+
12
+ if TYPE_CHECKING:
13
+ from elizaos.types import (
14
+ Action,
15
+ ActionParameter,
16
+ ActionParameterSchema,
17
+ IAgentRuntime,
18
+ Memory,
19
+ State,
20
+ )
21
+
22
+ # Get text content from centralized specs
23
+ _spec = require_provider_spec("ACTIONS")
24
+
25
+
26
+ def format_action_names(actions: list[Action]) -> str:
27
+ return ", ".join(action.name for action in actions)
28
+
29
+
30
+ def _format_parameter_type(schema: ActionParameterSchema) -> str:
31
+ if schema.type == "number" and (schema.minimum is not None or schema.maximum is not None):
32
+ min_val = schema.minimum if schema.minimum is not None else "∞"
33
+ max_val = schema.maximum if schema.maximum is not None else "∞"
34
+ return f"number [{min_val}-{max_val}]"
35
+ return schema.type
36
+
37
+
38
+ def _get_param_schema(param: ActionParameter) -> object:
39
+ """Get schema from ActionParameter, handling both Pydantic and protobuf variants."""
40
+ return getattr(param, "schema_def", None) or getattr(param, "schema", None)
41
+
42
+
43
+ def _format_action_parameters(parameters: list[ActionParameter]) -> str:
44
+ lines: list[str] = []
45
+ for param in parameters:
46
+ schema = _get_param_schema(param)
47
+ if schema is None:
48
+ lines.append(f" - {param.name}: {param.description}")
49
+ continue
50
+ required_str = " (required)" if param.required else " (optional)"
51
+ type_str = _format_parameter_type(schema)
52
+ default_val = getattr(schema, "default", None) or getattr(schema, "default_value", None)
53
+ default_str = f" [default: {default_val}]" if default_val else ""
54
+ enum_vals = getattr(schema, "enum", None) or getattr(schema, "enum_values", None)
55
+ enum_str = f" [values: {', '.join(enum_vals)}]" if enum_vals else ""
56
+ examples_str = (
57
+ f" [examples: {', '.join(repr(v) for v in param.examples)}]"
58
+ if getattr(param, "examples", None)
59
+ else ""
60
+ )
61
+ lines.append(
62
+ f" - {param.name}{required_str}: {param.description} ({type_str}{enum_str}{default_str}{examples_str})"
63
+ )
64
+ return "\n".join(lines)
65
+
66
+
67
+ def format_actions(actions: list[Action]) -> str:
68
+ lines: list[str] = []
69
+ for action in actions:
70
+ line = f"- **{action.name}**: {action.description or 'No description'}"
71
+ if action.parameters:
72
+ params_text = _format_action_parameters(action.parameters)
73
+ if params_text:
74
+ line += f"\n Parameters:\n{params_text}"
75
+ lines.append(line)
76
+ return "\n".join(lines)
77
+
78
+
79
+ def _replace_name_placeholders(text: str) -> str:
80
+ names = ["Alex", "Jordan", "Sam", "Taylor", "Riley"]
81
+ for i, name in enumerate(names, start=1):
82
+ text = text.replace(f"{{{{name{i}}}}}", name)
83
+ return text
84
+
85
+
86
+ def format_action_examples(actions: list[Action], max_examples: int = 10) -> str:
87
+ """
88
+ Format a deterministic subset of action examples for prompt context.
89
+
90
+ Deterministic ordering is important to keep tests stable and avoid prompt churn.
91
+ """
92
+ if max_examples <= 0:
93
+ return ""
94
+
95
+ examples: list[list[ActionExample]] = []
96
+ for action in sorted(actions, key=lambda a: a.name):
97
+ if not action.examples:
98
+ continue
99
+ for ex in action.examples:
100
+ if isinstance(ex, list) and ex:
101
+ examples.append(ex)
102
+ if len(examples) >= max_examples:
103
+ break
104
+ if len(examples) >= max_examples:
105
+ break
106
+
107
+ if not examples:
108
+ return ""
109
+
110
+ blocks: list[str] = []
111
+ for ex in examples:
112
+ lines: list[str] = []
113
+ for msg in ex:
114
+ msg_text = msg.content.text if msg.content and msg.content.text else ""
115
+ lines.append(f"{msg.name}: {_replace_name_placeholders(msg_text)}")
116
+ blocks.append("\n".join(lines))
117
+
118
+ return "\n\n".join(blocks)
119
+
120
+
121
+ def _escape_xml_text(text: str) -> str:
122
+ return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
123
+
124
+
125
+ def format_action_call_examples(actions: list[Action], max_examples: int = 5) -> str:
126
+ """
127
+ Format canonical action-call examples (including optional <params> blocks).
128
+
129
+ Deterministic ordering is important to keep tests stable and avoid prompt churn.
130
+ """
131
+ if max_examples <= 0:
132
+ return ""
133
+
134
+ blocks: list[str] = []
135
+ for action in sorted(actions, key=lambda a: a.name):
136
+ calls = get_canonical_action_example_calls(action.name)
137
+ for call in calls:
138
+ user = call.get("user")
139
+ action_names = call.get("actions")
140
+ params = call.get("params")
141
+
142
+ if not isinstance(user, str) or not isinstance(action_names, list):
143
+ continue
144
+ if not all(isinstance(a, str) for a in action_names):
145
+ continue
146
+
147
+ actions_xml = "\n".join(
148
+ f" <action>{_escape_xml_text(a)}</action>" for a in action_names
149
+ )
150
+
151
+ params_xml = ""
152
+ if isinstance(params, dict):
153
+ blocks_xml: list[str] = []
154
+ for act_name, act_params in params.items():
155
+ if not isinstance(act_name, str) or not isinstance(act_params, dict):
156
+ continue
157
+ inner: list[str] = []
158
+ for k, v in act_params.items():
159
+ if not isinstance(k, str):
160
+ continue
161
+ if isinstance(v, str):
162
+ raw = v
163
+ elif v is None:
164
+ raw = "null"
165
+ elif isinstance(v, bool):
166
+ raw = "true" if v else "false"
167
+ elif isinstance(v, (int, float)):
168
+ raw = str(v)
169
+ else:
170
+ raw = repr(v)
171
+ inner.append(f" <{k}>{_escape_xml_text(raw)}</{k}>")
172
+ blocks_xml.append(f" <{act_name}>\n" + "\n".join(inner) + f"\n </{act_name}>")
173
+ if blocks_xml:
174
+ params_xml = "\n<params>\n" + "\n".join(blocks_xml) + "\n</params>"
175
+
176
+ blocks.append(
177
+ f"User: {user}\nAssistant:\n<actions>\n{actions_xml}\n</actions>{params_xml}"
178
+ )
179
+ if len(blocks) >= max_examples:
180
+ return "\n\n".join(blocks)
181
+
182
+ return "\n\n".join(blocks)
183
+
184
+
185
+ async def get_actions(
186
+ runtime: IAgentRuntime,
187
+ message: Memory,
188
+ state: State | None = None,
189
+ ) -> ProviderResult:
190
+ validated_actions: list[Action] = []
191
+
192
+ for action in runtime.actions:
193
+ # Support both validate and validate_fn for backwards compatibility
194
+ validate_fn = getattr(action, "validate", None) or getattr(action, "validate_fn", None)
195
+ if validate_fn:
196
+ is_valid = await validate_fn(runtime, message, state)
197
+ if is_valid:
198
+ validated_actions.append(action)
199
+ else:
200
+ # If no validation function, include the action
201
+ validated_actions.append(action)
202
+
203
+ action_names = format_action_names(validated_actions)
204
+ actions_text = format_actions(validated_actions)
205
+ examples_text = format_action_examples(validated_actions, max_examples=10)
206
+ call_examples_text = format_action_call_examples(validated_actions, max_examples=5)
207
+
208
+ text_parts: list[str] = [f"Possible response actions: {action_names}"]
209
+ if actions_text:
210
+ text_parts.append(f"# Available Actions\n{actions_text}")
211
+ if examples_text:
212
+ text_parts.append(f"# Action Examples\n{examples_text}")
213
+ if call_examples_text:
214
+ text_parts.append(f"# Action Call Examples (with <params>)\n{call_examples_text}")
215
+
216
+ return ProviderResult(
217
+ text="\n\n".join(text_parts),
218
+ values={
219
+ "actionNames": action_names,
220
+ "actionCount": len(validated_actions),
221
+ },
222
+ data={
223
+ "actions": [
224
+ {
225
+ "name": a.name,
226
+ "description": a.description,
227
+ "examples": [
228
+ [
229
+ {
230
+ "name": ex.name,
231
+ "content": MessageToDict(
232
+ ex.content, preserving_proto_field_name=False
233
+ ),
234
+ }
235
+ for ex in example
236
+ ]
237
+ for example in (a.examples or [])
238
+ ],
239
+ "parameters": [
240
+ {
241
+ "name": p.name,
242
+ "description": p.description,
243
+ "required": bool(p.required),
244
+ "examples": getattr(p, "examples", None) or [],
245
+ "schema": MessageToDict(p.schema, preserving_proto_field_name=False)
246
+ if hasattr(p, "schema") and p.schema.ByteSize() > 0
247
+ else (getattr(p, "schema_def", None) or None),
248
+ }
249
+ for p in (a.parameters or [])
250
+ ],
251
+ }
252
+ for a in validated_actions
253
+ ],
254
+ },
255
+ )
256
+
257
+
258
+ actions_provider = Provider(
259
+ name=_spec["name"],
260
+ description=_spec["description"],
261
+ get=get_actions,
262
+ position=_spec.get("position", -1),
263
+ )
@@ -0,0 +1,76 @@
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("ATTACHMENTS")
13
+
14
+
15
+ def format_attachment(attachment: dict[str, str]) -> str:
16
+ att_type = attachment.get("type", "unknown")
17
+ url = attachment.get("url", "")
18
+ title = attachment.get("title", "")
19
+ description = attachment.get("description", "")
20
+
21
+ parts = [f"- Type: {att_type}"]
22
+ if title:
23
+ parts.append(f" Title: {title}")
24
+ if description:
25
+ parts.append(f" Description: {description}")
26
+ if url:
27
+ parts.append(f" URL: {url}")
28
+
29
+ return "\n".join(parts)
30
+
31
+
32
+ async def get_attachments(
33
+ runtime: IAgentRuntime,
34
+ message: Memory,
35
+ state: State | None = None,
36
+ ) -> ProviderResult:
37
+ attachments: list[dict[str, str]] = []
38
+
39
+ if message.content and hasattr(message.content, "attachments"):
40
+ raw_attachments = message.content.attachments or []
41
+ for att in raw_attachments:
42
+ if isinstance(att, dict):
43
+ attachments.append(att)
44
+ elif hasattr(att, "__dict__"):
45
+ attachments.append(att.__dict__)
46
+
47
+ if not attachments:
48
+ return ProviderResult(
49
+ text="",
50
+ values={"hasAttachments": False, "attachmentCount": 0},
51
+ data={"attachments": []},
52
+ )
53
+
54
+ formatted_attachments = "\n".join(format_attachment(att) for att in attachments)
55
+
56
+ text = f"# Attachments ({len(attachments)})\n{formatted_attachments}"
57
+
58
+ return ProviderResult(
59
+ text=text,
60
+ values={
61
+ "hasAttachments": True,
62
+ "attachmentCount": len(attachments),
63
+ "attachmentTypes": list({att.get("type", "unknown") for att in attachments}),
64
+ },
65
+ data={
66
+ "attachments": attachments,
67
+ },
68
+ )
69
+
70
+
71
+ attachments_provider = Provider(
72
+ name=_spec["name"],
73
+ description=_spec["description"],
74
+ get=get_attachments,
75
+ dynamic=_spec.get("dynamic", True),
76
+ )
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.types import Provider, ProviderResult
6
+
7
+ if TYPE_CHECKING:
8
+ from elizaos.types import IAgentRuntime, Memory, State
9
+
10
+
11
+ async def get_capabilities(
12
+ runtime: IAgentRuntime,
13
+ message: Memory,
14
+ state: State | None = None,
15
+ ) -> ProviderResult:
16
+ model_types = ["TEXT_LARGE", "TEXT_SMALL", "TEXT_EMBEDDING", "IMAGE", "AUDIO"]
17
+ available_models = [mt for mt in model_types if runtime.has_model(mt)]
18
+ service_names = [s.name for s in runtime.services if hasattr(s, "name")]
19
+
20
+ features: list[str] = []
21
+ if runtime.get_setting("ENABLE_VOICE"):
22
+ features.append("voice")
23
+ if runtime.get_setting("ENABLE_VISION"):
24
+ features.append("vision")
25
+ if runtime.get_setting("ENABLE_MEMORY"):
26
+ features.append("long_term_memory")
27
+
28
+ capabilities: dict[str, list[str] | bool] = {
29
+ "models": available_models,
30
+ "services": service_names,
31
+ "features": features,
32
+ }
33
+
34
+ text_parts: list[str] = ["# Agent Capabilities"]
35
+
36
+ if available_models:
37
+ text_parts.append(f"Models: {', '.join(available_models)}")
38
+
39
+ if service_names:
40
+ text_parts.append(f"Services: {', '.join(service_names)}")
41
+
42
+ if features:
43
+ text_parts.append(f"Features: {', '.join(features)}")
44
+
45
+ return ProviderResult(
46
+ text="\n".join(text_parts),
47
+ values={
48
+ "modelCount": len(available_models),
49
+ "serviceCount": len(service_names),
50
+ "hasVoice": "voice" in features,
51
+ "hasVision": "vision" in features,
52
+ },
53
+ data=capabilities,
54
+ )
55
+
56
+
57
+ capabilities_provider = Provider(
58
+ name="CAPABILITIES",
59
+ description="Agent capabilities including models, services, and features",
60
+ get=get_capabilities,
61
+ dynamic=False,
62
+ )
@@ -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("CHARACTER")
13
+
14
+
15
+ def _resolve_name(text: str, name: str) -> str:
16
+ """Replace ``{{name}}`` placeholders with the character's name.
17
+
18
+ Supports character template files where the name is injected at render
19
+ time so changing the character's name doesn't require rewriting every
20
+ field.
21
+ """
22
+ return text.replace("{{name}}", name)
23
+
24
+
25
+ def _resolve_name_list(items: list[str], name: str) -> list[str]:
26
+ """Resolve ``{{name}}`` in every element of a string list."""
27
+ return [_resolve_name(s, name) for s in items]
28
+
29
+
30
+ async def get_character_context(
31
+ runtime: IAgentRuntime,
32
+ message: Memory,
33
+ state: State | None = None,
34
+ ) -> ProviderResult:
35
+ character = runtime.character
36
+ agent_name: str = character.name
37
+
38
+ sections: list[str] = []
39
+
40
+ sections.append(f"# Agent: {agent_name}")
41
+
42
+ if character.bio:
43
+ if isinstance(character.bio, str):
44
+ bio_text = _resolve_name(character.bio, agent_name)
45
+ else:
46
+ bio_text = "\n".join(_resolve_name_list(list(character.bio), agent_name))
47
+ sections.append(f"\n## Bio\n{bio_text}")
48
+
49
+ if character.adjectives:
50
+ adjectives = (
51
+ character.adjectives
52
+ if isinstance(character.adjectives, list)
53
+ else [character.adjectives]
54
+ )
55
+ resolved_adjectives = _resolve_name_list(adjectives, agent_name)
56
+ sections.append(f"\n## Personality Traits\n{', '.join(resolved_adjectives)}")
57
+
58
+ # lore is optional and may not exist on all Character instances
59
+ lore = getattr(character, "lore", None)
60
+ if lore:
61
+ if isinstance(lore, str):
62
+ lore_text = _resolve_name(lore, agent_name)
63
+ else:
64
+ lore_text = "\n".join(_resolve_name_list(list(lore), agent_name))
65
+ sections.append(f"\n## Background\n{lore_text}")
66
+
67
+ if character.topics:
68
+ topics = character.topics if isinstance(character.topics, list) else [character.topics]
69
+ resolved_topics = _resolve_name_list(topics, agent_name)
70
+ sections.append(f"\n## Knowledge Areas\n{', '.join(resolved_topics)}")
71
+
72
+ if character.style:
73
+ style_sections: list[str] = []
74
+ if character.style.all:
75
+ all_style = (
76
+ character.style.all
77
+ if isinstance(character.style.all, list)
78
+ else [character.style.all]
79
+ )
80
+ resolved_all = _resolve_name_list(all_style, agent_name)
81
+ style_sections.append(f"General: {', '.join(resolved_all)}")
82
+ if character.style.chat:
83
+ chat_style = (
84
+ character.style.chat
85
+ if isinstance(character.style.chat, list)
86
+ else [character.style.chat]
87
+ )
88
+ resolved_chat = _resolve_name_list(chat_style, agent_name)
89
+ style_sections.append(f"Chat: {', '.join(resolved_chat)}")
90
+ if character.style.post:
91
+ post_style = (
92
+ character.style.post
93
+ if isinstance(character.style.post, list)
94
+ else [character.style.post]
95
+ )
96
+ resolved_post = _resolve_name_list(post_style, agent_name)
97
+ style_sections.append(f"Posts: {', '.join(resolved_post)}")
98
+ if style_sections:
99
+ sections.append("\n## Communication Style\n" + "\n".join(style_sections))
100
+
101
+ context_text = "\n".join(sections)
102
+
103
+ # Note: Protobuf ProviderResult.data is a Struct which has limited type support.
104
+ # The text already contains all the information needed for the agent context.
105
+ return ProviderResult(text=context_text)
106
+
107
+
108
+ character_provider = Provider(
109
+ name=_spec["name"],
110
+ description=_spec["description"],
111
+ get=get_character_context,
112
+ dynamic=_spec.get("dynamic", False),
113
+ )
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.types import Provider, ProviderResult
6
+
7
+ if TYPE_CHECKING:
8
+ from elizaos.types import IAgentRuntime, Memory, State
9
+
10
+
11
+ def format_choice(index: int, choice: dict[str, str]) -> str:
12
+ label = choice.get("label", f"Option {index + 1}")
13
+ description = choice.get("description", "")
14
+ value = choice.get("value", str(index))
15
+
16
+ if description:
17
+ return f"{index + 1}. [{value}] {label}: {description}"
18
+ return f"{index + 1}. [{value}] {label}"
19
+
20
+
21
+ async def get_choice_options(
22
+ runtime: IAgentRuntime,
23
+ message: Memory,
24
+ state: State | None = None,
25
+ ) -> ProviderResult:
26
+ choices: list[dict[str, str]] = []
27
+
28
+ if message.content and hasattr(message.content, "choices"):
29
+ raw_choices = message.content.choices or []
30
+ for choice in raw_choices:
31
+ if isinstance(choice, dict):
32
+ choices.append(choice)
33
+ elif isinstance(choice, str):
34
+ choices.append({"label": choice, "value": choice})
35
+
36
+ if state and hasattr(state, "choices"):
37
+ state_choices = state.choices or []
38
+ for choice in state_choices:
39
+ if isinstance(choice, dict):
40
+ choices.append(choice)
41
+ elif isinstance(choice, str):
42
+ choices.append({"label": choice, "value": choice})
43
+
44
+ if not choices:
45
+ return ProviderResult(
46
+ text="",
47
+ values={"hasChoices": False, "choiceCount": 0},
48
+ data={"choices": []},
49
+ )
50
+
51
+ formatted_choices = "\n".join(format_choice(i, choice) for i, choice in enumerate(choices))
52
+
53
+ text = f"# Available Choices\n{formatted_choices}"
54
+
55
+ return ProviderResult(
56
+ text=text,
57
+ values={
58
+ "hasChoices": True,
59
+ "choiceCount": len(choices),
60
+ "choiceLabels": [c.get("label", "") for c in choices],
61
+ },
62
+ data={
63
+ "choices": choices,
64
+ },
65
+ )
66
+
67
+
68
+ choice_provider = Provider(
69
+ name="CHOICE",
70
+ description="Available choice options for selection",
71
+ get=get_choice_options,
72
+ dynamic=True,
73
+ )
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.types import Provider, ProviderResult
6
+
7
+ if TYPE_CHECKING:
8
+ from elizaos.types import IAgentRuntime, Memory, State
9
+
10
+
11
+ async def get_context_bench(
12
+ runtime: IAgentRuntime,
13
+ message: Memory,
14
+ state: State | None = None,
15
+ ) -> ProviderResult:
16
+ _ = runtime
17
+ _ = state
18
+ meta = getattr(message, "metadata", None)
19
+ bench_ctx = None
20
+ if meta is not None:
21
+ maybe = getattr(meta, "benchmarkContext", None)
22
+ if isinstance(maybe, str) and maybe.strip():
23
+ bench_ctx = maybe.strip()
24
+
25
+ if not bench_ctx:
26
+ return ProviderResult(
27
+ text="",
28
+ values={"benchmark_has_context": False},
29
+ data={},
30
+ )
31
+
32
+ return ProviderResult(
33
+ text=f"# Benchmark Context\n{bench_ctx}",
34
+ values={"benchmark_has_context": True},
35
+ data={"benchmarkContext": bench_ctx},
36
+ )
37
+
38
+
39
+ context_bench_provider = Provider(
40
+ name="CONTEXT_BENCH",
41
+ description="Benchmark/task context injected by a benchmark harness",
42
+ get=get_context_bench,
43
+ dynamic=True,
44
+ )
@@ -0,0 +1,58 @@
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("CURRENT_TIME")
14
+
15
+
16
+ async def get_current_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
+
25
+ iso_timestamp = now.isoformat()
26
+ human_readable = now.strftime("%A, %B %d, %Y at %H:%M:%S UTC")
27
+ date_only = now.strftime("%Y-%m-%d")
28
+ time_only = now.strftime("%H:%M:%S")
29
+ day_of_week = now.strftime("%A")
30
+ unix_timestamp = int(now.timestamp())
31
+
32
+ context_text = f"# Current Time\n- Date: {date_only}\n- Time: {time_only} UTC\n- Day: {day_of_week}\n- Full: {human_readable}\n- ISO: {iso_timestamp}"
33
+
34
+ return ProviderResult(
35
+ text=context_text,
36
+ values={
37
+ "currentTime": iso_timestamp,
38
+ "currentDate": date_only,
39
+ "dayOfWeek": day_of_week,
40
+ "unixTimestamp": unix_timestamp,
41
+ },
42
+ data={
43
+ "iso": iso_timestamp,
44
+ "date": date_only,
45
+ "time": time_only,
46
+ "dayOfWeek": day_of_week,
47
+ "humanReadable": human_readable,
48
+ "unixTimestamp": unix_timestamp,
49
+ },
50
+ )
51
+
52
+
53
+ current_time_provider = Provider(
54
+ name=_spec["name"],
55
+ description=_spec["description"],
56
+ get=get_current_time_context,
57
+ dynamic=_spec.get("dynamic", True),
58
+ )