@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,117 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from elizaos.generated.spec_helpers import require_provider_spec
|
|
7
|
+
from elizaos.types import Provider, ProviderResult
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from elizaos.types import IAgentRuntime, Memory, State
|
|
11
|
+
|
|
12
|
+
# Get text content from centralized specs
|
|
13
|
+
_spec = require_provider_spec("FOLLOW_UPS")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_follow_ups_context(
|
|
17
|
+
runtime: IAgentRuntime,
|
|
18
|
+
message: Memory,
|
|
19
|
+
state: State | None = None,
|
|
20
|
+
) -> ProviderResult:
|
|
21
|
+
from elizaos.bootstrap.services.follow_up import FollowUpService
|
|
22
|
+
|
|
23
|
+
follow_up_service = runtime.get_service("follow_up")
|
|
24
|
+
if not follow_up_service or not isinstance(follow_up_service, FollowUpService):
|
|
25
|
+
return ProviderResult(text="", values={}, data={})
|
|
26
|
+
|
|
27
|
+
upcoming = await follow_up_service.get_upcoming_follow_ups(days_ahead=7, include_overdue=True)
|
|
28
|
+
|
|
29
|
+
if not upcoming:
|
|
30
|
+
return ProviderResult(
|
|
31
|
+
text="No upcoming follow-ups scheduled.", values={"followUpCount": 0}, data={}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
now = datetime.now(UTC)
|
|
35
|
+
|
|
36
|
+
overdue_items: list[dict[str, str]] = []
|
|
37
|
+
upcoming_items: list[dict[str, str]] = []
|
|
38
|
+
|
|
39
|
+
for task in upcoming:
|
|
40
|
+
scheduled = datetime.fromisoformat(task.scheduled_at.replace("Z", "+00:00"))
|
|
41
|
+
entity = await runtime.get_entity(task.entity_id)
|
|
42
|
+
name = entity.name if entity and entity.name else "Unknown"
|
|
43
|
+
|
|
44
|
+
item = {
|
|
45
|
+
"entityId": str(task.entity_id),
|
|
46
|
+
"name": name,
|
|
47
|
+
"scheduledAt": task.scheduled_at,
|
|
48
|
+
"reason": task.reason,
|
|
49
|
+
"priority": task.priority,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if scheduled < now:
|
|
53
|
+
days_overdue = (now - scheduled).days
|
|
54
|
+
item["daysOverdue"] = str(days_overdue)
|
|
55
|
+
overdue_items.append(item)
|
|
56
|
+
else:
|
|
57
|
+
days_until = (scheduled - now).days
|
|
58
|
+
item["daysUntil"] = str(days_until)
|
|
59
|
+
upcoming_items.append(item)
|
|
60
|
+
|
|
61
|
+
text_summary = f"You have {len(upcoming)} follow-up(s) scheduled:\n"
|
|
62
|
+
|
|
63
|
+
if overdue_items:
|
|
64
|
+
text_summary += f"\nOverdue ({len(overdue_items)}):\n"
|
|
65
|
+
for item in overdue_items:
|
|
66
|
+
text_summary += f"- {item['name']} ({item['daysOverdue']} days overdue)"
|
|
67
|
+
if item.get("reason"):
|
|
68
|
+
text_summary += f" - {item['reason']}"
|
|
69
|
+
text_summary += "\n"
|
|
70
|
+
|
|
71
|
+
if upcoming_items:
|
|
72
|
+
text_summary += f"\nUpcoming ({len(upcoming_items)}):\n"
|
|
73
|
+
for item in upcoming_items:
|
|
74
|
+
days = int(item["daysUntil"])
|
|
75
|
+
if days == 0:
|
|
76
|
+
time_str = "today"
|
|
77
|
+
elif days == 1:
|
|
78
|
+
time_str = "tomorrow"
|
|
79
|
+
else:
|
|
80
|
+
time_str = f"in {days} days"
|
|
81
|
+
text_summary += f"- {item['name']} ({time_str})"
|
|
82
|
+
if item.get("reason"):
|
|
83
|
+
text_summary += f" - {item['reason']}"
|
|
84
|
+
text_summary += "\n"
|
|
85
|
+
|
|
86
|
+
suggestions = await follow_up_service.get_follow_up_suggestions()
|
|
87
|
+
if suggestions:
|
|
88
|
+
text_summary += "\nSuggested follow-ups:\n"
|
|
89
|
+
for s in suggestions[:3]:
|
|
90
|
+
text_summary += (
|
|
91
|
+
f"- {s.entity_name} ({s.days_since_last_contact} days since last contact)\n"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return ProviderResult(
|
|
95
|
+
text=text_summary.strip(),
|
|
96
|
+
values={
|
|
97
|
+
"followUpCount": len(upcoming),
|
|
98
|
+
"overdueCount": len(overdue_items),
|
|
99
|
+
"upcomingCount": len(upcoming_items),
|
|
100
|
+
"suggestionsCount": len(suggestions),
|
|
101
|
+
},
|
|
102
|
+
data={
|
|
103
|
+
"followUpCount": len(upcoming),
|
|
104
|
+
"overdueCount": len(overdue_items),
|
|
105
|
+
"upcomingCount": len(upcoming_items),
|
|
106
|
+
"suggestionsCount": len(suggestions),
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
follow_ups_provider = Provider(
|
|
112
|
+
name=_spec["name"],
|
|
113
|
+
description=_spec["description"],
|
|
114
|
+
get=get_follow_ups_context,
|
|
115
|
+
dynamic=_spec.get("dynamic", True),
|
|
116
|
+
position=_spec.get("position"),
|
|
117
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from elizaos.generated.spec_helpers import require_provider_spec
|
|
6
|
+
from elizaos.types import Provider, ProviderResult
|
|
7
|
+
from elizaos.types.database import MemorySearchOptions
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from elizaos.types import IAgentRuntime, Memory, State
|
|
11
|
+
|
|
12
|
+
# Get text content from centralized specs
|
|
13
|
+
_spec = require_provider_spec("KNOWLEDGE")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_knowledge_context(
|
|
17
|
+
runtime: IAgentRuntime,
|
|
18
|
+
message: Memory,
|
|
19
|
+
state: State | None = None,
|
|
20
|
+
) -> ProviderResult:
|
|
21
|
+
sections: list[str] = []
|
|
22
|
+
knowledge_entries: list[dict[str, str]] = []
|
|
23
|
+
|
|
24
|
+
query_text = ""
|
|
25
|
+
if message.content and message.content.text:
|
|
26
|
+
query_text = message.content.text
|
|
27
|
+
|
|
28
|
+
if not query_text:
|
|
29
|
+
return ProviderResult(
|
|
30
|
+
text="", values={"knowledgeCount": 0, "hasKnowledge": False}, data={"entries": []}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# 1. Fetch recent messages to get embeddings
|
|
34
|
+
recent_messages = await runtime.get_memories(
|
|
35
|
+
room_id=message.room_id, limit=5, table_name="messages"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# 2. Extract valid embeddings
|
|
39
|
+
embeddings = [m.embedding for m in recent_messages if m and m.embedding]
|
|
40
|
+
|
|
41
|
+
relevant_knowledge = []
|
|
42
|
+
# 3. Search using the most recent embedding if available
|
|
43
|
+
if embeddings:
|
|
44
|
+
primary_embedding = embeddings[0]
|
|
45
|
+
params = MemorySearchOptions(
|
|
46
|
+
table_name="knowledge",
|
|
47
|
+
room_id=message.room_id,
|
|
48
|
+
embedding=primary_embedding,
|
|
49
|
+
match_threshold=0.75,
|
|
50
|
+
match_count=5,
|
|
51
|
+
unique=True,
|
|
52
|
+
)
|
|
53
|
+
relevant_knowledge = await runtime.search_memories(params)
|
|
54
|
+
elif query_text:
|
|
55
|
+
# Fallback to search_knowledge if no embeddings found?
|
|
56
|
+
# TS implementation might rely on search_memories logic handling missing embedding?
|
|
57
|
+
# No, TS skips if no embedding.
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
for entry in relevant_knowledge:
|
|
61
|
+
if entry.content and entry.content.text:
|
|
62
|
+
knowledge_text = entry.content.text
|
|
63
|
+
if len(knowledge_text) > 500:
|
|
64
|
+
knowledge_text = knowledge_text[:500] + "..."
|
|
65
|
+
|
|
66
|
+
entry_dict = {
|
|
67
|
+
"id": str(entry.id) if entry.id else "",
|
|
68
|
+
"text": knowledge_text,
|
|
69
|
+
"source": str(entry.metadata.get("source", "unknown"))
|
|
70
|
+
if entry.metadata
|
|
71
|
+
else "unknown",
|
|
72
|
+
}
|
|
73
|
+
knowledge_entries.append(entry_dict)
|
|
74
|
+
sections.append(f"- {knowledge_text}")
|
|
75
|
+
|
|
76
|
+
context_text = "# Relevant Knowledge\n" + "\n".join(sections) if sections else ""
|
|
77
|
+
|
|
78
|
+
return ProviderResult(
|
|
79
|
+
text=context_text,
|
|
80
|
+
values={
|
|
81
|
+
"knowledgeCount": len(knowledge_entries),
|
|
82
|
+
"hasKnowledge": len(knowledge_entries) > 0,
|
|
83
|
+
},
|
|
84
|
+
data={
|
|
85
|
+
"entries": knowledge_entries,
|
|
86
|
+
"query": query_text,
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
knowledge_provider = Provider(
|
|
92
|
+
name=_spec["name"],
|
|
93
|
+
description=_spec["description"],
|
|
94
|
+
get=get_knowledge_context,
|
|
95
|
+
dynamic=_spec.get("dynamic", True),
|
|
96
|
+
position=_spec.get("position"),
|
|
97
|
+
)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from elizaos.generated.spec_helpers import require_provider_spec
|
|
6
|
+
from elizaos.types import Provider, ProviderResult
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from elizaos.types import IAgentRuntime, Memory, State
|
|
10
|
+
|
|
11
|
+
# Get text content from centralized specs
|
|
12
|
+
_spec = require_provider_spec("RELATIONSHIPS")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def format_relationship(
|
|
16
|
+
relationship: dict[str, str | int | list[str] | dict[str, str]],
|
|
17
|
+
target_name: str,
|
|
18
|
+
) -> str:
|
|
19
|
+
tags = relationship.get("tags", [])
|
|
20
|
+
tags_str = (", ".join(tags) if tags else "none") if isinstance(tags, list) else str(tags)
|
|
21
|
+
|
|
22
|
+
interactions = relationship.get("metadata", {})
|
|
23
|
+
interaction_count = interactions.get("interactions", 0) if isinstance(interactions, dict) else 0
|
|
24
|
+
|
|
25
|
+
return f"- {target_name}: tags=[{tags_str}], interactions={interaction_count}"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def get_relationships(
|
|
29
|
+
runtime: IAgentRuntime,
|
|
30
|
+
message: Memory,
|
|
31
|
+
state: State | None = None,
|
|
32
|
+
) -> ProviderResult:
|
|
33
|
+
entity_id = message.entity_id
|
|
34
|
+
if not entity_id:
|
|
35
|
+
return ProviderResult(
|
|
36
|
+
text="No relationships found.",
|
|
37
|
+
values={"relationshipCount": 0},
|
|
38
|
+
data={"relationships": []},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
relationships = await runtime.get_relationships(entity_id=entity_id)
|
|
43
|
+
except Exception as e:
|
|
44
|
+
runtime.logger.debug(
|
|
45
|
+
{"src": "provider:relationships", "agentId": runtime.agent_id, "error": str(e)},
|
|
46
|
+
"Failed to get relationships",
|
|
47
|
+
)
|
|
48
|
+
relationships = []
|
|
49
|
+
|
|
50
|
+
if not relationships:
|
|
51
|
+
return ProviderResult(
|
|
52
|
+
text="No relationships found.",
|
|
53
|
+
values={"relationshipCount": 0},
|
|
54
|
+
data={"relationships": []},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
sorted_relationships = sorted(
|
|
58
|
+
relationships,
|
|
59
|
+
key=lambda r: (r.get("metadata", {}) or {}).get("interactions", 0),
|
|
60
|
+
reverse=True,
|
|
61
|
+
)[:30]
|
|
62
|
+
|
|
63
|
+
formatted_relationships: list[str] = []
|
|
64
|
+
entity_cache: dict[object, str] = {}
|
|
65
|
+
for rel in sorted_relationships:
|
|
66
|
+
target_id = rel.get("targetEntityId")
|
|
67
|
+
if not target_id:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
target_name = entity_cache.get(target_id)
|
|
71
|
+
if target_name is None:
|
|
72
|
+
target_entity = await runtime.get_entity(target_id)
|
|
73
|
+
target_name = target_entity.name if target_entity else str(target_id)[:8]
|
|
74
|
+
entity_cache[target_id] = target_name
|
|
75
|
+
|
|
76
|
+
formatted_relationships.append(format_relationship(rel, target_name))
|
|
77
|
+
|
|
78
|
+
if not formatted_relationships:
|
|
79
|
+
return ProviderResult(
|
|
80
|
+
text="No relationships found.",
|
|
81
|
+
values={"relationshipCount": 0},
|
|
82
|
+
data={"relationships": []},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
sender_name = message.content.sender_name if message.content else "Unknown"
|
|
86
|
+
text = f"# {runtime.character.name} has observed {sender_name} interacting with:\n" + "\n".join(
|
|
87
|
+
formatted_relationships
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return ProviderResult(
|
|
91
|
+
text=text,
|
|
92
|
+
values={
|
|
93
|
+
"relationshipCount": len(sorted_relationships),
|
|
94
|
+
},
|
|
95
|
+
data={
|
|
96
|
+
"relationships": sorted_relationships,
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
relationships_provider = Provider(
|
|
102
|
+
name=_spec["name"],
|
|
103
|
+
description=_spec["description"],
|
|
104
|
+
get=get_relationships,
|
|
105
|
+
dynamic=_spec.get("dynamic", True),
|
|
106
|
+
position=_spec.get("position"),
|
|
107
|
+
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from elizaos.generated.spec_helpers import require_provider_spec
|
|
6
|
+
from elizaos.types import Provider, ProviderResult
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from elizaos.types import IAgentRuntime, Memory, State
|
|
10
|
+
|
|
11
|
+
# Get text content from centralized specs
|
|
12
|
+
_spec = require_provider_spec("ROLES")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def format_role_info(entity_name: str, role: str) -> str:
|
|
16
|
+
return f"- {entity_name}: {role}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def get_roles(
|
|
20
|
+
runtime: IAgentRuntime,
|
|
21
|
+
message: Memory,
|
|
22
|
+
state: State | None = None,
|
|
23
|
+
) -> ProviderResult:
|
|
24
|
+
role_info: list[dict[str, str]] = []
|
|
25
|
+
|
|
26
|
+
world_id = None
|
|
27
|
+
if state and hasattr(state, "world"):
|
|
28
|
+
world = state.world
|
|
29
|
+
if world and hasattr(world, "id"):
|
|
30
|
+
world_id = world.id
|
|
31
|
+
|
|
32
|
+
if not world_id and message.room_id:
|
|
33
|
+
room = await runtime.get_room(message.room_id)
|
|
34
|
+
if room and hasattr(room, "world_id"):
|
|
35
|
+
world_id = room.world_id
|
|
36
|
+
|
|
37
|
+
if world_id:
|
|
38
|
+
world = await runtime.get_world(world_id)
|
|
39
|
+
if world and hasattr(world, "metadata"):
|
|
40
|
+
roles = world.metadata.get("roles", {})
|
|
41
|
+
if isinstance(roles, dict):
|
|
42
|
+
for entity_id, role in roles.items():
|
|
43
|
+
entity = await runtime.get_entity(entity_id)
|
|
44
|
+
entity_name = entity.name if entity else str(entity_id)[:8]
|
|
45
|
+
|
|
46
|
+
role_info.append(
|
|
47
|
+
{
|
|
48
|
+
"entityId": str(entity_id),
|
|
49
|
+
"entityName": entity_name,
|
|
50
|
+
"role": str(role),
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if message.entity_id:
|
|
55
|
+
entity = await runtime.get_entity(message.entity_id)
|
|
56
|
+
if entity and hasattr(entity, "metadata"):
|
|
57
|
+
sender_role = entity.metadata.get("role")
|
|
58
|
+
if sender_role:
|
|
59
|
+
existing = next(
|
|
60
|
+
(r for r in role_info if r["entityId"] == str(message.entity_id)), None
|
|
61
|
+
)
|
|
62
|
+
if not existing:
|
|
63
|
+
role_info.append(
|
|
64
|
+
{
|
|
65
|
+
"entityId": str(message.entity_id),
|
|
66
|
+
"entityName": entity.name or "Unknown",
|
|
67
|
+
"role": str(sender_role),
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if not role_info:
|
|
72
|
+
return ProviderResult(text="", values={"roleCount": 0}, data={"roles": []})
|
|
73
|
+
|
|
74
|
+
formatted_roles = "\n".join(format_role_info(r["entityName"], r["role"]) for r in role_info)
|
|
75
|
+
|
|
76
|
+
text = f"# Entity Roles\n{formatted_roles}"
|
|
77
|
+
|
|
78
|
+
return ProviderResult(
|
|
79
|
+
text=text,
|
|
80
|
+
values={
|
|
81
|
+
"roleCount": len(role_info),
|
|
82
|
+
"roles": {r["entityName"]: r["role"] for r in role_info},
|
|
83
|
+
},
|
|
84
|
+
data={
|
|
85
|
+
"roles": role_info,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
roles_provider = Provider(
|
|
91
|
+
name=_spec["name"],
|
|
92
|
+
description=_spec["description"],
|
|
93
|
+
get=get_roles,
|
|
94
|
+
dynamic=_spec.get("dynamic", True),
|
|
95
|
+
position=_spec.get("position"),
|
|
96
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from elizaos.bootstrap.providers.agent_settings import SENSITIVE_KEY_PATTERNS
|
|
6
|
+
from elizaos.generated.spec_helpers import require_provider_spec
|
|
7
|
+
from elizaos.types import Provider, ProviderResult
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from elizaos.types import IAgentRuntime, Memory, State
|
|
11
|
+
|
|
12
|
+
# Get text content from centralized specs
|
|
13
|
+
_spec = require_provider_spec("SETTINGS")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_settings_context(
|
|
17
|
+
runtime: IAgentRuntime,
|
|
18
|
+
message: Memory,
|
|
19
|
+
state: State | None = None,
|
|
20
|
+
) -> ProviderResult:
|
|
21
|
+
_ = message, state
|
|
22
|
+
|
|
23
|
+
all_settings = runtime.get_all_settings()
|
|
24
|
+
|
|
25
|
+
safe_settings: dict[str, str] = {}
|
|
26
|
+
for key, value in all_settings.items():
|
|
27
|
+
if not any(pattern in key.lower() for pattern in SENSITIVE_KEY_PATTERNS):
|
|
28
|
+
safe_settings[key] = str(value)
|
|
29
|
+
|
|
30
|
+
lines: list[str] = []
|
|
31
|
+
if safe_settings:
|
|
32
|
+
lines.append("## Current Configuration")
|
|
33
|
+
for key, value in safe_settings.items():
|
|
34
|
+
display_value = value if len(value) <= 50 else value[:50] + "..."
|
|
35
|
+
lines.append(f"- {key}: {display_value}")
|
|
36
|
+
|
|
37
|
+
context_text = "\n".join(lines) if lines else ""
|
|
38
|
+
|
|
39
|
+
return ProviderResult(
|
|
40
|
+
text=context_text,
|
|
41
|
+
values={
|
|
42
|
+
"settings": context_text,
|
|
43
|
+
"settingsCount": len(safe_settings),
|
|
44
|
+
"hasSettings": len(safe_settings) > 0,
|
|
45
|
+
},
|
|
46
|
+
data={"settings": safe_settings},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
settings_provider = Provider(
|
|
51
|
+
name=_spec["name"],
|
|
52
|
+
description=_spec["description"],
|
|
53
|
+
get=get_settings_context,
|
|
54
|
+
dynamic=_spec.get("dynamic", True),
|
|
55
|
+
position=_spec.get("position"),
|
|
56
|
+
)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from datetime import UTC, datetime
|
|
4
3
|
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
5
|
+
from elizaos.deterministic import get_prompt_reference_datetime
|
|
6
6
|
from elizaos.generated.spec_helpers import require_provider_spec
|
|
7
7
|
from elizaos.types import Provider, ProviderResult
|
|
8
8
|
|
|
@@ -18,9 +18,12 @@ async def get_time_context(
|
|
|
18
18
|
message: Memory,
|
|
19
19
|
state: State | None = None,
|
|
20
20
|
) -> ProviderResult:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
now = get_prompt_reference_datetime(
|
|
22
|
+
runtime,
|
|
23
|
+
message,
|
|
24
|
+
state,
|
|
25
|
+
"provider:time",
|
|
26
|
+
)
|
|
24
27
|
iso_string = now.isoformat()
|
|
25
28
|
timestamp_ms = int(now.timestamp() * 1000)
|
|
26
29
|
human_readable = now.strftime("%A, %B %d, %Y at %H:%M:%S UTC")
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"""elizaOS Bootstrap Plugin - Python implementation."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .plugin import bootstrap_plugin, create_bootstrap_plugin
|
|
9
|
+
from .types import CapabilityConfig
|
|
5
10
|
|
|
6
11
|
__version__ = "2.0.0-alpha.0"
|
|
7
12
|
__all__ = [
|
|
@@ -10,3 +15,17 @@ __all__ = [
|
|
|
10
15
|
"CapabilityConfig",
|
|
11
16
|
"__version__",
|
|
12
17
|
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def __getattr__(name: str) -> object:
|
|
21
|
+
if name in {"bootstrap_plugin", "create_bootstrap_plugin"}:
|
|
22
|
+
from .plugin import bootstrap_plugin, create_bootstrap_plugin
|
|
23
|
+
|
|
24
|
+
if name == "bootstrap_plugin":
|
|
25
|
+
return bootstrap_plugin
|
|
26
|
+
return create_bootstrap_plugin
|
|
27
|
+
if name == "CapabilityConfig":
|
|
28
|
+
from .types import CapabilityConfig
|
|
29
|
+
|
|
30
|
+
return CapabilityConfig
|
|
31
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -3,8 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
|
+
from uuid import UUID as StdUUID
|
|
6
7
|
|
|
7
8
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
9
|
+
from elizaos.deterministic import get_prompt_reference_datetime
|
|
8
10
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
9
11
|
from elizaos.prompts import SCHEDULE_FOLLOW_UP_TEMPLATE
|
|
10
12
|
from elizaos.types import (
|
|
@@ -48,6 +50,25 @@ def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
|
48
50
|
return []
|
|
49
51
|
|
|
50
52
|
|
|
53
|
+
def _normalize_priority(raw_priority: str) -> str:
|
|
54
|
+
normalized = raw_priority.strip().lower()
|
|
55
|
+
if normalized in {"high", "medium", "low"}:
|
|
56
|
+
return normalized
|
|
57
|
+
return "medium"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _coerce_uuid(value: object | None) -> StdUUID | None:
|
|
61
|
+
if value is None:
|
|
62
|
+
return None
|
|
63
|
+
text = str(value).strip()
|
|
64
|
+
if not text:
|
|
65
|
+
return None
|
|
66
|
+
try:
|
|
67
|
+
return StdUUID(text)
|
|
68
|
+
except ValueError:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
51
72
|
@dataclass
|
|
52
73
|
class ScheduleFollowUpAction:
|
|
53
74
|
name: str = _spec["name"]
|
|
@@ -93,7 +114,12 @@ class ScheduleFollowUpAction:
|
|
|
93
114
|
)
|
|
94
115
|
|
|
95
116
|
state = await runtime.compose_state(message, ["RECENT_MESSAGES", "ENTITIES"])
|
|
96
|
-
state.values["currentDateTime"] =
|
|
117
|
+
state.values["currentDateTime"] = get_prompt_reference_datetime(
|
|
118
|
+
runtime,
|
|
119
|
+
message,
|
|
120
|
+
state,
|
|
121
|
+
"action:schedule_follow_up",
|
|
122
|
+
).isoformat()
|
|
97
123
|
|
|
98
124
|
prompt = runtime.compose_prompt_from_state(
|
|
99
125
|
state=state,
|
|
@@ -114,14 +140,46 @@ class ScheduleFollowUpAction:
|
|
|
114
140
|
contact_name = str(parsed.get("contactName", ""))
|
|
115
141
|
scheduled_at_str = str(parsed.get("scheduledAt", ""))
|
|
116
142
|
reason = str(parsed.get("reason", "Follow-up"))
|
|
117
|
-
priority = str(parsed.get("priority", "medium"))
|
|
143
|
+
priority = _normalize_priority(str(parsed.get("priority", "medium")))
|
|
118
144
|
follow_up_message = str(parsed.get("message", ""))
|
|
145
|
+
parsed_entity_id = _coerce_uuid(parsed.get("entityId"))
|
|
146
|
+
message_entity_id = _coerce_uuid(message.entity_id)
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
scheduled_at = datetime.fromisoformat(scheduled_at_str.replace("Z", "+00:00"))
|
|
150
|
+
except ValueError:
|
|
151
|
+
return ActionResult(
|
|
152
|
+
text="Could not parse the follow-up date/time",
|
|
153
|
+
success=False,
|
|
154
|
+
values={"error": True},
|
|
155
|
+
data={"error": "Invalid follow-up datetime"},
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
entity_id_uuid = parsed_entity_id or message_entity_id
|
|
159
|
+
if entity_id_uuid is None and contact_name:
|
|
160
|
+
contacts = await rolodex_service.search_contacts(search_term=contact_name)
|
|
161
|
+
if contacts:
|
|
162
|
+
entity_id_uuid = contacts[0].entity_id
|
|
119
163
|
|
|
120
|
-
|
|
164
|
+
if entity_id_uuid is None:
|
|
165
|
+
return ActionResult(
|
|
166
|
+
text=f"Could not determine which contact to schedule for ({contact_name}).",
|
|
167
|
+
success=False,
|
|
168
|
+
values={"error": True},
|
|
169
|
+
data={"error": "Missing contact entity id"},
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
contact = await rolodex_service.get_contact(entity_id_uuid)
|
|
173
|
+
if contact is None:
|
|
174
|
+
return ActionResult(
|
|
175
|
+
text=f"Contact '{contact_name}' was not found in the rolodex.",
|
|
176
|
+
success=False,
|
|
177
|
+
values={"error": True},
|
|
178
|
+
data={"error": "Contact not found"},
|
|
179
|
+
)
|
|
121
180
|
|
|
122
|
-
entity_id = message.entity_id
|
|
123
181
|
await follow_up_service.schedule_follow_up(
|
|
124
|
-
entity_id=
|
|
182
|
+
entity_id=entity_id_uuid,
|
|
125
183
|
scheduled_at=scheduled_at,
|
|
126
184
|
reason=reason,
|
|
127
185
|
priority=priority,
|
|
@@ -137,11 +195,11 @@ class ScheduleFollowUpAction:
|
|
|
137
195
|
text=response_text,
|
|
138
196
|
success=True,
|
|
139
197
|
values={
|
|
140
|
-
"contactId": str(
|
|
198
|
+
"contactId": str(entity_id_uuid),
|
|
141
199
|
"scheduledAt": scheduled_at.isoformat(),
|
|
142
200
|
},
|
|
143
201
|
data={
|
|
144
|
-
"contactId": str(
|
|
202
|
+
"contactId": str(entity_id_uuid),
|
|
145
203
|
"contactName": contact_name,
|
|
146
204
|
"scheduledAt": scheduled_at.isoformat(),
|
|
147
205
|
"reason": reason,
|