@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,256 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from elizaos.action_docs import get_canonical_action_example_calls
6
+ from elizaos.generated.spec_helpers import require_provider_spec
7
+ from elizaos.types import Provider, ProviderResult
8
+ from elizaos.types.components import ActionExample
9
+
10
+ if TYPE_CHECKING:
11
+ from elizaos.types import (
12
+ Action,
13
+ ActionParameter,
14
+ ActionParameterSchema,
15
+ IAgentRuntime,
16
+ Memory,
17
+ State,
18
+ )
19
+
20
+ # Get text content from centralized specs
21
+ _spec = require_provider_spec("ACTIONS")
22
+
23
+
24
+ def format_action_names(actions: list[Action]) -> str:
25
+ return ", ".join(action.name for action in actions)
26
+
27
+
28
+ def _format_parameter_type(schema: ActionParameterSchema) -> str:
29
+ if schema.type == "number" and (schema.minimum is not None or schema.maximum is not None):
30
+ min_val = schema.minimum if schema.minimum is not None else "∞"
31
+ max_val = schema.maximum if schema.maximum is not None else "∞"
32
+ return f"number [{min_val}-{max_val}]"
33
+ return schema.type
34
+
35
+
36
+ def _get_param_schema(param: ActionParameter) -> object:
37
+ """Get schema from ActionParameter, handling both Pydantic and protobuf variants."""
38
+ return getattr(param, "schema_def", None) or getattr(param, "schema", None)
39
+
40
+
41
+ def _format_action_parameters(parameters: list[ActionParameter]) -> str:
42
+ lines: list[str] = []
43
+ for param in parameters:
44
+ schema = _get_param_schema(param)
45
+ if schema is None:
46
+ lines.append(f" - {param.name}: {param.description}")
47
+ continue
48
+ required_str = " (required)" if param.required else " (optional)"
49
+ type_str = _format_parameter_type(schema)
50
+ default_val = getattr(schema, "default", None) or getattr(schema, "default_value", None)
51
+ default_str = f" [default: {default_val}]" if default_val else ""
52
+ enum_vals = getattr(schema, "enum", None) or getattr(schema, "enum_values", None)
53
+ enum_str = f" [values: {', '.join(enum_vals)}]" if enum_vals else ""
54
+ examples_str = (
55
+ f" [examples: {', '.join(repr(v) for v in param.examples)}]"
56
+ if getattr(param, "examples", None)
57
+ else ""
58
+ )
59
+ lines.append(
60
+ f" - {param.name}{required_str}: {param.description} ({type_str}{enum_str}{default_str}{examples_str})"
61
+ )
62
+ return "\n".join(lines)
63
+
64
+
65
+ def format_actions(actions: list[Action]) -> str:
66
+ lines: list[str] = []
67
+ for action in actions:
68
+ line = f"- **{action.name}**: {action.description or 'No description'}"
69
+ if action.parameters:
70
+ params_text = _format_action_parameters(action.parameters)
71
+ if params_text:
72
+ line += f"\n Parameters:\n{params_text}"
73
+ lines.append(line)
74
+ return "\n".join(lines)
75
+
76
+
77
+ def _replace_name_placeholders(text: str) -> str:
78
+ names = ["Alex", "Jordan", "Sam", "Taylor", "Riley"]
79
+ for i, name in enumerate(names, start=1):
80
+ text = text.replace(f"{{{{name{i}}}}}", name)
81
+ return text
82
+
83
+
84
+ def format_action_examples(actions: list[Action], max_examples: int = 10) -> str:
85
+ """
86
+ Format a deterministic subset of action examples for prompt context.
87
+
88
+ Deterministic ordering is important to keep tests stable and avoid prompt churn.
89
+ """
90
+ if max_examples <= 0:
91
+ return ""
92
+
93
+ examples: list[list[ActionExample]] = []
94
+ for action in sorted(actions, key=lambda a: a.name):
95
+ if not action.examples:
96
+ continue
97
+ for ex in action.examples:
98
+ if isinstance(ex, list) and ex:
99
+ examples.append(ex)
100
+ if len(examples) >= max_examples:
101
+ break
102
+ if len(examples) >= max_examples:
103
+ break
104
+
105
+ if not examples:
106
+ return ""
107
+
108
+ blocks: list[str] = []
109
+ for ex in examples:
110
+ lines: list[str] = []
111
+ for msg in ex:
112
+ msg_text = msg.content.text if msg.content and msg.content.text else ""
113
+ lines.append(f"{msg.name}: {_replace_name_placeholders(msg_text)}")
114
+ blocks.append("\n".join(lines))
115
+
116
+ return "\n\n".join(blocks)
117
+
118
+
119
+ def _escape_xml_text(text: str) -> str:
120
+ return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
121
+
122
+
123
+ def format_action_call_examples(actions: list[Action], max_examples: int = 5) -> str:
124
+ """
125
+ Format canonical action-call examples (including optional <params> blocks).
126
+
127
+ Deterministic ordering is important to keep tests stable and avoid prompt churn.
128
+ """
129
+ if max_examples <= 0:
130
+ return ""
131
+
132
+ blocks: list[str] = []
133
+ for action in sorted(actions, key=lambda a: a.name):
134
+ calls = get_canonical_action_example_calls(action.name)
135
+ for call in calls:
136
+ user = call.get("user")
137
+ action_names = call.get("actions")
138
+ params = call.get("params")
139
+
140
+ if not isinstance(user, str) or not isinstance(action_names, list):
141
+ continue
142
+ if not all(isinstance(a, str) for a in action_names):
143
+ continue
144
+
145
+ actions_xml = "\n".join(
146
+ f" <action>{_escape_xml_text(a)}</action>" for a in action_names
147
+ )
148
+
149
+ params_xml = ""
150
+ if isinstance(params, dict):
151
+ blocks_xml: list[str] = []
152
+ for act_name, act_params in params.items():
153
+ if not isinstance(act_name, str) or not isinstance(act_params, dict):
154
+ continue
155
+ inner: list[str] = []
156
+ for k, v in act_params.items():
157
+ if not isinstance(k, str):
158
+ continue
159
+ if isinstance(v, str):
160
+ raw = v
161
+ elif v is None:
162
+ raw = "null"
163
+ elif isinstance(v, bool):
164
+ raw = "true" if v else "false"
165
+ elif isinstance(v, (int, float)):
166
+ raw = str(v)
167
+ else:
168
+ raw = repr(v)
169
+ inner.append(f" <{k}>{_escape_xml_text(raw)}</{k}>")
170
+ blocks_xml.append(f" <{act_name}>\n" + "\n".join(inner) + f"\n </{act_name}>")
171
+ if blocks_xml:
172
+ params_xml = "\n<params>\n" + "\n".join(blocks_xml) + "\n</params>"
173
+
174
+ blocks.append(
175
+ f"User: {user}\nAssistant:\n<actions>\n{actions_xml}\n</actions>{params_xml}"
176
+ )
177
+ if len(blocks) >= max_examples:
178
+ return "\n\n".join(blocks)
179
+
180
+ return "\n\n".join(blocks)
181
+
182
+
183
+ async def get_actions(
184
+ runtime: IAgentRuntime,
185
+ message: Memory,
186
+ state: State | None = None,
187
+ ) -> ProviderResult:
188
+ validated_actions: list[Action] = []
189
+
190
+ for action in runtime.actions:
191
+ validate_fn = getattr(action, "validate", None) or getattr(action, "validate_fn", None)
192
+ is_valid = await validate_fn(runtime, message, state) if validate_fn else True
193
+ if is_valid:
194
+ validated_actions.append(action)
195
+
196
+ action_names = format_action_names(validated_actions)
197
+ actions_text = format_actions(validated_actions)
198
+ examples_text = format_action_examples(validated_actions, max_examples=10)
199
+ call_examples_text = format_action_call_examples(validated_actions, max_examples=5)
200
+
201
+ text_parts: list[str] = [f"Possible response actions: {action_names}"]
202
+ if actions_text:
203
+ text_parts.append(f"# Available Actions\n{actions_text}")
204
+ if examples_text:
205
+ text_parts.append(f"# Action Examples\n{examples_text}")
206
+ if call_examples_text:
207
+ text_parts.append(f"# Action Call Examples (with <params>)\n{call_examples_text}")
208
+
209
+ return ProviderResult(
210
+ text="\n\n".join(text_parts),
211
+ values={
212
+ "actionNames": action_names,
213
+ "actionCount": len(validated_actions),
214
+ },
215
+ data={
216
+ "actions": [
217
+ {
218
+ "name": a.name,
219
+ "description": a.description,
220
+ "examples": [
221
+ [
222
+ {
223
+ "name": ex.name,
224
+ "content": ex.content.model_dump(),
225
+ }
226
+ for ex in example
227
+ ]
228
+ for example in (a.examples or [])
229
+ ],
230
+ "parameters": [
231
+ {
232
+ "name": p.name,
233
+ "description": p.description,
234
+ "required": bool(p.required),
235
+ "examples": getattr(p, "examples", None) or [],
236
+ "schema": (
237
+ p.schema_def.model_dump()
238
+ if hasattr(p, "schema_def") and hasattr(p.schema_def, "model_dump")
239
+ else getattr(p, "schema", None)
240
+ ),
241
+ }
242
+ for p in (a.parameters or [])
243
+ ],
244
+ }
245
+ for a in validated_actions
246
+ ],
247
+ },
248
+ )
249
+
250
+
251
+ actions_provider = Provider(
252
+ name=_spec["name"],
253
+ description=_spec["description"],
254
+ get=get_actions,
255
+ position=_spec.get("position", -1),
256
+ )
@@ -0,0 +1,63 @@
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("AGENT_SETTINGS")
13
+
14
+ SENSITIVE_KEY_PATTERNS = (
15
+ "key",
16
+ "secret",
17
+ "password",
18
+ "token",
19
+ "credential",
20
+ "auth",
21
+ "private",
22
+ )
23
+
24
+
25
+ async def get_agent_settings_context(
26
+ runtime: IAgentRuntime,
27
+ message: Memory,
28
+ state: State | None = None,
29
+ ) -> ProviderResult:
30
+ all_settings = runtime.get_all_settings()
31
+
32
+ safe_settings: dict[str, str] = {}
33
+ for key, value in all_settings.items():
34
+ if not any(pattern in key.lower() for pattern in SENSITIVE_KEY_PATTERNS):
35
+ safe_settings[key] = str(value)
36
+
37
+ sections: list[str] = []
38
+ if safe_settings:
39
+ sections.append("# Agent Settings")
40
+ for key, value in safe_settings.items():
41
+ display_value = value if len(value) <= 50 else value[:50] + "..."
42
+ sections.append(f"- {key}: {display_value}")
43
+
44
+ context_text = "\n".join(sections) if sections else ""
45
+
46
+ return ProviderResult(
47
+ text=context_text,
48
+ values={
49
+ "settingsCount": len(safe_settings),
50
+ "hasSettings": len(safe_settings) > 0,
51
+ },
52
+ data={
53
+ "settings": safe_settings,
54
+ },
55
+ )
56
+
57
+
58
+ agent_settings_provider = Provider(
59
+ name=_spec["name"],
60
+ description=_spec["description"],
61
+ get=get_agent_settings_context,
62
+ dynamic=_spec.get("dynamic", True),
63
+ )
@@ -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,66 @@
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("CAPABILITIES")
13
+
14
+
15
+ async def get_capabilities(
16
+ runtime: IAgentRuntime,
17
+ message: Memory,
18
+ state: State | None = None,
19
+ ) -> ProviderResult:
20
+ model_types = ["TEXT_LARGE", "TEXT_SMALL", "TEXT_EMBEDDING", "IMAGE", "AUDIO"]
21
+ available_models = [mt for mt in model_types if runtime.has_model(mt)]
22
+ service_names = [s.name for s in runtime.services if hasattr(s, "name")]
23
+
24
+ features: list[str] = []
25
+ if runtime.get_setting("ENABLE_VOICE"):
26
+ features.append("voice")
27
+ if runtime.get_setting("ENABLE_VISION"):
28
+ features.append("vision")
29
+ if runtime.get_setting("ENABLE_MEMORY"):
30
+ features.append("long_term_memory")
31
+
32
+ capabilities: dict[str, list[str] | bool] = {
33
+ "models": available_models,
34
+ "services": service_names,
35
+ "features": features,
36
+ }
37
+
38
+ text_parts: list[str] = ["# Agent Capabilities"]
39
+
40
+ if available_models:
41
+ text_parts.append(f"Models: {', '.join(available_models)}")
42
+
43
+ if service_names:
44
+ text_parts.append(f"Services: {', '.join(service_names)}")
45
+
46
+ if features:
47
+ text_parts.append(f"Features: {', '.join(features)}")
48
+
49
+ return ProviderResult(
50
+ text="\n".join(text_parts),
51
+ values={
52
+ "modelCount": len(available_models),
53
+ "serviceCount": len(service_names),
54
+ "hasVoice": "voice" in features,
55
+ "hasVision": "vision" in features,
56
+ },
57
+ data=capabilities,
58
+ )
59
+
60
+
61
+ capabilities_provider = Provider(
62
+ name=_spec["name"],
63
+ description=_spec["description"],
64
+ get=get_capabilities,
65
+ dynamic=_spec.get("dynamic", False),
66
+ )
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
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("CHARACTER")
14
+
15
+
16
+ def _to_str_list(value: str | Iterable[str] | None) -> list[str]:
17
+ """
18
+ Normalize a value to list[str].
19
+
20
+ Handles str, list, tuple, set, or any Iterable[str].
21
+ Returns empty list for None.
22
+
23
+ WHY: Character fields can be str | list[str] | tuple[str] depending on
24
+ how they're defined. This helper ensures consistent handling regardless
25
+ of the input type, avoiding issues like tuples being treated as scalars.
26
+ """
27
+ if value is None:
28
+ return []
29
+ if isinstance(value, str):
30
+ return [value]
31
+ # Any other iterable (list, tuple, set, etc.) - convert to list
32
+ return list(value)
33
+
34
+
35
+ def _to_str_list(value: str | Iterable[str] | None) -> list[str]:
36
+ """
37
+ Normalize a value to list[str].
38
+
39
+ Handles str, list, tuple, set, or any Iterable[str].
40
+ Returns empty list for None.
41
+
42
+ WHY: Character fields can be str | list[str] | tuple[str] depending on
43
+ how they're defined. This helper ensures consistent handling regardless
44
+ of the input type, avoiding issues like tuples being treated as scalars.
45
+ """
46
+ if value is None:
47
+ return []
48
+ if isinstance(value, str):
49
+ return [value]
50
+ # Any other iterable (list, tuple, set, etc.) - convert to list
51
+ return list(value)
52
+
53
+
54
+ async def get_character_context(
55
+ runtime: IAgentRuntime,
56
+ message: Memory,
57
+ state: State | None = None,
58
+ ) -> ProviderResult:
59
+ character = runtime.character
60
+
61
+ sections: list[str] = []
62
+
63
+ sections.append(f"# Agent: {character.name}")
64
+
65
+ bio = getattr(character, "bio", None)
66
+ if bio:
67
+ bio_list = _to_str_list(bio)
68
+ sections.append(f"\n## Bio\n{chr(10).join(bio_list)}")
69
+
70
+ adjectives = getattr(character, "adjectives", None)
71
+ if adjectives:
72
+ adjectives_list = _to_str_list(adjectives)
73
+ sections.append(f"\n## Personality Traits\n{', '.join(adjectives_list)}")
74
+
75
+ # lore is optional and may not exist on all Character instances
76
+ lore = getattr(character, "lore", None)
77
+ if lore:
78
+ lore_list = _to_str_list(lore)
79
+ sections.append(f"\n## Background\n{chr(10).join(lore_list)}")
80
+
81
+ topics = getattr(character, "topics", None)
82
+ if topics:
83
+ topics_list = _to_str_list(topics)
84
+ sections.append(f"\n## Knowledge Areas\n{', '.join(topics_list)}")
85
+
86
+ style = getattr(character, "style", None)
87
+ if style:
88
+ style_sections: list[str] = []
89
+ style_all = getattr(style, "all", None)
90
+ if style_all:
91
+ all_style = _to_str_list(style_all)
92
+ style_sections.append(f"General: {', '.join(all_style)}")
93
+ style_chat = getattr(style, "chat", None)
94
+ if style_chat:
95
+ chat_style = _to_str_list(style_chat)
96
+ style_sections.append(f"Chat: {', '.join(chat_style)}")
97
+ style_post = getattr(style, "post", None)
98
+ if style_post:
99
+ post_style = _to_str_list(style_post)
100
+ style_sections.append(f"Posts: {', '.join(post_style)}")
101
+ if style_sections:
102
+ sections.append("\n## Communication Style\n" + "\n".join(style_sections))
103
+
104
+ context_text = "\n".join(sections)
105
+
106
+ # Use variables retrieved via getattr above to avoid AttributeError
107
+ # if these optional attributes are missing from the character object
108
+ return ProviderResult(
109
+ text=context_text,
110
+ values={
111
+ "agentName": character.name,
112
+ "hasCharacter": True,
113
+ },
114
+ data={
115
+ "name": character.name,
116
+ "bio": bio,
117
+ "adjectives": adjectives,
118
+ "topics": topics,
119
+ },
120
+ )
121
+
122
+
123
+ character_provider = Provider(
124
+ name=_spec["name"],
125
+ description=_spec["description"],
126
+ get=get_character_context,
127
+ dynamic=_spec.get("dynamic", False),
128
+ )
@@ -0,0 +1,77 @@
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("CHOICE")
13
+
14
+
15
+ def format_choice(index: int, choice: dict[str, str]) -> str:
16
+ label = choice.get("label", f"Option {index + 1}")
17
+ description = choice.get("description", "")
18
+ value = choice.get("value", str(index))
19
+
20
+ if description:
21
+ return f"{index + 1}. [{value}] {label}: {description}"
22
+ return f"{index + 1}. [{value}] {label}"
23
+
24
+
25
+ async def get_choice_options(
26
+ runtime: IAgentRuntime,
27
+ message: Memory,
28
+ state: State | None = None,
29
+ ) -> ProviderResult:
30
+ choices: list[dict[str, str]] = []
31
+
32
+ if message.content and hasattr(message.content, "choices"):
33
+ raw_choices = message.content.choices or []
34
+ for choice in raw_choices:
35
+ if isinstance(choice, dict):
36
+ choices.append(choice)
37
+ elif isinstance(choice, str):
38
+ choices.append({"label": choice, "value": choice})
39
+
40
+ if state and hasattr(state, "choices"):
41
+ state_choices = state.choices or []
42
+ for choice in state_choices:
43
+ if isinstance(choice, dict):
44
+ choices.append(choice)
45
+ elif isinstance(choice, str):
46
+ choices.append({"label": choice, "value": choice})
47
+
48
+ if not choices:
49
+ return ProviderResult(
50
+ text="",
51
+ values={"hasChoices": False, "choiceCount": 0},
52
+ data={"choices": []},
53
+ )
54
+
55
+ formatted_choices = "\n".join(format_choice(i, choice) for i, choice in enumerate(choices))
56
+
57
+ text = f"# Available Choices\n{formatted_choices}"
58
+
59
+ return ProviderResult(
60
+ text=text,
61
+ values={
62
+ "hasChoices": True,
63
+ "choiceCount": len(choices),
64
+ "choiceLabels": [c.get("label", "") for c in choices],
65
+ },
66
+ data={
67
+ "choices": choices,
68
+ },
69
+ )
70
+
71
+
72
+ choice_provider = Provider(
73
+ name=_spec["name"],
74
+ description=_spec["description"],
75
+ get=get_choice_options,
76
+ dynamic=_spec.get("dynamic", True),
77
+ )