@elizaos/python 2.0.0-alpha.27 → 2.0.0-alpha.30
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/advanced_capabilities/actions/add_contact.py +4 -3
- package/elizaos/advanced_capabilities/actions/follow_room.py +2 -2
- package/elizaos/advanced_capabilities/actions/image_generation.py +2 -2
- package/elizaos/advanced_capabilities/actions/mute_room.py +2 -2
- package/elizaos/advanced_capabilities/actions/remove_contact.py +2 -2
- package/elizaos/advanced_capabilities/actions/roles.py +5 -4
- package/elizaos/advanced_capabilities/actions/search_contacts.py +3 -3
- package/elizaos/advanced_capabilities/actions/send_message.py +2 -2
- package/elizaos/advanced_capabilities/actions/settings.py +2 -2
- package/elizaos/advanced_capabilities/actions/unfollow_room.py +2 -2
- package/elizaos/advanced_capabilities/actions/unmute_room.py +2 -2
- package/elizaos/advanced_capabilities/actions/update_contact.py +2 -2
- package/elizaos/advanced_capabilities/actions/update_entity.py +2 -2
- package/elizaos/advanced_capabilities/providers/knowledge.py +8 -9
- package/elizaos/advanced_capabilities/services/rolodex.py +2 -2
- package/elizaos/advanced_memory/actions/reset_session.py +143 -11
- package/elizaos/advanced_memory/memory_service.py +54 -10
- package/elizaos/advanced_memory/plugin.py +2 -1
- package/elizaos/advanced_memory/types.py +2 -2
- package/elizaos/advanced_planning/actions/schedule_follow_up.py +2 -2
- package/elizaos/basic_capabilities/providers/contacts.py +1 -1
- package/elizaos/basic_capabilities/providers/follow_ups.py +1 -1
- package/elizaos/basic_capabilities/providers/knowledge.py +8 -9
- package/elizaos/basic_capabilities/providers/recent_messages.py +5 -0
- package/elizaos/basic_capabilities/providers/relationships.py +19 -13
- package/elizaos/basic_capabilities/services/embedding.py +10 -7
- package/elizaos/basic_capabilities/services/task.py +3 -3
- 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/providers/knowledge.py +8 -9
- package/elizaos/bootstrap/providers/recent_messages.py +5 -0
- package/elizaos/bootstrap/providers/relationships.py +19 -13
- package/elizaos/bootstrap/services/embedding.py +53 -10
- package/elizaos/bootstrap/services/rolodex.py +2 -2
- package/elizaos/bootstrap/services/task.py +3 -3
- package/elizaos/media/mime.py +2 -2
- package/elizaos/runtime.py +10 -9
- package/elizaos/services/hook_service.py +3 -3
- package/elizaos/types/generated/eliza/v1/agent_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/components_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/database_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/environment_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/events_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/ipc_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/knowledge_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/memory_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/message_service_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/messaging_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/model_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/payment_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/plugin_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/primitives_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/prompts_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/service_interfaces_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/service_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/settings_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/state_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/task_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/tee_pb2.py +4 -4
- package/elizaos/types/generated/eliza/v1/testing_pb2.py +4 -4
- package/elizaos/types/model.py +3 -3
- package/elizaos/types/primitives.py +3 -3
- package/elizaos/types/runtime.py +12 -2
- package/elizaos/types/state.py +2 -2
- package/elizaos/utils/streaming.py +3 -3
- package/package.json +3 -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_history_compaction.py +104 -0
- package/tests/test_memory_bounds.py +115 -0
- package/tests/test_validation.py +1 -1
- package/uv.lock +10 -10
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
|
+
from uuid import UUID as StdUUID
|
|
5
6
|
|
|
6
7
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
7
8
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
@@ -29,7 +30,7 @@ _spec = require_action_spec("ADD_CONTACT")
|
|
|
29
30
|
|
|
30
31
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
31
32
|
"""Convert spec examples to ActionExample format."""
|
|
32
|
-
spec_examples = _spec.get("examples", [])
|
|
33
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
33
34
|
if spec_examples:
|
|
34
35
|
return [
|
|
35
36
|
[
|
|
@@ -103,7 +104,7 @@ class AddContactAction:
|
|
|
103
104
|
notes = str(parsed.get("notes", ""))
|
|
104
105
|
reason = str(parsed.get("reason", ""))
|
|
105
106
|
|
|
106
|
-
entity_id = message.entity_id
|
|
107
|
+
entity_id = StdUUID(str(message.entity_id))
|
|
107
108
|
preferences = ContactPreferences(notes=notes) if notes else None
|
|
108
109
|
|
|
109
110
|
await rolodex_service.add_contact(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
7
7
|
from elizaos.types import Action, ActionExample, ActionResult, Content
|
|
@@ -15,7 +15,7 @@ _spec = require_action_spec("FOLLOW_ROOM")
|
|
|
15
15
|
|
|
16
16
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
17
17
|
"""Convert spec examples to ActionExample format."""
|
|
18
|
-
spec_examples = _spec.get("examples", [])
|
|
18
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
19
19
|
if spec_examples:
|
|
20
20
|
return [
|
|
21
21
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
7
7
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
@@ -17,7 +17,7 @@ _spec = require_action_spec("GENERATE_IMAGE")
|
|
|
17
17
|
|
|
18
18
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
19
19
|
"""Convert spec examples to ActionExample format."""
|
|
20
|
-
spec_examples = _spec.get("examples", [])
|
|
20
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
21
21
|
if spec_examples:
|
|
22
22
|
return [
|
|
23
23
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
7
7
|
from elizaos.types import Action, ActionExample, ActionResult, Content
|
|
@@ -15,7 +15,7 @@ _spec = require_action_spec("MUTE_ROOM")
|
|
|
15
15
|
|
|
16
16
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
17
17
|
"""Convert spec examples to ActionExample format."""
|
|
18
|
-
spec_examples = _spec.get("examples", [])
|
|
18
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
19
19
|
if spec_examples:
|
|
20
20
|
return [
|
|
21
21
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
7
7
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
@@ -29,7 +29,7 @@ _spec = require_action_spec("REMOVE_CONTACT")
|
|
|
29
29
|
|
|
30
30
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
31
31
|
"""Convert spec examples to ActionExample format."""
|
|
32
|
-
spec_examples = _spec.get("examples", [])
|
|
32
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
33
33
|
if spec_examples:
|
|
34
34
|
return [
|
|
35
35
|
[
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from enum import
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
8
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
9
9
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
10
|
+
from elizaos.prompts import UPDATE_ROLE_TEMPLATE
|
|
10
11
|
from elizaos.types import Action, ActionExample, ActionResult, Content, ModelType
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
@@ -18,7 +19,7 @@ _spec = require_action_spec("UPDATE_ROLE")
|
|
|
18
19
|
|
|
19
20
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
20
21
|
"""Convert spec examples to ActionExample format."""
|
|
21
|
-
spec_examples = _spec.get("examples", [])
|
|
22
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
22
23
|
if spec_examples:
|
|
23
24
|
return [
|
|
24
25
|
[
|
|
@@ -36,7 +37,7 @@ def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
|
36
37
|
return []
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
class Role(
|
|
40
|
+
class Role(StrEnum):
|
|
40
41
|
OWNER = "OWNER"
|
|
41
42
|
ADMIN = "ADMIN"
|
|
42
43
|
MEMBER = "MEMBER"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
7
7
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
@@ -29,7 +29,7 @@ _spec = require_action_spec("SEARCH_CONTACTS")
|
|
|
29
29
|
|
|
30
30
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
31
31
|
"""Convert spec examples to ActionExample format."""
|
|
32
|
-
spec_examples = _spec.get("examples", [])
|
|
32
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
33
33
|
if spec_examples:
|
|
34
34
|
return [
|
|
35
35
|
[
|
|
@@ -109,7 +109,7 @@ class SearchContactsAction:
|
|
|
109
109
|
|
|
110
110
|
contact_details: list[dict[str, str]] = []
|
|
111
111
|
for contact in contacts:
|
|
112
|
-
entity = await runtime.get_entity(contact.entity_id)
|
|
112
|
+
entity = await runtime.get_entity(str(contact.entity_id))
|
|
113
113
|
name = entity.name if entity and entity.name else "Unknown"
|
|
114
114
|
contact_details.append(
|
|
115
115
|
{
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
6
6
|
|
|
7
7
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
8
8
|
from elizaos.types import Action, ActionExample, ActionResult, Content
|
|
@@ -18,7 +18,7 @@ _spec = require_action_spec("SEND_MESSAGE")
|
|
|
18
18
|
|
|
19
19
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
20
20
|
"""Convert spec examples to ActionExample format."""
|
|
21
|
-
spec_examples = _spec.get("examples", [])
|
|
21
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
22
22
|
if spec_examples:
|
|
23
23
|
return [
|
|
24
24
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
7
7
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
@@ -17,7 +17,7 @@ _spec = require_action_spec("UPDATE_SETTINGS")
|
|
|
17
17
|
|
|
18
18
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
19
19
|
"""Convert spec examples to ActionExample format."""
|
|
20
|
-
spec_examples = _spec.get("examples", [])
|
|
20
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
21
21
|
if spec_examples:
|
|
22
22
|
return [
|
|
23
23
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
7
7
|
from elizaos.types import Action, ActionExample, ActionResult, Content
|
|
@@ -15,7 +15,7 @@ _spec = require_action_spec("UNFOLLOW_ROOM")
|
|
|
15
15
|
|
|
16
16
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
17
17
|
"""Convert spec examples to ActionExample format."""
|
|
18
|
-
spec_examples = _spec.get("examples", [])
|
|
18
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
19
19
|
if spec_examples:
|
|
20
20
|
return [
|
|
21
21
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
7
7
|
from elizaos.types import Action, ActionExample, ActionResult, Content
|
|
@@ -15,7 +15,7 @@ _spec = require_action_spec("UNMUTE_ROOM")
|
|
|
15
15
|
|
|
16
16
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
17
17
|
"""Convert spec examples to ActionExample format."""
|
|
18
|
-
spec_examples = _spec.get("examples", [])
|
|
18
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
19
19
|
if spec_examples:
|
|
20
20
|
return [
|
|
21
21
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
6
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
7
7
|
from elizaos.generated.spec_helpers import require_action_spec
|
|
@@ -29,7 +29,7 @@ _spec = require_action_spec("UPDATE_CONTACT")
|
|
|
29
29
|
|
|
30
30
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
31
31
|
"""Convert spec examples to ActionExample format."""
|
|
32
|
-
spec_examples = _spec.get("examples", [])
|
|
32
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
33
33
|
if spec_examples:
|
|
34
34
|
return [
|
|
35
35
|
[
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
@@ -18,7 +18,7 @@ _spec = require_action_spec("UPDATE_ENTITY")
|
|
|
18
18
|
|
|
19
19
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
20
20
|
"""Convert spec examples to ActionExample format."""
|
|
21
|
-
spec_examples = _spec.get("examples", [])
|
|
21
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
22
22
|
if spec_examples:
|
|
23
23
|
return [
|
|
24
24
|
[
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from elizaos.types import Provider, ProviderResult
|
|
6
|
-
from elizaos.types.database import MemorySearchOptions
|
|
7
6
|
|
|
8
7
|
if TYPE_CHECKING:
|
|
9
8
|
from elizaos.types import IAgentRuntime, Memory, State
|
|
@@ -38,14 +37,14 @@ async def get_knowledge_context(
|
|
|
38
37
|
# 3. Search using the most recent embedding if available
|
|
39
38
|
if embeddings:
|
|
40
39
|
primary_embedding = embeddings[0]
|
|
41
|
-
params =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
embedding
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
unique
|
|
48
|
-
|
|
40
|
+
params = {
|
|
41
|
+
"tableName": "knowledge",
|
|
42
|
+
"roomId": str(message.room_id),
|
|
43
|
+
"embedding": primary_embedding,
|
|
44
|
+
"matchThreshold": 0.75,
|
|
45
|
+
"matchCount": 5,
|
|
46
|
+
"unique": True,
|
|
47
|
+
}
|
|
49
48
|
relevant_knowledge = await runtime.search_memories(params)
|
|
50
49
|
elif query_text:
|
|
51
50
|
# Fallback skipped for parity with TS/Bootstrap
|
|
@@ -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"
|
|
@@ -1,11 +1,143 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
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
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ResetSessionAction:
|
|
15
|
+
name: str = "RESET_SESSION"
|
|
16
|
+
similes: list[str] = field(
|
|
17
|
+
default_factory=lambda: ["CLEAR_HISTORY", "NEW_SESSION", "FORGET", "START_OVER", "RESET"]
|
|
18
|
+
)
|
|
19
|
+
description: str = (
|
|
20
|
+
"Resets the conversation session by creating a compaction point. "
|
|
21
|
+
"Messages before this point will not be included in future context."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
async def validate(
|
|
25
|
+
self, runtime: IAgentRuntime, message: Memory, state: State | None = None
|
|
26
|
+
) -> bool:
|
|
27
|
+
room = None
|
|
28
|
+
if state and getattr(state, "data", None) and getattr(state.data, "room", None):
|
|
29
|
+
room = state.data.room
|
|
30
|
+
elif message.room_id:
|
|
31
|
+
room = await runtime.get_room(message.room_id)
|
|
32
|
+
|
|
33
|
+
if not room or not getattr(room, "world_id", None):
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
world = await runtime.get_world(room.world_id)
|
|
37
|
+
if not world or not getattr(world, "metadata", None):
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
roles = world.metadata.get("roles", {})
|
|
41
|
+
user_role = roles.get(str(message.entity_id), "NONE")
|
|
42
|
+
return user_role in ("OWNER", "ADMIN")
|
|
43
|
+
|
|
44
|
+
async def handler(
|
|
45
|
+
self,
|
|
46
|
+
runtime: IAgentRuntime,
|
|
47
|
+
message: Memory,
|
|
48
|
+
state: State | None = None,
|
|
49
|
+
options: HandlerOptions | None = None,
|
|
50
|
+
callback: HandlerCallback | None = None,
|
|
51
|
+
responses: list[Memory] | None = None,
|
|
52
|
+
) -> ActionResult:
|
|
53
|
+
del options, responses
|
|
54
|
+
|
|
55
|
+
room = None
|
|
56
|
+
if state and getattr(state, "data", None) and getattr(state.data, "room", None):
|
|
57
|
+
room = state.data.room
|
|
58
|
+
elif message.room_id:
|
|
59
|
+
room = await runtime.get_room(message.room_id)
|
|
60
|
+
|
|
61
|
+
if room is None:
|
|
62
|
+
if callback:
|
|
63
|
+
await callback(
|
|
64
|
+
Content(
|
|
65
|
+
text="Unable to reset session - room not found.",
|
|
66
|
+
actions=["RESET_SESSION_FAILED"],
|
|
67
|
+
source=getattr(message.content, "source", None),
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
return ActionResult(
|
|
71
|
+
text="Room not found",
|
|
72
|
+
values={"error": "room_not_found"},
|
|
73
|
+
data={"actionName": "RESET_SESSION"},
|
|
74
|
+
success=False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
metadata = dict(getattr(room, "metadata", {}) or {})
|
|
78
|
+
previous_compaction = metadata.get("lastCompactionAt")
|
|
79
|
+
compaction_history = list(metadata.get("compactionHistory", []))
|
|
80
|
+
now = int(time.time() * 1000)
|
|
81
|
+
|
|
82
|
+
compaction_history.append(
|
|
83
|
+
{
|
|
84
|
+
"timestamp": now,
|
|
85
|
+
"triggeredBy": str(message.entity_id),
|
|
86
|
+
"reason": "manual_reset",
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
metadata["lastCompactionAt"] = now
|
|
90
|
+
metadata["compactionHistory"] = compaction_history[-10:]
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
hasattr(room, "metadata")
|
|
94
|
+
and hasattr(room.metadata, "clear")
|
|
95
|
+
and hasattr(room.metadata, "update")
|
|
96
|
+
):
|
|
97
|
+
room.metadata.clear()
|
|
98
|
+
room.metadata.update(metadata)
|
|
99
|
+
else:
|
|
100
|
+
room.metadata = metadata
|
|
101
|
+
|
|
102
|
+
await runtime.update_room(room)
|
|
103
|
+
|
|
104
|
+
if callback:
|
|
105
|
+
await callback(
|
|
106
|
+
Content(
|
|
107
|
+
text="Session has been reset. I'll start fresh from here.",
|
|
108
|
+
actions=["RESET_SESSION"],
|
|
109
|
+
source=getattr(message.content, "source", None),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
result = ActionResult(
|
|
114
|
+
text="Session reset successfully",
|
|
115
|
+
values={
|
|
116
|
+
"success": True,
|
|
117
|
+
"compactionAt": now,
|
|
118
|
+
"roomId": str(room.id),
|
|
119
|
+
},
|
|
120
|
+
data={
|
|
121
|
+
"actionName": "RESET_SESSION",
|
|
122
|
+
"compactionAt": now,
|
|
123
|
+
"roomId": str(room.id),
|
|
124
|
+
},
|
|
125
|
+
success=True,
|
|
126
|
+
)
|
|
127
|
+
if previous_compaction is not None:
|
|
128
|
+
result.values["previousCompactionAt"] = previous_compaction
|
|
129
|
+
return result
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def examples(self) -> list[list[ActionExample]]:
|
|
133
|
+
return []
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
reset_session_action = Action(
|
|
137
|
+
name=ResetSessionAction.name,
|
|
138
|
+
similes=ResetSessionAction().similes,
|
|
139
|
+
description=ResetSessionAction.description,
|
|
140
|
+
validate=ResetSessionAction().validate,
|
|
141
|
+
handler=ResetSessionAction().handler,
|
|
142
|
+
examples=ResetSessionAction().examples,
|
|
143
|
+
)
|
|
@@ -3,7 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import heapq
|
|
4
4
|
import re
|
|
5
5
|
import time
|
|
6
|
-
from
|
|
6
|
+
from collections import OrderedDict
|
|
7
|
+
from typing import TypeVar, cast
|
|
7
8
|
from uuid import UUID, uuid4
|
|
8
9
|
|
|
9
10
|
from elizaos.types.model import ModelType
|
|
@@ -27,6 +28,7 @@ from .types import (
|
|
|
27
28
|
_TABLE_SESSION_SUMMARY = "session_summary"
|
|
28
29
|
_TABLE_LONG_TERM_MEMORY = "long_term_memory"
|
|
29
30
|
_GLOBAL_LONG_TERM_ROOM_ID = string_to_uuid("advanced-memory:long-term")
|
|
31
|
+
_OrderedValueT = TypeVar("_OrderedValueT")
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
def _parse_summary_xml(xml: str) -> SummaryResult:
|
|
@@ -79,14 +81,28 @@ def _top_k_by_confidence(items: list[LongTermMemory], limit: int) -> list[LongTe
|
|
|
79
81
|
|
|
80
82
|
class MemoryService(Service):
|
|
81
83
|
service_type = "memory"
|
|
84
|
+
_MAX_LOCAL_SESSION_SUMMARIES = 500
|
|
85
|
+
_MAX_LOCAL_EXTRACTION_CHECKPOINTS = 500
|
|
86
|
+
_MAX_LOCAL_LONG_TERM_ENTITIES = 500
|
|
87
|
+
_MAX_LOCAL_LONG_TERM_PER_ENTITY = 200
|
|
82
88
|
|
|
83
89
|
def __init__(self, runtime=None) -> None:
|
|
84
90
|
super().__init__(runtime=runtime)
|
|
85
91
|
self._config: MemoryConfig = MemoryConfig()
|
|
86
92
|
# Fallback storage for runtimes without a DB adapter (tests/benchmarks).
|
|
87
|
-
self._session_summaries:
|
|
88
|
-
self._long_term:
|
|
89
|
-
self._extraction_checkpoints:
|
|
93
|
+
self._session_summaries: OrderedDict[str, SessionSummary] = OrderedDict()
|
|
94
|
+
self._long_term: OrderedDict[str, list[LongTermMemory]] = OrderedDict()
|
|
95
|
+
self._extraction_checkpoints: OrderedDict[str, int] = OrderedDict()
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def _touch_ordered(mapping: OrderedDict[str, _OrderedValueT], key: str) -> None:
|
|
99
|
+
if key in mapping:
|
|
100
|
+
mapping.move_to_end(key)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def _prune_ordered(mapping: OrderedDict[str, _OrderedValueT], max_entries: int) -> None:
|
|
104
|
+
while len(mapping) > max_entries:
|
|
105
|
+
mapping.popitem(last=False)
|
|
90
106
|
|
|
91
107
|
@property
|
|
92
108
|
def capability_description(self) -> str:
|
|
@@ -156,7 +172,11 @@ class MemoryService(Service):
|
|
|
156
172
|
return 0
|
|
157
173
|
except Exception:
|
|
158
174
|
return 0
|
|
159
|
-
|
|
175
|
+
checkpoint = self._extraction_checkpoints.get(key)
|
|
176
|
+
if checkpoint is None:
|
|
177
|
+
return 0
|
|
178
|
+
self._touch_ordered(self._extraction_checkpoints, key)
|
|
179
|
+
return int(checkpoint)
|
|
160
180
|
|
|
161
181
|
async def set_last_extraction_checkpoint(
|
|
162
182
|
self, entity_id: UUID, room_id: UUID, message_count: int
|
|
@@ -167,6 +187,8 @@ class MemoryService(Service):
|
|
|
167
187
|
_ = await runtime.set_cache(key, int(message_count))
|
|
168
188
|
return
|
|
169
189
|
self._extraction_checkpoints[key] = int(message_count)
|
|
190
|
+
self._touch_ordered(self._extraction_checkpoints, key)
|
|
191
|
+
self._prune_ordered(self._extraction_checkpoints, self._MAX_LOCAL_EXTRACTION_CHECKPOINTS)
|
|
170
192
|
|
|
171
193
|
async def should_run_extraction(
|
|
172
194
|
self, entity_id: UUID, room_id: UUID, current_message_count: int
|
|
@@ -219,7 +241,11 @@ class MemoryService(Service):
|
|
|
219
241
|
# Best-effort; ignore corrupt rows.
|
|
220
242
|
continue
|
|
221
243
|
|
|
222
|
-
|
|
244
|
+
summary = self._session_summaries.get(str(room_id))
|
|
245
|
+
if summary is None:
|
|
246
|
+
return None
|
|
247
|
+
self._touch_ordered(self._session_summaries, str(room_id))
|
|
248
|
+
return summary
|
|
223
249
|
|
|
224
250
|
async def store_session_summary(
|
|
225
251
|
self,
|
|
@@ -271,7 +297,10 @@ class MemoryService(Service):
|
|
|
271
297
|
)
|
|
272
298
|
return s
|
|
273
299
|
|
|
274
|
-
|
|
300
|
+
room_key = str(room_id)
|
|
301
|
+
self._session_summaries[room_key] = s
|
|
302
|
+
self._touch_ordered(self._session_summaries, room_key)
|
|
303
|
+
self._prune_ordered(self._session_summaries, self._MAX_LOCAL_SESSION_SUMMARIES)
|
|
275
304
|
return s
|
|
276
305
|
|
|
277
306
|
async def update_session_summary(
|
|
@@ -322,9 +351,11 @@ class MemoryService(Service):
|
|
|
322
351
|
)
|
|
323
352
|
return
|
|
324
353
|
|
|
325
|
-
|
|
354
|
+
room_key = str(room_id)
|
|
355
|
+
existing = self._session_summaries.get(room_key)
|
|
326
356
|
if not existing or existing.id != summary_id:
|
|
327
357
|
return
|
|
358
|
+
self._touch_ordered(self._session_summaries, room_key)
|
|
328
359
|
if "summary" in updates and isinstance(updates["summary"], str):
|
|
329
360
|
existing.summary = updates["summary"]
|
|
330
361
|
if "message_count" in updates and isinstance(updates["message_count"], (int, float, str)):
|
|
@@ -381,7 +412,17 @@ class MemoryService(Service):
|
|
|
381
412
|
)
|
|
382
413
|
return m
|
|
383
414
|
|
|
384
|
-
|
|
415
|
+
entity_key = str(entity_id)
|
|
416
|
+
bucket = self._long_term.get(entity_key)
|
|
417
|
+
if bucket is None:
|
|
418
|
+
bucket = []
|
|
419
|
+
self._long_term[entity_key] = bucket
|
|
420
|
+
else:
|
|
421
|
+
self._touch_ordered(self._long_term, entity_key)
|
|
422
|
+
bucket.append(m)
|
|
423
|
+
if len(bucket) > self._MAX_LOCAL_LONG_TERM_PER_ENTITY:
|
|
424
|
+
del bucket[: len(bucket) - self._MAX_LOCAL_LONG_TERM_PER_ENTITY]
|
|
425
|
+
self._prune_ordered(self._long_term, self._MAX_LOCAL_LONG_TERM_ENTITIES)
|
|
385
426
|
return m
|
|
386
427
|
|
|
387
428
|
async def get_long_term_memories(
|
|
@@ -437,7 +478,10 @@ class MemoryService(Service):
|
|
|
437
478
|
continue
|
|
438
479
|
return _top_k_by_confidence(out, limit)
|
|
439
480
|
|
|
440
|
-
|
|
481
|
+
entity_key = str(entity_id)
|
|
482
|
+
local_mems = self._long_term.get(entity_key, [])
|
|
483
|
+
if entity_key in self._long_term:
|
|
484
|
+
self._touch_ordered(self._long_term, entity_key)
|
|
441
485
|
if category is not None:
|
|
442
486
|
local_mems = [m for m in local_mems if m.category == category]
|
|
443
487
|
return _top_k_by_confidence(local_mems, limit)
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from elizaos.types import Plugin
|
|
4
4
|
|
|
5
|
+
from .actions.reset_session import reset_session_action
|
|
5
6
|
from .evaluators import long_term_extraction_evaluator, summarization_evaluator
|
|
6
7
|
from .memory_service import MemoryService
|
|
7
8
|
from .providers import context_summary_provider, long_term_memory_provider
|
|
@@ -21,7 +22,7 @@ def create_advanced_memory_plugin() -> Plugin:
|
|
|
21
22
|
init=init_plugin,
|
|
22
23
|
config={},
|
|
23
24
|
services=[MemoryService],
|
|
24
|
-
actions=[],
|
|
25
|
+
actions=[reset_session_action],
|
|
25
26
|
providers=[long_term_memory_provider, context_summary_provider],
|
|
26
27
|
evaluators=[summarization_evaluator, long_term_extraction_evaluator],
|
|
27
28
|
)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from enum import
|
|
4
|
+
from enum import StrEnum
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class LongTermMemoryCategory(
|
|
8
|
+
class LongTermMemoryCategory(StrEnum):
|
|
9
9
|
EPISODIC = "episodic"
|
|
10
10
|
SEMANTIC = "semantic"
|
|
11
11
|
PROCEDURAL = "procedural"
|
|
@@ -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 typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
6
6
|
from uuid import UUID as StdUUID
|
|
7
7
|
|
|
8
8
|
from elizaos.bootstrap.utils.xml import parse_key_value_xml
|
|
@@ -32,7 +32,7 @@ _spec = require_action_spec("SCHEDULE_FOLLOW_UP")
|
|
|
32
32
|
|
|
33
33
|
def _convert_spec_examples() -> list[list[ActionExample]]:
|
|
34
34
|
"""Convert spec examples to ActionExample format."""
|
|
35
|
-
spec_examples = _spec.get("examples", [])
|
|
35
|
+
spec_examples = cast(list[list[dict[str, Any]]], _spec.get("examples", []))
|
|
36
36
|
if spec_examples:
|
|
37
37
|
return [
|
|
38
38
|
[
|