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