@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.
- package/LICENSE +26 -0
- package/README.md +239 -0
- package/elizaos/__init__.py +280 -0
- package/elizaos/action_docs.py +149 -0
- package/elizaos/advanced_capabilities/__init__.py +85 -0
- package/elizaos/advanced_capabilities/actions/__init__.py +54 -0
- package/elizaos/advanced_capabilities/actions/add_contact.py +139 -0
- package/elizaos/advanced_capabilities/actions/follow_room.py +151 -0
- package/elizaos/advanced_capabilities/actions/image_generation.py +148 -0
- package/elizaos/advanced_capabilities/actions/mute_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/remove_contact.py +145 -0
- package/elizaos/advanced_capabilities/actions/roles.py +207 -0
- package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +154 -0
- package/elizaos/advanced_capabilities/actions/search_contacts.py +145 -0
- package/elizaos/advanced_capabilities/actions/send_message.py +187 -0
- package/elizaos/advanced_capabilities/actions/settings.py +151 -0
- package/elizaos/advanced_capabilities/actions/unfollow_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/unmute_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/update_contact.py +164 -0
- package/elizaos/advanced_capabilities/actions/update_entity.py +161 -0
- package/elizaos/advanced_capabilities/evaluators/__init__.py +18 -0
- package/elizaos/advanced_capabilities/evaluators/reflection.py +134 -0
- package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +203 -0
- package/elizaos/advanced_capabilities/providers/__init__.py +36 -0
- package/elizaos/advanced_capabilities/providers/agent_settings.py +60 -0
- package/elizaos/advanced_capabilities/providers/contacts.py +77 -0
- package/elizaos/advanced_capabilities/providers/facts.py +82 -0
- package/elizaos/advanced_capabilities/providers/follow_ups.py +113 -0
- package/elizaos/advanced_capabilities/providers/knowledge.py +83 -0
- package/elizaos/advanced_capabilities/providers/relationships.py +112 -0
- package/elizaos/advanced_capabilities/providers/roles.py +97 -0
- package/elizaos/advanced_capabilities/providers/settings.py +51 -0
- package/elizaos/advanced_capabilities/services/__init__.py +18 -0
- package/elizaos/advanced_capabilities/services/follow_up.py +138 -0
- package/elizaos/advanced_capabilities/services/rolodex.py +244 -0
- package/elizaos/advanced_memory/__init__.py +3 -0
- package/elizaos/advanced_memory/evaluators.py +97 -0
- package/elizaos/advanced_memory/memory_service.py +556 -0
- package/elizaos/advanced_memory/plugin.py +30 -0
- package/elizaos/advanced_memory/prompts.py +12 -0
- package/elizaos/advanced_memory/providers.py +90 -0
- package/elizaos/advanced_memory/types.py +65 -0
- package/elizaos/advanced_planning/__init__.py +10 -0
- package/elizaos/advanced_planning/actions.py +145 -0
- package/elizaos/advanced_planning/message_classifier.py +127 -0
- package/elizaos/advanced_planning/planning_service.py +712 -0
- package/elizaos/advanced_planning/plugin.py +40 -0
- package/elizaos/advanced_planning/prompts.py +4 -0
- package/elizaos/basic_capabilities/__init__.py +66 -0
- package/elizaos/basic_capabilities/actions/__init__.py +24 -0
- package/elizaos/basic_capabilities/actions/choice.py +140 -0
- package/elizaos/basic_capabilities/actions/ignore.py +66 -0
- package/elizaos/basic_capabilities/actions/none.py +56 -0
- package/elizaos/basic_capabilities/actions/reply.py +120 -0
- package/elizaos/basic_capabilities/providers/__init__.py +54 -0
- package/elizaos/basic_capabilities/providers/action_state.py +113 -0
- package/elizaos/basic_capabilities/providers/actions.py +263 -0
- package/elizaos/basic_capabilities/providers/attachments.py +76 -0
- package/elizaos/basic_capabilities/providers/capabilities.py +62 -0
- package/elizaos/basic_capabilities/providers/character.py +113 -0
- package/elizaos/basic_capabilities/providers/choice.py +73 -0
- package/elizaos/basic_capabilities/providers/context_bench.py +44 -0
- package/elizaos/basic_capabilities/providers/current_time.py +58 -0
- package/elizaos/basic_capabilities/providers/entities.py +99 -0
- package/elizaos/basic_capabilities/providers/evaluators.py +54 -0
- package/elizaos/basic_capabilities/providers/providers_list.py +55 -0
- package/elizaos/basic_capabilities/providers/recent_messages.py +85 -0
- package/elizaos/basic_capabilities/providers/time.py +45 -0
- package/elizaos/basic_capabilities/providers/world.py +93 -0
- package/elizaos/basic_capabilities/services/__init__.py +18 -0
- package/elizaos/basic_capabilities/services/embedding.py +122 -0
- package/elizaos/basic_capabilities/services/task.py +178 -0
- package/elizaos/bootstrap/__init__.py +12 -0
- package/elizaos/bootstrap/actions/__init__.py +68 -0
- package/elizaos/bootstrap/actions/add_contact.py +149 -0
- package/elizaos/bootstrap/actions/choice.py +147 -0
- package/elizaos/bootstrap/actions/follow_room.py +151 -0
- package/elizaos/bootstrap/actions/ignore.py +80 -0
- package/elizaos/bootstrap/actions/image_generation.py +135 -0
- package/elizaos/bootstrap/actions/mute_room.py +151 -0
- package/elizaos/bootstrap/actions/none.py +71 -0
- package/elizaos/bootstrap/actions/remove_contact.py +159 -0
- package/elizaos/bootstrap/actions/reply.py +140 -0
- package/elizaos/bootstrap/actions/roles.py +193 -0
- package/elizaos/bootstrap/actions/schedule_follow_up.py +164 -0
- package/elizaos/bootstrap/actions/search_contacts.py +159 -0
- package/elizaos/bootstrap/actions/send_message.py +173 -0
- package/elizaos/bootstrap/actions/settings.py +165 -0
- package/elizaos/bootstrap/actions/unfollow_room.py +151 -0
- package/elizaos/bootstrap/actions/unmute_room.py +151 -0
- package/elizaos/bootstrap/actions/update_contact.py +178 -0
- package/elizaos/bootstrap/actions/update_entity.py +175 -0
- package/elizaos/bootstrap/autonomy/__init__.py +18 -0
- package/elizaos/bootstrap/autonomy/action.py +197 -0
- package/elizaos/bootstrap/autonomy/providers.py +165 -0
- package/elizaos/bootstrap/autonomy/routes.py +171 -0
- package/elizaos/bootstrap/autonomy/service.py +562 -0
- package/elizaos/bootstrap/autonomy/types.py +18 -0
- package/elizaos/bootstrap/evaluators/__init__.py +19 -0
- package/elizaos/bootstrap/evaluators/reflection.py +118 -0
- package/elizaos/bootstrap/evaluators/relationship_extraction.py +192 -0
- package/elizaos/bootstrap/plugin.py +140 -0
- package/elizaos/bootstrap/providers/__init__.py +80 -0
- package/elizaos/bootstrap/providers/action_state.py +71 -0
- package/elizaos/bootstrap/providers/actions.py +256 -0
- package/elizaos/bootstrap/providers/agent_settings.py +63 -0
- package/elizaos/bootstrap/providers/attachments.py +76 -0
- package/elizaos/bootstrap/providers/capabilities.py +66 -0
- package/elizaos/bootstrap/providers/character.py +128 -0
- package/elizaos/bootstrap/providers/choice.py +77 -0
- package/elizaos/bootstrap/providers/contacts.py +78 -0
- package/elizaos/bootstrap/providers/context_bench.py +49 -0
- package/elizaos/bootstrap/providers/current_time.py +56 -0
- package/elizaos/bootstrap/providers/entities.py +99 -0
- package/elizaos/bootstrap/providers/evaluators.py +58 -0
- package/elizaos/bootstrap/providers/facts.py +86 -0
- package/elizaos/bootstrap/providers/follow_ups.py +116 -0
- package/elizaos/bootstrap/providers/knowledge.py +73 -0
- package/elizaos/bootstrap/providers/providers_list.py +59 -0
- package/elizaos/bootstrap/providers/recent_messages.py +85 -0
- package/elizaos/bootstrap/providers/relationships.py +106 -0
- package/elizaos/bootstrap/providers/roles.py +95 -0
- package/elizaos/bootstrap/providers/settings.py +55 -0
- package/elizaos/bootstrap/providers/time.py +45 -0
- package/elizaos/bootstrap/providers/world.py +97 -0
- package/elizaos/bootstrap/services/__init__.py +26 -0
- package/elizaos/bootstrap/services/embedding.py +122 -0
- package/elizaos/bootstrap/services/follow_up.py +138 -0
- package/elizaos/bootstrap/services/rolodex.py +244 -0
- package/elizaos/bootstrap/services/task.py +585 -0
- package/elizaos/bootstrap/types.py +54 -0
- package/elizaos/bootstrap/utils/__init__.py +7 -0
- package/elizaos/bootstrap/utils/xml.py +69 -0
- package/elizaos/character.py +149 -0
- package/elizaos/logger.py +179 -0
- package/elizaos/media/__init__.py +45 -0
- package/elizaos/media/mime.py +315 -0
- package/elizaos/media/search.py +161 -0
- package/elizaos/media/tests/__init__.py +1 -0
- package/elizaos/media/tests/test_mime.py +117 -0
- package/elizaos/media/tests/test_search.py +156 -0
- package/elizaos/plugin.py +191 -0
- package/elizaos/prompts.py +1071 -0
- package/elizaos/py.typed +0 -0
- package/elizaos/runtime.py +2572 -0
- package/elizaos/services/__init__.py +49 -0
- package/elizaos/services/hook_service.py +511 -0
- package/elizaos/services/message_service.py +1248 -0
- package/elizaos/settings.py +182 -0
- package/elizaos/streaming_context.py +159 -0
- package/elizaos/trajectory_context.py +18 -0
- package/elizaos/types/__init__.py +512 -0
- package/elizaos/types/agent.py +31 -0
- package/elizaos/types/components.py +208 -0
- package/elizaos/types/database.py +64 -0
- package/elizaos/types/environment.py +46 -0
- package/elizaos/types/events.py +47 -0
- package/elizaos/types/memory.py +45 -0
- package/elizaos/types/model.py +393 -0
- package/elizaos/types/plugin.py +188 -0
- package/elizaos/types/primitives.py +100 -0
- package/elizaos/types/runtime.py +460 -0
- package/elizaos/types/service.py +113 -0
- package/elizaos/types/service_interfaces.py +244 -0
- package/elizaos/types/state.py +188 -0
- package/elizaos/types/task.py +29 -0
- package/elizaos/utils/__init__.py +108 -0
- package/elizaos/utils/spec_examples.py +48 -0
- package/elizaos/utils/streaming.py +426 -0
- package/elizaos_atropos_shared/__init__.py +1 -0
- package/elizaos_atropos_shared/canonical_eliza.py +282 -0
- package/package.json +19 -0
- package/pyproject.toml +143 -0
- package/requirements-dev.in +11 -0
- package/requirements-dev.lock +134 -0
- package/requirements.in +9 -0
- package/requirements.lock +64 -0
- package/tests/__init__.py +0 -0
- package/tests/test_action_parameters.py +154 -0
- package/tests/test_actions_provider_examples.py +39 -0
- package/tests/test_advanced_memory_behavior.py +96 -0
- package/tests/test_advanced_memory_flag.py +30 -0
- package/tests/test_advanced_planning_behavior.py +225 -0
- package/tests/test_advanced_planning_flag.py +26 -0
- package/tests/test_autonomy.py +445 -0
- package/tests/test_bootstrap_initialize.py +37 -0
- package/tests/test_character.py +163 -0
- package/tests/test_character_provider.py +231 -0
- package/tests/test_dynamic_prompt_exec.py +561 -0
- package/tests/test_logger_redaction.py +43 -0
- package/tests/test_plugin.py +117 -0
- package/tests/test_runtime.py +422 -0
- package/tests/test_salt_production_enforcement.py +22 -0
- package/tests/test_settings_crypto.py +118 -0
- package/tests/test_streaming.py +295 -0
- package/tests/test_types.py +221 -0
- package/tests/test_uuid_parity.py +46 -0
|
@@ -0,0 +1,178 @@
|
|
|
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
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from elizaos.types import (
|
|
19
|
+
HandlerCallback,
|
|
20
|
+
HandlerOptions,
|
|
21
|
+
IAgentRuntime,
|
|
22
|
+
Memory,
|
|
23
|
+
State,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Get text content from centralized specs
|
|
27
|
+
_spec = require_action_spec("UPDATE_CONTACT")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
31
|
+
"""Convert spec examples to ActionExample format."""
|
|
32
|
+
spec_examples = _spec.get("examples", [])
|
|
33
|
+
if spec_examples:
|
|
34
|
+
return [
|
|
35
|
+
[
|
|
36
|
+
ActionExample(
|
|
37
|
+
name=msg.get("name", ""),
|
|
38
|
+
content=Content(
|
|
39
|
+
text=msg.get("content", {}).get("text", ""),
|
|
40
|
+
actions=msg.get("content", {}).get("actions"),
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
for msg in example
|
|
44
|
+
]
|
|
45
|
+
for example in spec_examples
|
|
46
|
+
]
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class UpdateContactAction:
|
|
52
|
+
name: str = _spec["name"]
|
|
53
|
+
similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
|
|
54
|
+
description: str = _spec["description"]
|
|
55
|
+
|
|
56
|
+
async def validate(
|
|
57
|
+
self, runtime: IAgentRuntime, _message: Memory, _state: State | None = None
|
|
58
|
+
) -> bool:
|
|
59
|
+
rolodex_service = runtime.get_service("rolodex")
|
|
60
|
+
return rolodex_service is not None
|
|
61
|
+
|
|
62
|
+
async def handler(
|
|
63
|
+
self,
|
|
64
|
+
runtime: IAgentRuntime,
|
|
65
|
+
message: Memory,
|
|
66
|
+
state: State | None = None,
|
|
67
|
+
options: HandlerOptions | None = None,
|
|
68
|
+
callback: HandlerCallback | None = None,
|
|
69
|
+
responses: list[Memory] | None = None,
|
|
70
|
+
) -> ActionResult:
|
|
71
|
+
from elizaos.bootstrap.services.rolodex import RolodexService
|
|
72
|
+
|
|
73
|
+
rolodex_service = runtime.get_service("rolodex")
|
|
74
|
+
if not rolodex_service or not isinstance(rolodex_service, RolodexService):
|
|
75
|
+
return ActionResult(
|
|
76
|
+
text="Rolodex service not available",
|
|
77
|
+
success=False,
|
|
78
|
+
values={"error": True},
|
|
79
|
+
data={"error": "RolodexService not available"},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
state = await runtime.compose_state(message, ["RECENT_MESSAGES", "ENTITIES"])
|
|
83
|
+
|
|
84
|
+
prompt = runtime.compose_prompt_from_state(
|
|
85
|
+
state=state,
|
|
86
|
+
template=UPDATE_CONTACT_TEMPLATE,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
response = await runtime.use_model(ModelType.TEXT_SMALL, {"prompt": prompt})
|
|
90
|
+
parsed = parse_key_value_xml(response)
|
|
91
|
+
|
|
92
|
+
if not parsed or not parsed.get("contactName"):
|
|
93
|
+
return ActionResult(
|
|
94
|
+
text="Could not determine which contact to update",
|
|
95
|
+
success=False,
|
|
96
|
+
values={"error": True},
|
|
97
|
+
data={"error": "No contact name provided"},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
contact_name = str(parsed.get("contactName", ""))
|
|
101
|
+
operation = str(parsed.get("operation", "replace"))
|
|
102
|
+
|
|
103
|
+
contacts = await rolodex_service.search_contacts(search_term=contact_name)
|
|
104
|
+
|
|
105
|
+
if not contacts:
|
|
106
|
+
return ActionResult(
|
|
107
|
+
text=f"Could not find a contact named '{contact_name}'",
|
|
108
|
+
success=False,
|
|
109
|
+
values={"error": True},
|
|
110
|
+
data={"error": "Contact not found"},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
contact = contacts[0]
|
|
114
|
+
|
|
115
|
+
categories = None
|
|
116
|
+
tags = None
|
|
117
|
+
|
|
118
|
+
if parsed.get("categories"):
|
|
119
|
+
new_categories = [c.strip() for c in str(parsed["categories"]).split(",") if c.strip()]
|
|
120
|
+
if operation == "add_to" and contact.categories:
|
|
121
|
+
categories = list(set(contact.categories + new_categories))
|
|
122
|
+
else:
|
|
123
|
+
categories = new_categories
|
|
124
|
+
|
|
125
|
+
if parsed.get("tags"):
|
|
126
|
+
new_tags = [t.strip() for t in str(parsed["tags"]).split(",") if t.strip()]
|
|
127
|
+
if operation == "add_to" and contact.tags:
|
|
128
|
+
tags = list(set(contact.tags + new_tags))
|
|
129
|
+
else:
|
|
130
|
+
tags = new_tags
|
|
131
|
+
|
|
132
|
+
updated = await rolodex_service.update_contact(
|
|
133
|
+
entity_id=contact.entity_id,
|
|
134
|
+
categories=categories,
|
|
135
|
+
tags=tags,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if updated:
|
|
139
|
+
response_text = f"I've updated {contact_name}'s contact information."
|
|
140
|
+
if categories:
|
|
141
|
+
response_text += f" Categories: {', '.join(categories)}."
|
|
142
|
+
if tags:
|
|
143
|
+
response_text += f" Tags: {', '.join(tags)}."
|
|
144
|
+
|
|
145
|
+
if callback:
|
|
146
|
+
await callback(Content(text=response_text, actions=["UPDATE_CONTACT"]))
|
|
147
|
+
|
|
148
|
+
return ActionResult(
|
|
149
|
+
text=response_text,
|
|
150
|
+
success=True,
|
|
151
|
+
values={
|
|
152
|
+
"contactId": str(contact.entity_id),
|
|
153
|
+
"categoriesStr": ",".join(categories) if categories else "",
|
|
154
|
+
"tagsStr": ",".join(tags) if tags else "",
|
|
155
|
+
},
|
|
156
|
+
data={"success": True},
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
return ActionResult(
|
|
160
|
+
text="Failed to update contact",
|
|
161
|
+
success=False,
|
|
162
|
+
values={"error": True},
|
|
163
|
+
data={"error": "Update operation failed"},
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def examples(self) -> list[list[ActionExample]]:
|
|
168
|
+
return _convert_spec_examples()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
update_contact_action = Action(
|
|
172
|
+
name=UpdateContactAction.name,
|
|
173
|
+
similes=UpdateContactAction().similes,
|
|
174
|
+
description=UpdateContactAction.description,
|
|
175
|
+
validate=UpdateContactAction().validate,
|
|
176
|
+
handler=UpdateContactAction().handler,
|
|
177
|
+
examples=UpdateContactAction().examples,
|
|
178
|
+
)
|
|
@@ -0,0 +1,175 @@
|
|
|
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
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from elizaos.types import HandlerCallback, HandlerOptions, IAgentRuntime, Memory, State
|
|
14
|
+
|
|
15
|
+
# Get text content from centralized specs
|
|
16
|
+
_spec = require_action_spec("UPDATE_ENTITY")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
20
|
+
"""Convert spec examples to ActionExample format."""
|
|
21
|
+
spec_examples = _spec.get("examples", [])
|
|
22
|
+
if spec_examples:
|
|
23
|
+
return [
|
|
24
|
+
[
|
|
25
|
+
ActionExample(
|
|
26
|
+
name=msg.get("name", ""),
|
|
27
|
+
content=Content(
|
|
28
|
+
text=msg.get("content", {}).get("text", ""),
|
|
29
|
+
actions=msg.get("content", {}).get("actions"),
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
for msg in example
|
|
33
|
+
]
|
|
34
|
+
for example in spec_examples
|
|
35
|
+
]
|
|
36
|
+
return []
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class UpdateEntityAction:
|
|
41
|
+
name: str = _spec["name"]
|
|
42
|
+
similes: list[str] = field(default_factory=lambda: list(_spec.get("similes", [])))
|
|
43
|
+
description: str = _spec["description"]
|
|
44
|
+
|
|
45
|
+
async def validate(
|
|
46
|
+
self, runtime: IAgentRuntime, message: Memory, _state: State | None = None
|
|
47
|
+
) -> bool:
|
|
48
|
+
return message.entity_id is not None
|
|
49
|
+
|
|
50
|
+
async def handler(
|
|
51
|
+
self,
|
|
52
|
+
runtime: IAgentRuntime,
|
|
53
|
+
message: Memory,
|
|
54
|
+
state: State | None = None,
|
|
55
|
+
options: HandlerOptions | None = None,
|
|
56
|
+
callback: HandlerCallback | None = None,
|
|
57
|
+
responses: list[Memory] | None = None,
|
|
58
|
+
) -> ActionResult:
|
|
59
|
+
if state is None:
|
|
60
|
+
raise ValueError("State is required for UPDATE_ENTITY action")
|
|
61
|
+
|
|
62
|
+
entity_id = message.entity_id
|
|
63
|
+
if not entity_id:
|
|
64
|
+
return ActionResult(
|
|
65
|
+
text="No entity specified to update",
|
|
66
|
+
values={"success": False, "error": "no_entity_id"},
|
|
67
|
+
data={"actionName": "UPDATE_ENTITY"},
|
|
68
|
+
success=False,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
entity = await runtime.get_entity(entity_id)
|
|
72
|
+
if entity is None:
|
|
73
|
+
return ActionResult(
|
|
74
|
+
text="Entity not found",
|
|
75
|
+
values={"success": False, "error": "entity_not_found"},
|
|
76
|
+
data={"actionName": "UPDATE_ENTITY"},
|
|
77
|
+
success=False,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
state = await runtime.compose_state(
|
|
81
|
+
message, ["RECENT_MESSAGES", "ACTION_STATE", "ENTITY_INFO"]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
entity_info = f"""
|
|
85
|
+
Entity ID: {entity.id}
|
|
86
|
+
Name: {entity.name or "Unknown"}
|
|
87
|
+
Type: {entity.entity_type or "Unknown"}
|
|
88
|
+
"""
|
|
89
|
+
if entity.metadata:
|
|
90
|
+
entity_info += f"Metadata: {entity.metadata}"
|
|
91
|
+
|
|
92
|
+
template = (
|
|
93
|
+
runtime.character.templates.get("updateEntityTemplate")
|
|
94
|
+
if runtime.character.templates and "updateEntityTemplate" in runtime.character.templates
|
|
95
|
+
else UPDATE_ENTITY_TEMPLATE
|
|
96
|
+
)
|
|
97
|
+
prompt = runtime.compose_prompt(state=state, template=template)
|
|
98
|
+
prompt = prompt.replace("{{entityInfo}}", entity_info)
|
|
99
|
+
|
|
100
|
+
response_text = await runtime.use_model(ModelType.TEXT_LARGE, prompt=prompt)
|
|
101
|
+
parsed_xml = parse_key_value_xml(response_text)
|
|
102
|
+
|
|
103
|
+
if parsed_xml is None:
|
|
104
|
+
raise ValueError("Failed to parse XML response")
|
|
105
|
+
|
|
106
|
+
thought = str(parsed_xml.get("thought", ""))
|
|
107
|
+
target_entity_id_str = str(parsed_xml.get("entity_id", str(entity_id)))
|
|
108
|
+
|
|
109
|
+
target_entity_id = UUID(target_entity_id_str)
|
|
110
|
+
|
|
111
|
+
updates_raw = parsed_xml.get("updates", {})
|
|
112
|
+
updated_fields: list[str] = []
|
|
113
|
+
|
|
114
|
+
if isinstance(updates_raw, dict):
|
|
115
|
+
field_list = updates_raw.get("field", [])
|
|
116
|
+
if isinstance(field_list, dict):
|
|
117
|
+
field_list = [field_list]
|
|
118
|
+
for field_update in field_list:
|
|
119
|
+
if isinstance(field_update, dict):
|
|
120
|
+
field_name = str(field_update.get("name", ""))
|
|
121
|
+
field_value = str(field_update.get("value", ""))
|
|
122
|
+
if field_name and field_value:
|
|
123
|
+
if entity.metadata is None:
|
|
124
|
+
entity.metadata = {}
|
|
125
|
+
entity.metadata[field_name] = field_value
|
|
126
|
+
updated_fields.append(field_name)
|
|
127
|
+
|
|
128
|
+
if not updated_fields:
|
|
129
|
+
return ActionResult(
|
|
130
|
+
text="No fields to update",
|
|
131
|
+
values={"success": True, "noChanges": True},
|
|
132
|
+
data={"actionName": "UPDATE_ENTITY", "thought": thought},
|
|
133
|
+
success=True,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
await runtime.update_entity(entity)
|
|
137
|
+
|
|
138
|
+
response_content = Content(
|
|
139
|
+
text=f"Updated entity fields: {', '.join(updated_fields)}",
|
|
140
|
+
actions=["UPDATE_ENTITY"],
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if callback:
|
|
144
|
+
await callback(response_content)
|
|
145
|
+
|
|
146
|
+
return ActionResult(
|
|
147
|
+
text=f"Updated entity: {', '.join(updated_fields)}",
|
|
148
|
+
values={
|
|
149
|
+
"success": True,
|
|
150
|
+
"entityUpdated": True,
|
|
151
|
+
"entityId": str(target_entity_id),
|
|
152
|
+
"updatedFields": ", ".join(updated_fields),
|
|
153
|
+
},
|
|
154
|
+
data={
|
|
155
|
+
"actionName": "UPDATE_ENTITY",
|
|
156
|
+
"entityId": str(target_entity_id),
|
|
157
|
+
"updatedFields": updated_fields,
|
|
158
|
+
"thought": thought,
|
|
159
|
+
},
|
|
160
|
+
success=True,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def examples(self) -> list[list[ActionExample]]:
|
|
165
|
+
return _convert_spec_examples()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
update_entity_action = Action(
|
|
169
|
+
name=UpdateEntityAction.name,
|
|
170
|
+
similes=UpdateEntityAction().similes,
|
|
171
|
+
description=UpdateEntityAction.description,
|
|
172
|
+
validate=UpdateEntityAction().validate,
|
|
173
|
+
handler=UpdateEntityAction().handler,
|
|
174
|
+
examples=UpdateEntityAction().examples,
|
|
175
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .action import send_to_admin_action
|
|
4
|
+
from .providers import admin_chat_provider, autonomy_status_provider
|
|
5
|
+
from .routes import autonomy_routes
|
|
6
|
+
from .service import AUTONOMY_SERVICE_TYPE, AutonomyService
|
|
7
|
+
from .types import AutonomyConfig, AutonomyStatus
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AutonomyConfig",
|
|
11
|
+
"AutonomyStatus",
|
|
12
|
+
"AutonomyService",
|
|
13
|
+
"AUTONOMY_SERVICE_TYPE",
|
|
14
|
+
"send_to_admin_action",
|
|
15
|
+
"admin_chat_provider",
|
|
16
|
+
"autonomy_status_provider",
|
|
17
|
+
"autonomy_routes",
|
|
18
|
+
]
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from elizaos.types.components import Action, ActionResult, HandlerOptions
|
|
8
|
+
from elizaos.types.memory import Memory
|
|
9
|
+
from elizaos.types.primitives import UUID, Content, as_uuid
|
|
10
|
+
|
|
11
|
+
from .service import AUTONOMY_SERVICE_TYPE, AutonomyService
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from collections.abc import Awaitable, Callable
|
|
15
|
+
|
|
16
|
+
from elizaos.types.runtime import IAgentRuntime
|
|
17
|
+
from elizaos.types.state import State
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
ADMIN_KEYWORDS = (
|
|
21
|
+
"admin",
|
|
22
|
+
"user",
|
|
23
|
+
"tell",
|
|
24
|
+
"notify",
|
|
25
|
+
"inform",
|
|
26
|
+
"update",
|
|
27
|
+
"message",
|
|
28
|
+
"send",
|
|
29
|
+
"communicate",
|
|
30
|
+
"report",
|
|
31
|
+
"alert",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def _validate_send_to_admin(
|
|
36
|
+
runtime: IAgentRuntime,
|
|
37
|
+
message: Memory,
|
|
38
|
+
_state: State | None = None,
|
|
39
|
+
) -> bool:
|
|
40
|
+
autonomy_service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
|
|
41
|
+
if not autonomy_service or not isinstance(autonomy_service, AutonomyService):
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
autonomous_room_id = autonomy_service.get_autonomous_room_id()
|
|
45
|
+
if not autonomous_room_id or message.room_id != autonomous_room_id:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
admin_user_id = runtime.get_setting("ADMIN_USER_ID")
|
|
49
|
+
if not admin_user_id:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
text = (message.content.text or "").lower() if message.content else ""
|
|
53
|
+
if not text:
|
|
54
|
+
return False
|
|
55
|
+
return any(keyword in text for keyword in ADMIN_KEYWORDS)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def _handle_send_to_admin(
|
|
59
|
+
runtime: IAgentRuntime,
|
|
60
|
+
message: Memory,
|
|
61
|
+
state: State | None = None,
|
|
62
|
+
options: HandlerOptions | None = None,
|
|
63
|
+
callback: Callable[[Content], Awaitable[None]] | None = None,
|
|
64
|
+
responses: list[Memory] | None = None,
|
|
65
|
+
) -> ActionResult:
|
|
66
|
+
autonomy_service = runtime.get_service(AUTONOMY_SERVICE_TYPE)
|
|
67
|
+
if not autonomy_service or not isinstance(autonomy_service, AutonomyService):
|
|
68
|
+
return ActionResult(
|
|
69
|
+
success=False,
|
|
70
|
+
text="Autonomy service not available",
|
|
71
|
+
data={"error": "Service unavailable"},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
autonomous_room_id = autonomy_service.get_autonomous_room_id()
|
|
75
|
+
if not autonomous_room_id or message.room_id != autonomous_room_id:
|
|
76
|
+
return ActionResult(
|
|
77
|
+
success=False,
|
|
78
|
+
text="Send to admin only available in autonomous context",
|
|
79
|
+
data={"error": "Invalid context"},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
admin_user_id = runtime.get_setting("ADMIN_USER_ID")
|
|
83
|
+
if not admin_user_id:
|
|
84
|
+
return ActionResult(
|
|
85
|
+
success=False,
|
|
86
|
+
text="No admin user configured. Set ADMIN_USER_ID in settings.",
|
|
87
|
+
data={"error": "No admin configured"},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
admin_messages = await runtime.get_memories(
|
|
91
|
+
{
|
|
92
|
+
"roomId": runtime.agent_id,
|
|
93
|
+
"count": 10,
|
|
94
|
+
"tableName": "memories",
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
target_room_id: UUID
|
|
99
|
+
if admin_messages and len(admin_messages) > 0:
|
|
100
|
+
target_room_id = admin_messages[-1].room_id or runtime.agent_id
|
|
101
|
+
else:
|
|
102
|
+
target_room_id = runtime.agent_id
|
|
103
|
+
|
|
104
|
+
autonomous_thought = message.content.text or "" if message.content else ""
|
|
105
|
+
|
|
106
|
+
if "completed" in autonomous_thought or "finished" in autonomous_thought:
|
|
107
|
+
message_to_admin = (
|
|
108
|
+
f"I've completed a task and wanted to update you. My thoughts: {autonomous_thought}"
|
|
109
|
+
)
|
|
110
|
+
elif (
|
|
111
|
+
"problem" in autonomous_thought
|
|
112
|
+
or "issue" in autonomous_thought
|
|
113
|
+
or "error" in autonomous_thought
|
|
114
|
+
):
|
|
115
|
+
message_to_admin = (
|
|
116
|
+
f"I encountered something that might need your attention: {autonomous_thought}"
|
|
117
|
+
)
|
|
118
|
+
elif "question" in autonomous_thought or "unsure" in autonomous_thought:
|
|
119
|
+
message_to_admin = (
|
|
120
|
+
f"I have a question and would appreciate your guidance: {autonomous_thought}"
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
message_to_admin = f"Autonomous update: {autonomous_thought}"
|
|
124
|
+
|
|
125
|
+
current_time_ms = int(time.time() * 1000)
|
|
126
|
+
admin_message = Memory(
|
|
127
|
+
id=as_uuid(str(uuid.uuid4())),
|
|
128
|
+
entity_id=runtime.agent_id,
|
|
129
|
+
room_id=target_room_id,
|
|
130
|
+
content=Content(
|
|
131
|
+
text=message_to_admin,
|
|
132
|
+
source="autonomy-to-admin",
|
|
133
|
+
),
|
|
134
|
+
created_at=current_time_ms,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
await runtime.create_memory(admin_message, "memories")
|
|
138
|
+
|
|
139
|
+
# Emit MESSAGE_SENT event after creating the admin message
|
|
140
|
+
await runtime.emit_event(
|
|
141
|
+
"MESSAGE_SENT",
|
|
142
|
+
{
|
|
143
|
+
"runtime": runtime,
|
|
144
|
+
"source": "autonomy-to-admin",
|
|
145
|
+
"message": admin_message,
|
|
146
|
+
},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
success_message = f"Message sent to admin in room {str(target_room_id)[:8]}..."
|
|
150
|
+
|
|
151
|
+
if callback:
|
|
152
|
+
await callback(
|
|
153
|
+
Content(
|
|
154
|
+
text=success_message,
|
|
155
|
+
data={
|
|
156
|
+
"adminUserId": str(admin_user_id),
|
|
157
|
+
"targetRoomId": str(target_room_id),
|
|
158
|
+
"messageContent": message_to_admin,
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return ActionResult(
|
|
164
|
+
success=True,
|
|
165
|
+
text=success_message,
|
|
166
|
+
data={
|
|
167
|
+
"adminUserId": str(admin_user_id),
|
|
168
|
+
"targetRoomId": str(target_room_id),
|
|
169
|
+
"messageContent": message_to_admin,
|
|
170
|
+
"sent": True,
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
send_to_admin_action = Action(
|
|
176
|
+
name="SEND_TO_ADMIN",
|
|
177
|
+
description="Send a message directly to the admin user from autonomous context",
|
|
178
|
+
examples=[
|
|
179
|
+
[
|
|
180
|
+
{
|
|
181
|
+
"name": "Agent",
|
|
182
|
+
"content": {
|
|
183
|
+
"text": "I need to update the admin about my progress on the task.",
|
|
184
|
+
"action": "SEND_TO_ADMIN",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "Agent",
|
|
189
|
+
"content": {
|
|
190
|
+
"text": "Message sent to admin successfully.",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
],
|
|
195
|
+
validate=_validate_send_to_admin,
|
|
196
|
+
handler=_handle_send_to_admin,
|
|
197
|
+
)
|