@elizaos/python 2.0.0-alpha.3 → 2.0.0-alpha.31
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/elizaos/__init__.py +0 -1
- package/elizaos/advanced_capabilities/__init__.py +6 -41
- package/elizaos/advanced_capabilities/actions/__init__.py +1 -21
- package/elizaos/advanced_capabilities/actions/add_contact.py +24 -13
- package/elizaos/advanced_capabilities/actions/follow_room.py +29 -29
- package/elizaos/advanced_capabilities/actions/image_generation.py +15 -28
- package/elizaos/advanced_capabilities/actions/mute_room.py +15 -28
- package/elizaos/advanced_capabilities/actions/remove_contact.py +17 -3
- package/elizaos/advanced_capabilities/actions/roles.py +17 -30
- package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +70 -15
- package/elizaos/advanced_capabilities/actions/search_contacts.py +17 -3
- package/elizaos/advanced_capabilities/actions/send_message.py +184 -51
- package/elizaos/advanced_capabilities/actions/settings.py +17 -3
- package/elizaos/advanced_capabilities/actions/unfollow_room.py +15 -28
- package/elizaos/advanced_capabilities/actions/unmute_room.py +15 -28
- package/elizaos/advanced_capabilities/actions/update_contact.py +17 -3
- package/elizaos/advanced_capabilities/actions/update_entity.py +17 -3
- package/elizaos/advanced_capabilities/evaluators/__init__.py +2 -9
- package/elizaos/advanced_capabilities/evaluators/reflection.py +3 -132
- package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +5 -201
- package/elizaos/advanced_capabilities/providers/__init__.py +1 -12
- package/elizaos/advanced_capabilities/providers/knowledge.py +23 -3
- package/elizaos/advanced_capabilities/services/__init__.py +2 -9
- package/elizaos/advanced_capabilities/services/rolodex.py +2 -2
- package/elizaos/advanced_memory/actions/reset_session.py +143 -0
- package/elizaos/advanced_memory/evaluators/reflection.py +134 -0
- package/elizaos/advanced_memory/evaluators/relationship_extraction.py +203 -0
- package/elizaos/advanced_memory/memory_service.py +69 -27
- package/elizaos/advanced_memory/plugin.py +2 -1
- package/elizaos/advanced_memory/test_advanced_memory.py +357 -0
- package/elizaos/advanced_memory/types.py +2 -2
- package/elizaos/advanced_planning/actions/schedule_follow_up.py +222 -0
- package/elizaos/advanced_planning/planning_service.py +26 -14
- package/elizaos/basic_capabilities/__init__.py +0 -2
- package/elizaos/basic_capabilities/providers/__init__.py +0 -3
- package/elizaos/basic_capabilities/providers/actions.py +118 -29
- package/elizaos/basic_capabilities/providers/agent_settings.py +64 -0
- package/elizaos/basic_capabilities/providers/character.py +19 -21
- package/elizaos/basic_capabilities/providers/contacts.py +79 -0
- package/elizaos/basic_capabilities/providers/current_time.py +7 -4
- package/elizaos/basic_capabilities/providers/facts.py +87 -0
- package/elizaos/basic_capabilities/providers/follow_ups.py +117 -0
- package/elizaos/basic_capabilities/providers/knowledge.py +96 -0
- package/elizaos/basic_capabilities/providers/recent_messages.py +5 -0
- package/elizaos/basic_capabilities/providers/relationships.py +113 -0
- package/elizaos/basic_capabilities/providers/roles.py +96 -0
- package/elizaos/basic_capabilities/providers/settings.py +56 -0
- package/elizaos/basic_capabilities/providers/time.py +7 -4
- package/elizaos/basic_capabilities/services/embedding.py +10 -7
- package/elizaos/basic_capabilities/services/task.py +3 -3
- package/elizaos/bootstrap/__init__.py +21 -2
- package/elizaos/bootstrap/actions/__init__.py +3 -0
- package/elizaos/bootstrap/actions/reset_session.py +3 -0
- package/elizaos/bootstrap/actions/roles.py +5 -4
- package/elizaos/bootstrap/actions/schedule_follow_up.py +65 -7
- package/elizaos/bootstrap/actions/send_message.py +162 -15
- package/elizaos/bootstrap/autonomy/__init__.py +5 -1
- package/elizaos/bootstrap/autonomy/action.py +161 -0
- package/elizaos/bootstrap/autonomy/evaluators.py +217 -0
- package/elizaos/bootstrap/autonomy/service.py +238 -28
- package/elizaos/bootstrap/plugin.py +7 -0
- package/elizaos/bootstrap/providers/actions.py +118 -27
- package/elizaos/bootstrap/providers/agent_settings.py +1 -0
- package/elizaos/bootstrap/providers/attachments.py +1 -0
- package/elizaos/bootstrap/providers/capabilities.py +1 -0
- package/elizaos/bootstrap/providers/character.py +1 -0
- package/elizaos/bootstrap/providers/choice.py +1 -0
- package/elizaos/bootstrap/providers/contacts.py +1 -0
- package/elizaos/bootstrap/providers/current_time.py +8 -2
- package/elizaos/bootstrap/providers/entities.py +1 -0
- package/elizaos/bootstrap/providers/evaluators.py +1 -0
- package/elizaos/bootstrap/providers/facts.py +1 -0
- package/elizaos/bootstrap/providers/follow_ups.py +1 -0
- package/elizaos/bootstrap/providers/knowledge.py +26 -3
- package/elizaos/bootstrap/providers/providers_list.py +1 -0
- package/elizaos/bootstrap/providers/recent_messages.py +5 -0
- package/elizaos/bootstrap/providers/relationships.py +20 -13
- package/elizaos/bootstrap/providers/roles.py +1 -0
- package/elizaos/bootstrap/providers/settings.py +1 -0
- package/elizaos/bootstrap/providers/time.py +8 -4
- package/elizaos/bootstrap/providers/world.py +1 -0
- package/elizaos/bootstrap/services/embedding.py +206 -8
- package/elizaos/bootstrap/services/rolodex.py +2 -2
- package/elizaos/bootstrap/services/task.py +3 -3
- package/elizaos/deterministic.py +193 -0
- package/elizaos/generated/__init__.py +1 -0
- package/elizaos/generated/action_docs.py +3181 -0
- package/elizaos/generated/spec_helpers.py +175 -0
- package/elizaos/media/mime.py +4 -4
- package/elizaos/media/search.py +23 -23
- package/elizaos/runtime.py +223 -64
- package/elizaos/services/hook_service.py +3 -3
- package/elizaos/services/message_service.py +175 -29
- package/elizaos/types/components.py +2 -2
- package/elizaos/types/generated/__init__.py +12 -0
- package/elizaos/types/generated/eliza/v1/agent_pb2.py +63 -0
- package/elizaos/types/generated/eliza/v1/agent_pb2.pyi +159 -0
- package/elizaos/types/generated/eliza/v1/components_pb2.py +65 -0
- package/elizaos/types/generated/eliza/v1/components_pb2.pyi +160 -0
- package/elizaos/types/generated/eliza/v1/database_pb2.py +78 -0
- package/elizaos/types/generated/eliza/v1/database_pb2.pyi +305 -0
- package/elizaos/types/generated/eliza/v1/environment_pb2.py +58 -0
- package/elizaos/types/generated/eliza/v1/environment_pb2.pyi +135 -0
- package/elizaos/types/generated/eliza/v1/events_pb2.py +82 -0
- package/elizaos/types/generated/eliza/v1/events_pb2.pyi +322 -0
- package/elizaos/types/generated/eliza/v1/ipc_pb2.py +113 -0
- package/elizaos/types/generated/eliza/v1/ipc_pb2.pyi +367 -0
- package/elizaos/types/generated/eliza/v1/knowledge_pb2.py +41 -0
- package/elizaos/types/generated/eliza/v1/knowledge_pb2.pyi +26 -0
- package/elizaos/types/generated/eliza/v1/memory_pb2.py +55 -0
- package/elizaos/types/generated/eliza/v1/memory_pb2.pyi +111 -0
- package/elizaos/types/generated/eliza/v1/message_service_pb2.py +48 -0
- package/elizaos/types/generated/eliza/v1/message_service_pb2.pyi +69 -0
- package/elizaos/types/generated/eliza/v1/messaging_pb2.py +51 -0
- package/elizaos/types/generated/eliza/v1/messaging_pb2.pyi +97 -0
- package/elizaos/types/generated/eliza/v1/model_pb2.py +84 -0
- package/elizaos/types/generated/eliza/v1/model_pb2.pyi +280 -0
- package/elizaos/types/generated/eliza/v1/payment_pb2.py +44 -0
- package/elizaos/types/generated/eliza/v1/payment_pb2.pyi +70 -0
- package/elizaos/types/generated/eliza/v1/plugin_pb2.py +68 -0
- package/elizaos/types/generated/eliza/v1/plugin_pb2.pyi +145 -0
- package/elizaos/types/generated/eliza/v1/primitives_pb2.py +48 -0
- package/elizaos/types/generated/eliza/v1/primitives_pb2.pyi +92 -0
- package/elizaos/types/generated/eliza/v1/prompts_pb2.py +52 -0
- package/elizaos/types/generated/eliza/v1/prompts_pb2.pyi +74 -0
- package/elizaos/types/generated/eliza/v1/service_interfaces_pb2.py +211 -0
- package/elizaos/types/generated/eliza/v1/service_interfaces_pb2.pyi +1296 -0
- package/elizaos/types/generated/eliza/v1/service_pb2.py +42 -0
- package/elizaos/types/generated/eliza/v1/service_pb2.pyi +69 -0
- package/elizaos/types/generated/eliza/v1/settings_pb2.py +58 -0
- package/elizaos/types/generated/eliza/v1/settings_pb2.pyi +85 -0
- package/elizaos/types/generated/eliza/v1/state_pb2.py +60 -0
- package/elizaos/types/generated/eliza/v1/state_pb2.pyi +114 -0
- package/elizaos/types/generated/eliza/v1/task_pb2.py +42 -0
- package/elizaos/types/generated/eliza/v1/task_pb2.pyi +58 -0
- package/elizaos/types/generated/eliza/v1/tee_pb2.py +52 -0
- package/elizaos/types/generated/eliza/v1/tee_pb2.pyi +90 -0
- package/elizaos/types/generated/eliza/v1/testing_pb2.py +39 -0
- package/elizaos/types/generated/eliza/v1/testing_pb2.pyi +23 -0
- package/elizaos/types/model.py +33 -3
- package/elizaos/types/primitives.py +3 -3
- package/elizaos/types/runtime.py +17 -3
- package/elizaos/types/state.py +2 -2
- package/elizaos/utils/streaming.py +3 -3
- package/elizaos/utils/validation.py +76 -0
- package/package.json +4 -3
- package/pyproject.toml +1 -2
- package/requirements-dev.lock +2 -2
- package/requirements.in +1 -2
- package/requirements.lock +2 -2
- package/tests/test_action_parameters.py +2 -3
- package/tests/test_actions_provider_examples.py +58 -1
- package/tests/test_advanced_memory_behavior.py +0 -2
- package/tests/test_advanced_memory_flag.py +0 -2
- package/tests/test_advanced_planning_behavior.py +11 -5
- package/tests/test_async_embedding.py +124 -0
- package/tests/test_autonomy.py +24 -3
- package/tests/test_history_compaction.py +104 -0
- package/tests/test_memory_bounds.py +115 -0
- package/tests/test_runtime.py +8 -17
- package/tests/test_schedule_follow_up_action.py +260 -0
- package/tests/test_send_message_action_targets.py +114 -0
- package/tests/test_settings_crypto.py +0 -2
- package/tests/test_validation.py +141 -0
- package/tests/verify_memory_architecture.py +192 -0
- package/uv.lock +1565 -0
- package/elizaos/basic_capabilities/providers/capabilities.py +0 -62
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
4
7
|
|
|
5
8
|
from elizaos.types import ModelType, Service, ServiceType
|
|
9
|
+
from elizaos.types.events import EventType
|
|
6
10
|
|
|
7
11
|
if TYPE_CHECKING:
|
|
8
12
|
from elizaos.types import IAgentRuntime
|
|
@@ -18,14 +22,28 @@ class EmbeddingService(Service):
|
|
|
18
22
|
|
|
19
23
|
def __init__(self) -> None:
|
|
20
24
|
self._runtime: IAgentRuntime | None = None
|
|
21
|
-
self._cache:
|
|
25
|
+
self._cache: OrderedDict[str, list[float]] = OrderedDict()
|
|
22
26
|
self._cache_enabled: bool = True
|
|
23
27
|
self._max_cache_size: int = 1000
|
|
28
|
+
self._queue_max_size: int = 1000
|
|
29
|
+
self._queue: asyncio.Queue[tuple[str | None, Any]] = asyncio.Queue(
|
|
30
|
+
maxsize=self._queue_max_size
|
|
31
|
+
)
|
|
32
|
+
self._pending_payload_keys: set[str] = set()
|
|
33
|
+
self._worker_task: asyncio.Task | None = None
|
|
24
34
|
|
|
25
35
|
@classmethod
|
|
26
36
|
async def start(cls, runtime: IAgentRuntime) -> EmbeddingService:
|
|
27
37
|
service = cls()
|
|
28
38
|
service._runtime = runtime
|
|
39
|
+
|
|
40
|
+
# Register event handler
|
|
41
|
+
event_name = EventType.Name(EventType.EVENT_TYPE_EMBEDDING_GENERATION_REQUESTED)
|
|
42
|
+
runtime.register_event(event_name, service._handle_embedding_request)
|
|
43
|
+
|
|
44
|
+
# Start worker
|
|
45
|
+
service._worker_task = asyncio.create_task(service._worker())
|
|
46
|
+
|
|
29
47
|
runtime.logger.info(
|
|
30
48
|
"Embedding service started",
|
|
31
49
|
src="service:embedding",
|
|
@@ -34,6 +52,12 @@ class EmbeddingService(Service):
|
|
|
34
52
|
return service
|
|
35
53
|
|
|
36
54
|
async def stop(self) -> None:
|
|
55
|
+
if self._worker_task:
|
|
56
|
+
self._worker_task.cancel()
|
|
57
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
58
|
+
await self._worker_task
|
|
59
|
+
self._worker_task = None
|
|
60
|
+
|
|
37
61
|
if self._runtime:
|
|
38
62
|
self._runtime.logger.info(
|
|
39
63
|
"Embedding service stopped",
|
|
@@ -41,6 +65,8 @@ class EmbeddingService(Service):
|
|
|
41
65
|
agentId=str(self._runtime.agent_id),
|
|
42
66
|
)
|
|
43
67
|
self._cache.clear()
|
|
68
|
+
self._pending_payload_keys.clear()
|
|
69
|
+
self._queue = asyncio.Queue(maxsize=self._queue_max_size)
|
|
44
70
|
self._runtime = None
|
|
45
71
|
|
|
46
72
|
# Max characters for embedding input (~8K tokens at ~4 chars/token)
|
|
@@ -51,7 +77,9 @@ class EmbeddingService(Service):
|
|
|
51
77
|
raise ValueError("Embedding service not started - no runtime available")
|
|
52
78
|
|
|
53
79
|
if self._cache_enabled and text in self._cache:
|
|
54
|
-
|
|
80
|
+
embedding = self._cache.pop(text)
|
|
81
|
+
self._cache[text] = embedding
|
|
82
|
+
return embedding
|
|
55
83
|
|
|
56
84
|
# Truncate to stay within embedding model token limits
|
|
57
85
|
embed_text = text
|
|
@@ -87,9 +115,10 @@ class EmbeddingService(Service):
|
|
|
87
115
|
return embeddings
|
|
88
116
|
|
|
89
117
|
def _add_to_cache(self, text: str, embedding: list[float]) -> None:
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
118
|
+
if text in self._cache:
|
|
119
|
+
self._cache.pop(text)
|
|
120
|
+
elif len(self._cache) >= self._max_cache_size:
|
|
121
|
+
self._cache.popitem(last=False)
|
|
93
122
|
self._cache[text] = embedding
|
|
94
123
|
|
|
95
124
|
def clear_cache(self) -> None:
|
|
@@ -105,8 +134,7 @@ class EmbeddingService(Service):
|
|
|
105
134
|
raise ValueError("Cache size must be positive")
|
|
106
135
|
self._max_cache_size = size
|
|
107
136
|
while len(self._cache) > self._max_cache_size:
|
|
108
|
-
|
|
109
|
-
del self._cache[oldest_key]
|
|
137
|
+
self._cache.popitem(last=False)
|
|
110
138
|
|
|
111
139
|
async def similarity(self, text1: str, text2: str) -> float:
|
|
112
140
|
embedding1 = await self.embed(text1)
|
|
@@ -120,3 +148,173 @@ class EmbeddingService(Service):
|
|
|
120
148
|
return 0.0
|
|
121
149
|
|
|
122
150
|
return dot_product / (magnitude1 * magnitude2)
|
|
151
|
+
|
|
152
|
+
async def _handle_embedding_request(self, payload: Any) -> None:
|
|
153
|
+
"""Handle embedding generation request event."""
|
|
154
|
+
payload_key = self._get_payload_key(payload)
|
|
155
|
+
if payload_key is not None:
|
|
156
|
+
if payload_key in self._pending_payload_keys:
|
|
157
|
+
return
|
|
158
|
+
self._pending_payload_keys.add(payload_key)
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
await self._queue.put((payload_key, payload))
|
|
162
|
+
except Exception:
|
|
163
|
+
if payload_key is not None:
|
|
164
|
+
self._pending_payload_keys.discard(payload_key)
|
|
165
|
+
raise
|
|
166
|
+
|
|
167
|
+
def _get_payload_key(self, payload: Any) -> str | None:
|
|
168
|
+
memory_data = getattr(payload, "memory", None)
|
|
169
|
+
if memory_data is None:
|
|
170
|
+
extra = getattr(payload, "extra", None)
|
|
171
|
+
if hasattr(extra, "__getitem__"):
|
|
172
|
+
with contextlib.suppress(Exception):
|
|
173
|
+
if "memory" in extra:
|
|
174
|
+
memory_data = extra["memory"]
|
|
175
|
+
if memory_data is None and isinstance(payload, dict):
|
|
176
|
+
memory_data = payload.get("memory")
|
|
177
|
+
if memory_data is None:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
if isinstance(memory_data, dict):
|
|
181
|
+
memory_id = memory_data.get("id")
|
|
182
|
+
else:
|
|
183
|
+
memory_id = getattr(memory_data, "id", None)
|
|
184
|
+
if memory_id is None:
|
|
185
|
+
return None
|
|
186
|
+
return str(memory_id)
|
|
187
|
+
|
|
188
|
+
async def _worker(self) -> None:
|
|
189
|
+
"""Background worker for processing embedding requests."""
|
|
190
|
+
while True:
|
|
191
|
+
try:
|
|
192
|
+
payload_key, payload = await self._queue.get()
|
|
193
|
+
except asyncio.CancelledError:
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
await self._process_embedding_request(payload)
|
|
198
|
+
except Exception as e:
|
|
199
|
+
if self._runtime:
|
|
200
|
+
self._runtime.logger.error(f"Error in embedding worker: {e}", exc_info=True)
|
|
201
|
+
finally:
|
|
202
|
+
if payload_key is not None:
|
|
203
|
+
self._pending_payload_keys.discard(payload_key)
|
|
204
|
+
self._queue.task_done()
|
|
205
|
+
|
|
206
|
+
async def _process_embedding_request(self, payload: Any) -> None:
|
|
207
|
+
from elizaos.types.events import EventType
|
|
208
|
+
from elizaos.types.memory import Memory
|
|
209
|
+
|
|
210
|
+
# Extract memory from payload
|
|
211
|
+
# Handle both protobuf object and dict/wrapper
|
|
212
|
+
memory_data = None
|
|
213
|
+
if hasattr(payload, "memory"): # specific payload
|
|
214
|
+
memory_data = payload.memory
|
|
215
|
+
elif hasattr(payload, "extra") and hasattr(
|
|
216
|
+
payload.extra, "__getitem__"
|
|
217
|
+
): # generic event payload
|
|
218
|
+
try:
|
|
219
|
+
# Check if 'memory' is in extra
|
|
220
|
+
# payload.extra might be a Struct or dict
|
|
221
|
+
if "memory" in payload.extra:
|
|
222
|
+
from elizaos.runtime import _struct_value_to_python
|
|
223
|
+
|
|
224
|
+
mem_val = payload.extra["memory"]
|
|
225
|
+
if hasattr(mem_val, "struct_value"):
|
|
226
|
+
if mem_val.HasField("struct_value"):
|
|
227
|
+
memory_data = _struct_value_to_python(mem_val)
|
|
228
|
+
else:
|
|
229
|
+
memory_data = mem_val
|
|
230
|
+
else:
|
|
231
|
+
memory_data = mem_val
|
|
232
|
+
except Exception:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
if not memory_data:
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
# Convert to Memory object if needed
|
|
239
|
+
if isinstance(memory_data, dict):
|
|
240
|
+
memory = Memory(
|
|
241
|
+
id=memory_data.get("id"),
|
|
242
|
+
content=memory_data.get("content"),
|
|
243
|
+
room_id=memory_data.get("roomId") or memory_data.get("room_id"),
|
|
244
|
+
entity_id=memory_data.get("entityId")
|
|
245
|
+
or memory_data.get("entity_id")
|
|
246
|
+
or memory_data.get("userId")
|
|
247
|
+
or memory_data.get("user_id"),
|
|
248
|
+
agent_id=memory_data.get("agentId") or memory_data.get("agent_id"),
|
|
249
|
+
)
|
|
250
|
+
if "embedding" in memory_data:
|
|
251
|
+
memory.embedding = memory_data["embedding"]
|
|
252
|
+
if "metadata" in memory_data:
|
|
253
|
+
memory.metadata = memory_data["metadata"]
|
|
254
|
+
else:
|
|
255
|
+
memory = memory_data
|
|
256
|
+
|
|
257
|
+
if not memory.id:
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
if memory.embedding and len(memory.embedding) > 0:
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
text = (
|
|
264
|
+
memory.content.text
|
|
265
|
+
if hasattr(memory.content, "text")
|
|
266
|
+
else getattr(memory.content, "text", "")
|
|
267
|
+
)
|
|
268
|
+
if not text:
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
embedding_source_text = text
|
|
272
|
+
|
|
273
|
+
# Intent generation logic
|
|
274
|
+
if len(text) > 20:
|
|
275
|
+
has_intent = False
|
|
276
|
+
if memory.metadata and isinstance(memory.metadata, dict):
|
|
277
|
+
has_intent = "intent" in memory.metadata
|
|
278
|
+
|
|
279
|
+
if not has_intent:
|
|
280
|
+
prompt = (
|
|
281
|
+
"Analyze the following message and extract the core user intent or a summary "
|
|
282
|
+
"of what they are asking/saying. Return ONLY the intent text.\n"
|
|
283
|
+
f'Message:\n"{text}"\n\nIntent:'
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
output = await self._runtime.use_model(ModelType.TEXT_SMALL, prompt=prompt)
|
|
288
|
+
|
|
289
|
+
intent = str(output).strip()
|
|
290
|
+
if intent:
|
|
291
|
+
embedding_source_text = intent
|
|
292
|
+
# Update metadata
|
|
293
|
+
# Use custom metadata for intent
|
|
294
|
+
memory.metadata.custom.custom_data["intent"] = intent
|
|
295
|
+
except Exception as e:
|
|
296
|
+
self._runtime.logger.warning(f"Failed to generate intent: {e}")
|
|
297
|
+
|
|
298
|
+
# Generate embedding
|
|
299
|
+
try:
|
|
300
|
+
embedding = await self.embed(embedding_source_text)
|
|
301
|
+
# Protobuf repeated field assignment must extend or use slice
|
|
302
|
+
if hasattr(memory.embedding, "extend"): # It's a repeated field
|
|
303
|
+
del memory.embedding[:]
|
|
304
|
+
memory.embedding.extend(embedding)
|
|
305
|
+
else:
|
|
306
|
+
# If it's a list (unlikely based on error)
|
|
307
|
+
memory.embedding = embedding
|
|
308
|
+
|
|
309
|
+
# Update in DB
|
|
310
|
+
if getattr(self._runtime, "_adapter", None):
|
|
311
|
+
await self._runtime._adapter.update_memory(memory)
|
|
312
|
+
|
|
313
|
+
# Emit completion
|
|
314
|
+
await self._runtime.emit_event(
|
|
315
|
+
EventType.Name(EventType.EVENT_TYPE_EMBEDDING_GENERATION_COMPLETED),
|
|
316
|
+
{"source": "embedding_service", "memory_id": str(memory.id)},
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
except Exception as e:
|
|
320
|
+
self._runtime.logger.error(f"Failed to generate embedding: {e}")
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from enum import
|
|
5
|
+
from enum import StrEnum
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
from uuid import UUID
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
|
|
12
12
|
from elizaos.types import IAgentRuntime
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class ContactCategory(
|
|
15
|
+
class ContactCategory(StrEnum):
|
|
16
16
|
FRIEND = "friend"
|
|
17
17
|
FAMILY = "family"
|
|
18
18
|
COLLEAGUE = "colleague"
|
|
@@ -14,7 +14,7 @@ import asyncio
|
|
|
14
14
|
import contextlib
|
|
15
15
|
import time
|
|
16
16
|
from dataclasses import dataclass
|
|
17
|
-
from enum import
|
|
17
|
+
from enum import StrEnum
|
|
18
18
|
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
19
19
|
from uuid import UUID, uuid4
|
|
20
20
|
|
|
@@ -27,7 +27,7 @@ if TYPE_CHECKING:
|
|
|
27
27
|
TICK_INTERVAL_MS = 1000
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class TaskStatus(
|
|
30
|
+
class TaskStatus(StrEnum):
|
|
31
31
|
"""Task status enum."""
|
|
32
32
|
|
|
33
33
|
PENDING = "pending"
|
|
@@ -37,7 +37,7 @@ class TaskStatus(str, Enum):
|
|
|
37
37
|
CANCELLED = "cancelled"
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
class TaskPriority(
|
|
40
|
+
class TaskPriority(StrEnum):
|
|
41
41
|
"""Task priority enum."""
|
|
42
42
|
|
|
43
43
|
LOW = "low"
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
from hashlib import sha256
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
DEFAULT_TIME_BUCKET_MS = 5 * 60 * 1000
|
|
9
|
+
|
|
10
|
+
SeedPart = str | int | float | bool | None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RuntimeLike(Protocol):
|
|
14
|
+
@property
|
|
15
|
+
def agent_id(self) -> object: ...
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def character(self) -> object: ...
|
|
19
|
+
|
|
20
|
+
def get_setting(self, key: str) -> object | None: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _normalize_seed_part(part: SeedPart) -> str:
|
|
24
|
+
if part is None:
|
|
25
|
+
return "none"
|
|
26
|
+
return str(part)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _coerce_non_empty_string(value: object | None) -> str | None:
|
|
30
|
+
if value is None:
|
|
31
|
+
return None
|
|
32
|
+
as_text = str(value).strip()
|
|
33
|
+
if not as_text:
|
|
34
|
+
return None
|
|
35
|
+
return as_text
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_field(obj: object | None, *names: str) -> object | None:
|
|
39
|
+
if obj is None:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
if isinstance(obj, dict):
|
|
43
|
+
for name in names:
|
|
44
|
+
if name in obj:
|
|
45
|
+
value = obj[name]
|
|
46
|
+
if value is not None and value != "":
|
|
47
|
+
return value
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
for name in names:
|
|
51
|
+
value = getattr(obj, name, None)
|
|
52
|
+
if value is not None and value != "":
|
|
53
|
+
return value
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def build_deterministic_seed(parts: list[SeedPart]) -> str:
|
|
58
|
+
return "|".join(_normalize_seed_part(part) for part in parts)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def deterministic_hex(seed: str, surface: str, length: int = 16) -> str:
|
|
62
|
+
if length <= 0:
|
|
63
|
+
return ""
|
|
64
|
+
|
|
65
|
+
output = ""
|
|
66
|
+
counter = 0
|
|
67
|
+
while len(output) < length:
|
|
68
|
+
payload = f"{seed}|{surface}|{counter}".encode()
|
|
69
|
+
output += sha256(payload).hexdigest()
|
|
70
|
+
counter += 1
|
|
71
|
+
return output[:length]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def deterministic_int(seed: str, surface: str, max_exclusive: int) -> int:
|
|
75
|
+
if max_exclusive <= 1:
|
|
76
|
+
return 0
|
|
77
|
+
value = int(deterministic_hex(seed, surface, 12), 16)
|
|
78
|
+
return value % max_exclusive
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def deterministic_uuid(seed: str, surface: str) -> str:
|
|
82
|
+
return str(UUID(hex=deterministic_hex(seed, surface, 32)))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def parse_boolean_setting(value: object | None) -> bool:
|
|
86
|
+
if isinstance(value, bool):
|
|
87
|
+
return value
|
|
88
|
+
if isinstance(value, (int, float)):
|
|
89
|
+
return value != 0
|
|
90
|
+
if isinstance(value, str):
|
|
91
|
+
normalized = value.strip().lower()
|
|
92
|
+
return normalized in ("1", "true", "yes", "on", "enabled")
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def parse_positive_int_setting(value: object | None, fallback: int) -> int:
|
|
97
|
+
if isinstance(value, bool):
|
|
98
|
+
return fallback
|
|
99
|
+
if isinstance(value, (int, float)):
|
|
100
|
+
numeric = int(value)
|
|
101
|
+
return numeric if numeric > 0 else fallback
|
|
102
|
+
if isinstance(value, str):
|
|
103
|
+
try:
|
|
104
|
+
numeric = int(float(value))
|
|
105
|
+
return numeric if numeric > 0 else fallback
|
|
106
|
+
except ValueError:
|
|
107
|
+
return fallback
|
|
108
|
+
return fallback
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def build_conversation_seed(
|
|
112
|
+
runtime: RuntimeLike,
|
|
113
|
+
message: object | None,
|
|
114
|
+
state: object | None,
|
|
115
|
+
surface: str,
|
|
116
|
+
*,
|
|
117
|
+
bucket_ms: int | None = None,
|
|
118
|
+
now_ms: int | None = None,
|
|
119
|
+
) -> str:
|
|
120
|
+
now_ms_value = now_ms if now_ms is not None else int(datetime.now(UTC).timestamp() * 1000)
|
|
121
|
+
|
|
122
|
+
state_data = _get_field(state, "data")
|
|
123
|
+
room_obj = _get_field(state_data, "room")
|
|
124
|
+
world_obj = _get_field(state_data, "world")
|
|
125
|
+
|
|
126
|
+
room_id = (
|
|
127
|
+
_coerce_non_empty_string(_get_field(room_obj, "id"))
|
|
128
|
+
or _coerce_non_empty_string(_get_field(state_data, "room_id", "roomId"))
|
|
129
|
+
or _coerce_non_empty_string(_get_field(message, "room_id", "roomId"))
|
|
130
|
+
or "room:none"
|
|
131
|
+
)
|
|
132
|
+
world_id = (
|
|
133
|
+
_coerce_non_empty_string(_get_field(world_obj, "id"))
|
|
134
|
+
or _coerce_non_empty_string(_get_field(room_obj, "world_id", "worldId"))
|
|
135
|
+
or _coerce_non_empty_string(_get_field(state_data, "world_id", "worldId"))
|
|
136
|
+
or _coerce_non_empty_string(_get_field(message, "world_id", "worldId"))
|
|
137
|
+
or "world:none"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
character_obj = _get_field(runtime, "character")
|
|
141
|
+
character_id = (
|
|
142
|
+
_coerce_non_empty_string(_get_field(character_obj, "id"))
|
|
143
|
+
or _coerce_non_empty_string(_get_field(runtime, "agent_id"))
|
|
144
|
+
or "agent:none"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
epoch_bucket = 0
|
|
148
|
+
if bucket_ms and bucket_ms > 0:
|
|
149
|
+
epoch_bucket = now_ms_value // bucket_ms
|
|
150
|
+
|
|
151
|
+
return build_deterministic_seed(
|
|
152
|
+
[
|
|
153
|
+
"eliza-prompt-cache-v1",
|
|
154
|
+
world_id,
|
|
155
|
+
room_id,
|
|
156
|
+
character_id,
|
|
157
|
+
epoch_bucket,
|
|
158
|
+
surface,
|
|
159
|
+
]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_prompt_reference_datetime(
|
|
164
|
+
runtime: RuntimeLike,
|
|
165
|
+
message: object | None,
|
|
166
|
+
state: object | None,
|
|
167
|
+
surface: str,
|
|
168
|
+
*,
|
|
169
|
+
now: datetime | None = None,
|
|
170
|
+
) -> datetime:
|
|
171
|
+
now_utc = now.astimezone(UTC) if now is not None else datetime.now(UTC)
|
|
172
|
+
deterministic_enabled = parse_boolean_setting(
|
|
173
|
+
runtime.get_setting("PROMPT_CACHE_DETERMINISTIC_TIME")
|
|
174
|
+
)
|
|
175
|
+
if not deterministic_enabled:
|
|
176
|
+
return now_utc
|
|
177
|
+
|
|
178
|
+
bucket_ms = parse_positive_int_setting(
|
|
179
|
+
runtime.get_setting("PROMPT_CACHE_TIME_BUCKET_MS"),
|
|
180
|
+
DEFAULT_TIME_BUCKET_MS,
|
|
181
|
+
)
|
|
182
|
+
now_ms = int(now_utc.timestamp() * 1000)
|
|
183
|
+
seed = build_conversation_seed(
|
|
184
|
+
runtime,
|
|
185
|
+
message,
|
|
186
|
+
state,
|
|
187
|
+
surface,
|
|
188
|
+
bucket_ms=bucket_ms,
|
|
189
|
+
now_ms=now_ms,
|
|
190
|
+
)
|
|
191
|
+
bucket_start = (now_ms // bucket_ms) * bucket_ms
|
|
192
|
+
offset = deterministic_int(seed, "time-offset-ms", bucket_ms)
|
|
193
|
+
return datetime.fromtimestamp((bucket_start + offset) / 1000, tz=UTC)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Auto-generated module package."""
|