@elizaos/python 2.0.0-alpha.10 → 2.0.0-alpha.26
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 +21 -11
- package/elizaos/advanced_capabilities/actions/follow_room.py +28 -28
- package/elizaos/advanced_capabilities/actions/image_generation.py +13 -26
- package/elizaos/advanced_capabilities/actions/mute_room.py +13 -26
- package/elizaos/advanced_capabilities/actions/remove_contact.py +16 -2
- package/elizaos/advanced_capabilities/actions/roles.py +13 -27
- 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 +183 -50
- package/elizaos/advanced_capabilities/actions/settings.py +16 -2
- package/elizaos/advanced_capabilities/actions/unfollow_room.py +13 -26
- package/elizaos/advanced_capabilities/actions/unmute_room.py +13 -26
- package/elizaos/advanced_capabilities/actions/update_contact.py +16 -2
- package/elizaos/advanced_capabilities/actions/update_entity.py +16 -2
- 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 +24 -3
- package/elizaos/advanced_capabilities/services/__init__.py +2 -9
- package/elizaos/advanced_memory/actions/reset_session.py +11 -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 +15 -17
- package/elizaos/advanced_memory/test_advanced_memory.py +357 -0
- 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 +97 -0
- package/elizaos/basic_capabilities/providers/relationships.py +107 -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/bootstrap/__init__.py +21 -2
- 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 +27 -3
- package/elizaos/bootstrap/providers/providers_list.py +1 -0
- package/elizaos/bootstrap/providers/relationships.py +1 -0
- 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 +156 -1
- 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 +2 -2
- package/elizaos/media/search.py +23 -23
- package/elizaos/runtime.py +215 -57
- 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 +30 -0
- package/elizaos/types/runtime.py +6 -2
- package/elizaos/utils/validation.py +76 -0
- package/package.json +3 -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_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
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from elizaos.types.memory import Memory
|
|
4
|
+
from elizaos.types.primitives import Content
|
|
5
|
+
from elizaos.utils.validation import validate_action_keywords, validate_action_regex
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def create_mock_memory(text: str, id: str = "1") -> Memory:
|
|
9
|
+
return Memory(
|
|
10
|
+
id=id,
|
|
11
|
+
entity_id="user1",
|
|
12
|
+
room_id="room1",
|
|
13
|
+
agent_id="agent1",
|
|
14
|
+
content=Content(text=text),
|
|
15
|
+
created_at=int(time.time() * 1000),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_validate_action_keywords():
|
|
20
|
+
mock_message = Memory(
|
|
21
|
+
id="123",
|
|
22
|
+
entity_id="user1",
|
|
23
|
+
room_id="room1",
|
|
24
|
+
agent_id="agent1",
|
|
25
|
+
content=Content(text="Hello world"),
|
|
26
|
+
created_at=int(time.time() * 1000),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
mock_recent_messages = [
|
|
30
|
+
Memory(
|
|
31
|
+
id="1",
|
|
32
|
+
entity_id="user1",
|
|
33
|
+
room_id="room1",
|
|
34
|
+
agent_id="agent1",
|
|
35
|
+
content=Content(text="Previous message 1"),
|
|
36
|
+
created_at=0,
|
|
37
|
+
),
|
|
38
|
+
Memory(
|
|
39
|
+
id="2",
|
|
40
|
+
entity_id="user1",
|
|
41
|
+
room_id="room1",
|
|
42
|
+
agent_id="agent1",
|
|
43
|
+
content=Content(text="Previous message 2"),
|
|
44
|
+
created_at=0,
|
|
45
|
+
),
|
|
46
|
+
Memory(
|
|
47
|
+
id="3",
|
|
48
|
+
entity_id="user1",
|
|
49
|
+
room_id="room1",
|
|
50
|
+
agent_id="agent1",
|
|
51
|
+
content=Content(text="Crypto is cool"),
|
|
52
|
+
created_at=0,
|
|
53
|
+
),
|
|
54
|
+
Memory(
|
|
55
|
+
id="4",
|
|
56
|
+
entity_id="user1",
|
|
57
|
+
room_id="room1",
|
|
58
|
+
agent_id="agent1",
|
|
59
|
+
content=Content(text="Another message"),
|
|
60
|
+
created_at=0,
|
|
61
|
+
),
|
|
62
|
+
Memory(
|
|
63
|
+
id="5",
|
|
64
|
+
entity_id="user1",
|
|
65
|
+
room_id="room1",
|
|
66
|
+
agent_id="agent1",
|
|
67
|
+
content=Content(text="Last one"),
|
|
68
|
+
created_at=0,
|
|
69
|
+
),
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
# 1. Keyword in current message
|
|
73
|
+
msg = create_mock_memory("I want to transfer sol", "124")
|
|
74
|
+
assert validate_action_keywords(msg, [], ["transfer"])
|
|
75
|
+
|
|
76
|
+
# 2. Keyword in recent messages
|
|
77
|
+
assert validate_action_keywords(mock_message, mock_recent_messages, ["crypto"])
|
|
78
|
+
|
|
79
|
+
# 3. Keyword not found
|
|
80
|
+
assert not validate_action_keywords(mock_message, mock_recent_messages, ["banana"])
|
|
81
|
+
|
|
82
|
+
# 4. Case insensitive
|
|
83
|
+
msg_upper = create_mock_memory("I want to TRANSFER sol", "125")
|
|
84
|
+
assert validate_action_keywords(msg_upper, [], ["transfer"])
|
|
85
|
+
|
|
86
|
+
# 5. Empty keywords list
|
|
87
|
+
assert not validate_action_keywords(mock_message, mock_recent_messages, [])
|
|
88
|
+
|
|
89
|
+
# 6. Partial match
|
|
90
|
+
msg_partial = Memory(
|
|
91
|
+
id="126",
|
|
92
|
+
entity_id="user1",
|
|
93
|
+
room_id="room1",
|
|
94
|
+
agent_id="agent1",
|
|
95
|
+
content=Content(text="cryptography"),
|
|
96
|
+
created_at=0,
|
|
97
|
+
)
|
|
98
|
+
assert validate_action_keywords(msg_partial, [], ["crypto"])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_validate_action_regex():
|
|
102
|
+
mock_message = create_mock_memory("Hello world", "123")
|
|
103
|
+
mock_recent_messages = [
|
|
104
|
+
create_mock_memory("Previous message 1", "1"),
|
|
105
|
+
create_mock_memory("Previous message 2", "2"),
|
|
106
|
+
create_mock_memory("Crypto is cool", "3"),
|
|
107
|
+
create_mock_memory("Another message", "4"),
|
|
108
|
+
create_mock_memory("Last one", "5"),
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
# Regex in current message
|
|
112
|
+
msg = create_mock_memory("Transfer 100 SOL")
|
|
113
|
+
# Default re.search is case-sensitive
|
|
114
|
+
assert not validate_action_regex(msg, [], r"transfer \d+ sol")
|
|
115
|
+
# Use inline flag for case-insensitive
|
|
116
|
+
assert validate_action_regex(msg, [], r"(?i)transfer \d+ sol")
|
|
117
|
+
|
|
118
|
+
# Regex in recent messages
|
|
119
|
+
assert validate_action_regex(mock_message, mock_recent_messages, r"(?i)crypto")
|
|
120
|
+
|
|
121
|
+
# No match
|
|
122
|
+
assert not validate_action_regex(mock_message, mock_recent_messages, r"banana")
|
|
123
|
+
|
|
124
|
+
# Complex regex
|
|
125
|
+
msg = create_mock_memory("user@example.com")
|
|
126
|
+
assert validate_action_regex(msg, [], r"^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$") # Empty pattern
|
|
127
|
+
assert not validate_action_regex(mock_message, mock_recent_messages, "")
|
|
128
|
+
|
|
129
|
+
# Unicode characters
|
|
130
|
+
msg = create_mock_memory("Transfer 100 €")
|
|
131
|
+
assert not validate_action_regex(msg, [], r"transfer \d+ €") # case sensitive
|
|
132
|
+
assert validate_action_regex(msg, [], r"(?i)transfer \d+ €")
|
|
133
|
+
|
|
134
|
+
# Special characters
|
|
135
|
+
msg = create_mock_memory("Hello (world) [ok]")
|
|
136
|
+
assert validate_action_regex(msg, [], r"\(world\)")
|
|
137
|
+
|
|
138
|
+
# Long inputs (basic DoS check)
|
|
139
|
+
long_text = "a" * 10000 + "transfer"
|
|
140
|
+
msg = create_mock_memory(long_text)
|
|
141
|
+
assert validate_action_regex(msg, [], r"transfer")
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import math
|
|
3
|
+
import unittest
|
|
4
|
+
import uuid
|
|
5
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
6
|
+
|
|
7
|
+
from elizaos.bootstrap.services.embedding import EmbeddingService
|
|
8
|
+
from elizaos.types import ModelType
|
|
9
|
+
from elizaos.types.events import EventType
|
|
10
|
+
from elizaos.types.memory import Memory
|
|
11
|
+
from elizaos.types.primitives import Content
|
|
12
|
+
|
|
13
|
+
# Mock vector for "hello world" - simple 384 dim vector
|
|
14
|
+
MOCK_VECTOR_HELLO = [0.1] * 384
|
|
15
|
+
# Mock vector for something else - orthogonal or different
|
|
16
|
+
MOCK_VECTOR_OTHER = [-0.1] * 384
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MockRuntime(MagicMock):
|
|
20
|
+
def __init__(self, *args, **kwargs):
|
|
21
|
+
super().__init__(*args, **kwargs)
|
|
22
|
+
self.agent_id = uuid.uuid4()
|
|
23
|
+
self.logger = MagicMock()
|
|
24
|
+
self.events = {}
|
|
25
|
+
self._adapter = AsyncMock()
|
|
26
|
+
self._models = {}
|
|
27
|
+
|
|
28
|
+
# Setup model mock
|
|
29
|
+
async def use_model_side_effect(model_type, **kwargs):
|
|
30
|
+
if model_type == ModelType.TEXT_EMBEDDING:
|
|
31
|
+
text = kwargs.get("text", "")
|
|
32
|
+
if "hello" in text.lower():
|
|
33
|
+
return MOCK_VECTOR_HELLO
|
|
34
|
+
return MOCK_VECTOR_OTHER
|
|
35
|
+
if model_type == ModelType.TEXT_SMALL:
|
|
36
|
+
return "intent"
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
self.use_model = AsyncMock(side_effect=use_model_side_effect)
|
|
40
|
+
|
|
41
|
+
def register_event(self, event, handler):
|
|
42
|
+
if event not in self.events:
|
|
43
|
+
self.events[event] = []
|
|
44
|
+
self.events[event].append(handler)
|
|
45
|
+
|
|
46
|
+
async def emit_event(self, event, payload):
|
|
47
|
+
handlers = self.events.get(event, [])
|
|
48
|
+
for handler in handlers:
|
|
49
|
+
if asyncio.iscoroutinefunction(handler):
|
|
50
|
+
await handler(payload)
|
|
51
|
+
else:
|
|
52
|
+
handler(payload)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def dot_product(v1: list[float], v2: list[float]) -> float:
|
|
56
|
+
return sum(x * y for x, y in zip(v1, v2, strict=False))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def magnitude(v: list[float]) -> float:
|
|
60
|
+
return math.sqrt(sum(x * x for x in v))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def cosine_similarity(v1: list[float], v2: list[float]) -> float:
|
|
64
|
+
m1 = magnitude(v1)
|
|
65
|
+
m2 = magnitude(v2)
|
|
66
|
+
if m1 == 0 or m2 == 0:
|
|
67
|
+
return 0.0
|
|
68
|
+
return dot_product(v1, v2) / (m1 * m2)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class VerifyMemoryArchitecture(unittest.IsolatedAsyncioTestCase):
|
|
72
|
+
async def test_end_to_end_memory_flow(self):
|
|
73
|
+
print("\n=== Starting Architectural Verification ===")
|
|
74
|
+
|
|
75
|
+
# 1. Setup
|
|
76
|
+
runtime = MockRuntime()
|
|
77
|
+
service = await EmbeddingService.start(runtime)
|
|
78
|
+
|
|
79
|
+
# --- TEST CASE 1: Short Message (Direct Embedding) ---
|
|
80
|
+
print("\n[Test 1] Short Message (< 20 chars) -> Direct Embedding")
|
|
81
|
+
|
|
82
|
+
short_id = str(uuid.uuid4())
|
|
83
|
+
short_memory = Memory(
|
|
84
|
+
id=short_id,
|
|
85
|
+
content=Content(text="Hello World"), # < 20 chars
|
|
86
|
+
room_id=str(uuid.uuid4()),
|
|
87
|
+
entity_id=str(uuid.uuid4()),
|
|
88
|
+
agent_id=str(runtime.agent_id),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Reset Update Memory Mock
|
|
92
|
+
runtime._adapter.update_memory.reset_mock()
|
|
93
|
+
|
|
94
|
+
# Trigger
|
|
95
|
+
from types import SimpleNamespace
|
|
96
|
+
|
|
97
|
+
payload = SimpleNamespace(extra={"memory": short_memory})
|
|
98
|
+
await self._run_pipeline(runtime, payload)
|
|
99
|
+
|
|
100
|
+
# Verify
|
|
101
|
+
runtime._adapter.update_memory.assert_called_once()
|
|
102
|
+
stored_short = runtime._adapter.update_memory.call_args[0][0]
|
|
103
|
+
|
|
104
|
+
# "Hello World" contains "hello" -> MOCK_VECTOR_HELLO
|
|
105
|
+
sim = cosine_similarity(list(stored_short.embedding), MOCK_VECTOR_HELLO)
|
|
106
|
+
self.assertAlmostEqual(
|
|
107
|
+
sim, 1.0, places=4, msg="Short message should use direct embedding (Hello)"
|
|
108
|
+
)
|
|
109
|
+
print(f" -> Verified: Short message embedding matches content (sim={sim:.4f})")
|
|
110
|
+
|
|
111
|
+
# --- TEST CASE 2: Long Message (Intent Embedding) ---
|
|
112
|
+
print("\n[Test 2] Long Message (> 20 chars) -> Intent Embedding")
|
|
113
|
+
|
|
114
|
+
long_id = str(uuid.uuid4())
|
|
115
|
+
# "Hello World" repeated to be long, but also contains "hello"
|
|
116
|
+
# However, the logic generates INTENT.
|
|
117
|
+
# Mock returns "intent" as intent text.
|
|
118
|
+
# "intent" does NOT contain "hello", so mock returns MOCK_VECTOR_OTHER.
|
|
119
|
+
long_memory = Memory(
|
|
120
|
+
id=long_id,
|
|
121
|
+
content=Content(text="Hello World " * 5),
|
|
122
|
+
room_id=str(uuid.uuid4()),
|
|
123
|
+
entity_id=str(uuid.uuid4()),
|
|
124
|
+
agent_id=str(runtime.agent_id),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
runtime._adapter.update_memory.reset_mock()
|
|
128
|
+
|
|
129
|
+
payload_long = SimpleNamespace(extra={"memory": long_memory})
|
|
130
|
+
await self._run_pipeline(runtime, payload_long)
|
|
131
|
+
|
|
132
|
+
runtime._adapter.update_memory.assert_called_once()
|
|
133
|
+
stored_long = runtime._adapter.update_memory.call_args[0][0]
|
|
134
|
+
|
|
135
|
+
# Expect MOCK_VECTOR_OTHER because embedding was on "intent"
|
|
136
|
+
sim_intent = cosine_similarity(list(stored_long.embedding), MOCK_VECTOR_OTHER)
|
|
137
|
+
self.assertAlmostEqual(
|
|
138
|
+
sim_intent, 1.0, places=4, msg="Long message should use intent embedding"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Verify metadata
|
|
142
|
+
self.assertEqual(stored_long.metadata.custom.custom_data["intent"], "intent")
|
|
143
|
+
print(f" -> Verified: Long message uses intent embedding (sim={sim_intent:.4f})")
|
|
144
|
+
print(" -> Verified: Intent metadata stored")
|
|
145
|
+
|
|
146
|
+
# 4. Verify Retrieval & Similarity
|
|
147
|
+
print("\n[3] Verifying Retrieval & Similarity logic...")
|
|
148
|
+
# "Hello there" -> MOCK_VECTOR_HELLO
|
|
149
|
+
# "General Kenobi" -> MOCK_VECTOR_OTHER
|
|
150
|
+
score = await service.similarity("Hello there", "General Kenobi")
|
|
151
|
+
|
|
152
|
+
# Expected: dot(HELLO, OTHER)
|
|
153
|
+
# 384 * (0.1 * -0.1) = 384 * -0.01 = -3.84
|
|
154
|
+
# Mag(HELLO) = sqrt(384 * 0.01) = sqrt(3.84) ~= 1.9596
|
|
155
|
+
# Mag(OTHER) = sqrt(384 * 0.01) = sqrt(3.84) ~= 1.9596
|
|
156
|
+
# Cos = -3.84 / (1.9596 * 1.9596) = -3.84 / 3.84 = -1.0
|
|
157
|
+
expected_sim = cosine_similarity(MOCK_VECTOR_HELLO, MOCK_VECTOR_OTHER)
|
|
158
|
+
self.assertAlmostEqual(score, expected_sim, places=4)
|
|
159
|
+
print(f" -> Similarity calculation verified: {score:.4f} (expected {expected_sim:.4f})")
|
|
160
|
+
|
|
161
|
+
await service.stop()
|
|
162
|
+
print("=== Verification Complete ===")
|
|
163
|
+
|
|
164
|
+
async def _run_pipeline(self, runtime, payload):
|
|
165
|
+
event_name = EventType.Name(EventType.EVENT_TYPE_EMBEDDING_GENERATION_REQUESTED)
|
|
166
|
+
completion_future = asyncio.Future()
|
|
167
|
+
|
|
168
|
+
async def on_complete(p):
|
|
169
|
+
if not completion_future.done():
|
|
170
|
+
completion_future.set_result(p)
|
|
171
|
+
|
|
172
|
+
# We need to register/unregister to avoid duplicate calls if running multiple times
|
|
173
|
+
# But MockRuntime implementation appends.
|
|
174
|
+
# For simplicity, just append and ensure we trigger the right future?
|
|
175
|
+
# A new future is needed for each run.
|
|
176
|
+
# Let's clear mocks events for clean slate or just handle it.
|
|
177
|
+
# We can just register a new one.
|
|
178
|
+
|
|
179
|
+
runtime.register_event(
|
|
180
|
+
EventType.Name(EventType.EVENT_TYPE_EMBEDDING_GENERATION_COMPLETED), on_complete
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
await runtime.emit_event(event_name, payload)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
await asyncio.wait_for(completion_future, timeout=2.0)
|
|
187
|
+
except TimeoutError:
|
|
188
|
+
self.fail("Async pipeline timed out")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
unittest.main()
|