aetherforge-platform 1.0.0__py3-none-any.whl
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.
- aetherforge_platform-1.0.0.dist-info/METADATA +86 -0
- aetherforge_platform-1.0.0.dist-info/RECORD +55 -0
- aetherforge_platform-1.0.0.dist-info/WHEEL +5 -0
- aetherforge_platform-1.0.0.dist-info/top_level.txt +4 -0
- ai-life-assistant-copy/ai_agent.py +145 -0
- ai-life-assistant-copy/avatar_manager.py +231 -0
- ai-life-assistant-copy/avatar_packer.py +261 -0
- ai-life-assistant-copy/backup_all.py +262 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/ai_agent.py +145 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/avatar_manager.py +231 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/avatar_packer.py +261 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/backup_all.py +262 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/commands.py +210 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/config.py +30 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/daemon/__init__.py +3 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/daemon/daemon.py +174 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/database.py +292 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/graph.py +531 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/main.py +830 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/mcp_tools.py +449 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/memory.py +92 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/memory_v2.py +333 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/mock_shopping_data.py +172 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/personality.py +159 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/speech.py +41 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/test_simple.py +127 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/__init__.py +15 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/amazon_tool.py +103 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/calendar_tool.py +92 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/reminder_tool.py +92 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/weather_tool.py +45 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tree_memory.py +340 -0
- ai-life-assistant-copy/commands.py +210 -0
- ai-life-assistant-copy/config.py +30 -0
- ai-life-assistant-copy/daemon/__init__.py +3 -0
- ai-life-assistant-copy/daemon/daemon.py +174 -0
- ai-life-assistant-copy/database.py +292 -0
- ai-life-assistant-copy/graph.py +531 -0
- ai-life-assistant-copy/main.py +830 -0
- ai-life-assistant-copy/mcp_tools.py +449 -0
- ai-life-assistant-copy/memory.py +92 -0
- ai-life-assistant-copy/memory_v2.py +333 -0
- ai-life-assistant-copy/mock_shopping_data.py +172 -0
- ai-life-assistant-copy/personality.py +159 -0
- ai-life-assistant-copy/speech.py +41 -0
- ai-life-assistant-copy/test_simple.py +127 -0
- ai-life-assistant-copy/tools/__init__.py +15 -0
- ai-life-assistant-copy/tools/amazon_tool.py +103 -0
- ai-life-assistant-copy/tools/calendar_tool.py +92 -0
- ai-life-assistant-copy/tools/reminder_tool.py +92 -0
- ai-life-assistant-copy/tools/weather_tool.py +45 -0
- ai-life-assistant-copy/tree_memory.py +340 -0
- ai_agent_runtime.py +447 -0
- main.py +6752 -0
- mcp_server.py +427 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import List, Dict, Any, Optional
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from tree_memory import tree_memory_system, InvertedTreeMemory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Avatar:
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
avatar_id: Optional[str] = None,
|
|
13
|
+
name: str = "虚拟形象",
|
|
14
|
+
description: str = "",
|
|
15
|
+
personality_traits: List[str] = None,
|
|
16
|
+
current_age: int = 25,
|
|
17
|
+
birth_year: Optional[int] = None,
|
|
18
|
+
avatar_image: Optional[str] = None,
|
|
19
|
+
is_public: bool = False,
|
|
20
|
+
price: Optional[float] = None,
|
|
21
|
+
creator_id: Optional[str] = None,
|
|
22
|
+
created_at: Optional[str] = None,
|
|
23
|
+
updated_at: Optional[str] = None
|
|
24
|
+
):
|
|
25
|
+
self.avatar_id = avatar_id or str(uuid.uuid4())
|
|
26
|
+
self.name = name
|
|
27
|
+
self.description = description
|
|
28
|
+
self.personality_traits = personality_traits or ["友好", "好奇"]
|
|
29
|
+
self.current_age = current_age
|
|
30
|
+
self.birth_year = birth_year or (datetime.now().year - current_age)
|
|
31
|
+
self.avatar_image = avatar_image
|
|
32
|
+
self.is_public = is_public
|
|
33
|
+
self.price = price
|
|
34
|
+
self.creator_id = creator_id
|
|
35
|
+
self.created_at = created_at or datetime.now().isoformat()
|
|
36
|
+
self.updated_at = updated_at or datetime.now().isoformat()
|
|
37
|
+
self.settings = {
|
|
38
|
+
"developer_mode": False,
|
|
39
|
+
"auto_organize": True,
|
|
40
|
+
"privacy_level": "private"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
44
|
+
return {
|
|
45
|
+
"avatar_id": self.avatar_id,
|
|
46
|
+
"name": self.name,
|
|
47
|
+
"description": self.description,
|
|
48
|
+
"personality_traits": self.personality_traits,
|
|
49
|
+
"current_age": self.current_age,
|
|
50
|
+
"birth_year": self.birth_year,
|
|
51
|
+
"avatar_image": self.avatar_image,
|
|
52
|
+
"is_public": self.is_public,
|
|
53
|
+
"price": self.price,
|
|
54
|
+
"creator_id": self.creator_id,
|
|
55
|
+
"created_at": self.created_at,
|
|
56
|
+
"updated_at": self.updated_at,
|
|
57
|
+
"settings": self.settings
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Avatar':
|
|
62
|
+
avatar = cls(
|
|
63
|
+
avatar_id=data.get("avatar_id"),
|
|
64
|
+
name=data.get("name", "虚拟形象"),
|
|
65
|
+
description=data.get("description", ""),
|
|
66
|
+
personality_traits=data.get("personality_traits"),
|
|
67
|
+
current_age=data.get("current_age", 25),
|
|
68
|
+
birth_year=data.get("birth_year"),
|
|
69
|
+
avatar_image=data.get("avatar_image"),
|
|
70
|
+
is_public=data.get("is_public", False),
|
|
71
|
+
price=data.get("price"),
|
|
72
|
+
creator_id=data.get("creator_id"),
|
|
73
|
+
created_at=data.get("created_at"),
|
|
74
|
+
updated_at=data.get("updated_at")
|
|
75
|
+
)
|
|
76
|
+
avatar.settings = data.get("settings", avatar.settings)
|
|
77
|
+
return avatar
|
|
78
|
+
|
|
79
|
+
def calculate_real_age(self) -> int:
|
|
80
|
+
return datetime.now().year - self.birth_year
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AvatarManager:
|
|
84
|
+
def __init__(self, storage_dir: str = "avatar_data"):
|
|
85
|
+
self.storage_dir = storage_dir
|
|
86
|
+
self.avatars_dir = os.path.join(storage_dir, "avatars")
|
|
87
|
+
self.market_dir = os.path.join(storage_dir, "market")
|
|
88
|
+
|
|
89
|
+
for dir_path in [self.storage_dir, self.avatars_dir, self.market_dir]:
|
|
90
|
+
if not os.path.exists(dir_path):
|
|
91
|
+
os.makedirs(dir_path)
|
|
92
|
+
|
|
93
|
+
def _get_avatar_file(self, avatar_id: str) -> str:
|
|
94
|
+
return os.path.join(self.avatars_dir, f"{avatar_id}.json")
|
|
95
|
+
|
|
96
|
+
def create_avatar(
|
|
97
|
+
self,
|
|
98
|
+
name: str,
|
|
99
|
+
description: str = "",
|
|
100
|
+
personality_traits: List[str] = None,
|
|
101
|
+
current_age: int = 25,
|
|
102
|
+
creator_id: Optional[str] = None
|
|
103
|
+
) -> Avatar:
|
|
104
|
+
avatar = Avatar(
|
|
105
|
+
name=name,
|
|
106
|
+
description=description,
|
|
107
|
+
personality_traits=personality_traits,
|
|
108
|
+
current_age=current_age,
|
|
109
|
+
creator_id=creator_id
|
|
110
|
+
)
|
|
111
|
+
self.save_avatar(avatar)
|
|
112
|
+
return avatar
|
|
113
|
+
|
|
114
|
+
def save_avatar(self, avatar: Avatar):
|
|
115
|
+
avatar.updated_at = datetime.now().isoformat()
|
|
116
|
+
file_path = self._get_avatar_file(avatar.avatar_id)
|
|
117
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
118
|
+
json.dump(avatar.to_dict(), f, ensure_ascii=False, indent=2)
|
|
119
|
+
|
|
120
|
+
def load_avatar(self, avatar_id: str) -> Optional[Avatar]:
|
|
121
|
+
file_path = self._get_avatar_file(avatar_id)
|
|
122
|
+
if os.path.exists(file_path):
|
|
123
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
124
|
+
data = json.load(f)
|
|
125
|
+
return Avatar.from_dict(data)
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def update_avatar(self, avatar_id: str, **kwargs) -> Optional[Avatar]:
|
|
129
|
+
avatar = self.load_avatar(avatar_id)
|
|
130
|
+
if not avatar:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
for key, value in kwargs.items():
|
|
134
|
+
if hasattr(avatar, key):
|
|
135
|
+
setattr(avatar, key, value)
|
|
136
|
+
|
|
137
|
+
self.save_avatar(avatar)
|
|
138
|
+
return avatar
|
|
139
|
+
|
|
140
|
+
def delete_avatar(self, avatar_id: str) -> bool:
|
|
141
|
+
file_path = self._get_avatar_file(avatar_id)
|
|
142
|
+
if os.path.exists(file_path):
|
|
143
|
+
os.remove(file_path)
|
|
144
|
+
return True
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
def list_avatars(self, creator_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
148
|
+
avatars = []
|
|
149
|
+
for filename in os.listdir(self.avatars_dir):
|
|
150
|
+
if filename.endswith(".json"):
|
|
151
|
+
file_path = os.path.join(self.avatars_dir, filename)
|
|
152
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
153
|
+
data = json.load(f)
|
|
154
|
+
if creator_id is None or data.get("creator_id") == creator_id:
|
|
155
|
+
avatars.append(data)
|
|
156
|
+
return avatars
|
|
157
|
+
|
|
158
|
+
def set_age(self, avatar_id: str, age: int) -> Optional[Avatar]:
|
|
159
|
+
avatar = self.load_avatar(avatar_id)
|
|
160
|
+
if avatar:
|
|
161
|
+
avatar.current_age = age
|
|
162
|
+
self.save_avatar(avatar)
|
|
163
|
+
return avatar
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
def get_memories_for_age(self, avatar_id: str, target_age: int) -> List[Dict[str, Any]]:
|
|
167
|
+
return tree_memory_system.get_memories_by_age(avatar_id, target_age)
|
|
168
|
+
|
|
169
|
+
def add_memory(
|
|
170
|
+
self,
|
|
171
|
+
avatar_id: str,
|
|
172
|
+
content: str,
|
|
173
|
+
min_age: int = 0,
|
|
174
|
+
max_age: int = 150,
|
|
175
|
+
clarity: int = 80,
|
|
176
|
+
emotional_tone: str = "neutral",
|
|
177
|
+
topic: Optional[str] = None
|
|
178
|
+
) -> str:
|
|
179
|
+
return tree_memory_system.add_memory(
|
|
180
|
+
avatar_id=avatar_id,
|
|
181
|
+
content=content,
|
|
182
|
+
min_age=min_age,
|
|
183
|
+
max_age=max_age,
|
|
184
|
+
clarity=clarity,
|
|
185
|
+
emotional_tone=emotional_tone,
|
|
186
|
+
topic=topic
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def get_tree_structure(self, avatar_id: str) -> Optional[Dict[str, Any]]:
|
|
190
|
+
return tree_memory_system.get_tree_structure(avatar_id)
|
|
191
|
+
|
|
192
|
+
def toggle_developer_mode(self, avatar_id: str) -> Optional[bool]:
|
|
193
|
+
avatar = self.load_avatar(avatar_id)
|
|
194
|
+
if avatar:
|
|
195
|
+
avatar.settings["developer_mode"] = not avatar.settings["developer_mode"]
|
|
196
|
+
self.save_avatar(avatar)
|
|
197
|
+
return avatar.settings["developer_mode"]
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
def list_market_avatars(self) -> List[Dict[str, Any]]:
|
|
201
|
+
avatars = []
|
|
202
|
+
for filename in os.listdir(self.avatars_dir):
|
|
203
|
+
if filename.endswith(".json"):
|
|
204
|
+
file_path = os.path.join(self.avatars_dir, filename)
|
|
205
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
206
|
+
data = json.load(f)
|
|
207
|
+
if data.get("is_public") and data.get("price") is not None:
|
|
208
|
+
avatars.append(data)
|
|
209
|
+
return avatars
|
|
210
|
+
|
|
211
|
+
def purchase_avatar(self, avatar_id: str, buyer_id: str) -> Optional[Avatar]:
|
|
212
|
+
avatar = self.load_avatar(avatar_id)
|
|
213
|
+
if avatar and avatar.is_public and avatar.price is not None:
|
|
214
|
+
new_avatar = Avatar(
|
|
215
|
+
name=avatar.name,
|
|
216
|
+
description=avatar.description,
|
|
217
|
+
personality_traits=avatar.personality_traits.copy(),
|
|
218
|
+
current_age=avatar.current_age,
|
|
219
|
+
birth_year=avatar.birth_year,
|
|
220
|
+
creator_id=buyer_id
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
src_tree = tree_memory_system.load_tree(avatar_id)
|
|
224
|
+
tree_memory_system.save_tree(new_avatar.avatar_id, src_tree)
|
|
225
|
+
|
|
226
|
+
self.save_avatar(new_avatar)
|
|
227
|
+
return new_avatar
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
avatar_manager = AvatarManager()
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import zipfile
|
|
3
|
+
import os
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from avatar_manager import Avatar, avatar_manager
|
|
7
|
+
from tree_memory import tree_memory_system, InvertedTreeMemory
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AvatarPacker:
|
|
11
|
+
def __init__(self, export_dir: str = "avatar_exports"):
|
|
12
|
+
self.export_dir = export_dir
|
|
13
|
+
if not os.path.exists(export_dir):
|
|
14
|
+
os.makedirs(export_dir)
|
|
15
|
+
|
|
16
|
+
def export_avatar(self, avatar_id: str, export_format: str = "avatar") -> Optional[str]:
|
|
17
|
+
avatar = avatar_manager.load_avatar(avatar_id)
|
|
18
|
+
if not avatar:
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
tree = tree_memory_system.load_tree(avatar_id)
|
|
22
|
+
|
|
23
|
+
if export_format == "avatar":
|
|
24
|
+
return self._export_as_avatar_file(avatar, tree)
|
|
25
|
+
elif export_format == "json":
|
|
26
|
+
return self._export_as_json(avatar, tree)
|
|
27
|
+
elif export_format == "sql":
|
|
28
|
+
return self._export_as_sql(avatar, tree)
|
|
29
|
+
else:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def _export_as_avatar_file(self, avatar: Avatar, tree: InvertedTreeMemory) -> str:
|
|
33
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
34
|
+
filename = f"{avatar.name.replace(' ', '_')}_{timestamp}.avatar"
|
|
35
|
+
filepath = os.path.join(self.export_dir, filename)
|
|
36
|
+
|
|
37
|
+
package_data = {
|
|
38
|
+
"version": "1.0",
|
|
39
|
+
"type": "avatar_package",
|
|
40
|
+
"created_at": datetime.now().isoformat(),
|
|
41
|
+
"avatar": avatar.to_dict(),
|
|
42
|
+
"memory_tree": tree.to_dict()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
46
|
+
json.dump(package_data, f, ensure_ascii=False, indent=2)
|
|
47
|
+
|
|
48
|
+
return filepath
|
|
49
|
+
|
|
50
|
+
def _export_as_json(self, avatar: Avatar, tree: InvertedTreeMemory) -> str:
|
|
51
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
52
|
+
filename = f"{avatar.name.replace(' ', '_')}_{timestamp}.json"
|
|
53
|
+
filepath = os.path.join(self.export_dir, filename)
|
|
54
|
+
|
|
55
|
+
export_data = {
|
|
56
|
+
"avatar": avatar.to_dict(),
|
|
57
|
+
"memories": [node.to_dict() for node in tree.nodes.values()]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
61
|
+
json.dump(export_data, f, ensure_ascii=False, indent=2)
|
|
62
|
+
|
|
63
|
+
return filepath
|
|
64
|
+
|
|
65
|
+
def _export_as_sql(self, avatar: Avatar, tree: InvertedTreeMemory) -> str:
|
|
66
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
67
|
+
filename = f"{avatar.name.replace(' ', '_')}_{timestamp}.sql"
|
|
68
|
+
filepath = os.path.join(self.export_dir, filename)
|
|
69
|
+
|
|
70
|
+
sql_content = []
|
|
71
|
+
|
|
72
|
+
sql_content.append("-- Avatar System SQL Export")
|
|
73
|
+
sql_content.append(f"-- Generated at: {datetime.now().isoformat()}")
|
|
74
|
+
sql_content.append("")
|
|
75
|
+
|
|
76
|
+
sql_content.append("-- Create avatars table if not exists")
|
|
77
|
+
sql_content.append("""
|
|
78
|
+
CREATE TABLE IF NOT EXISTS avatars (
|
|
79
|
+
id VARCHAR(50) PRIMARY KEY,
|
|
80
|
+
name VARCHAR(255) NOT NULL,
|
|
81
|
+
description TEXT,
|
|
82
|
+
personality_traits JSON,
|
|
83
|
+
current_age INT DEFAULT 25,
|
|
84
|
+
birth_year INT,
|
|
85
|
+
avatar_image TEXT,
|
|
86
|
+
is_public BOOLEAN DEFAULT FALSE,
|
|
87
|
+
price DECIMAL(10, 2),
|
|
88
|
+
creator_id VARCHAR(50),
|
|
89
|
+
created_at TIMESTAMP,
|
|
90
|
+
updated_at TIMESTAMP,
|
|
91
|
+
settings JSON
|
|
92
|
+
);
|
|
93
|
+
""")
|
|
94
|
+
|
|
95
|
+
sql_content.append("-- Create memories table if not exists")
|
|
96
|
+
sql_content.append("""
|
|
97
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
98
|
+
id VARCHAR(50) PRIMARY KEY,
|
|
99
|
+
avatar_id VARCHAR(50) NOT NULL,
|
|
100
|
+
content TEXT NOT NULL,
|
|
101
|
+
min_age INT DEFAULT 0,
|
|
102
|
+
max_age INT DEFAULT 150,
|
|
103
|
+
clarity INT DEFAULT 80,
|
|
104
|
+
emotional_tone VARCHAR(50) DEFAULT 'neutral',
|
|
105
|
+
topic VARCHAR(255),
|
|
106
|
+
parent_id VARCHAR(50),
|
|
107
|
+
created_at TIMESTAMP,
|
|
108
|
+
INDEX idx_avatar_id (avatar_id),
|
|
109
|
+
FOREIGN KEY (avatar_id) REFERENCES avatars(id)
|
|
110
|
+
);
|
|
111
|
+
""")
|
|
112
|
+
|
|
113
|
+
sql_content.append("")
|
|
114
|
+
sql_content.append("-- Insert avatar data")
|
|
115
|
+
sql_content.append(f"""
|
|
116
|
+
INSERT INTO avatars (id, name, description, personality_traits, current_age,
|
|
117
|
+
birth_year, avatar_image, is_public, price, creator_id, created_at, updated_at, settings)
|
|
118
|
+
VALUES (
|
|
119
|
+
'{avatar.avatar_id}',
|
|
120
|
+
'{avatar.name.replace("'", "''")}',
|
|
121
|
+
'{avatar.description.replace("'", "''")}',
|
|
122
|
+
'{json.dumps(avatar.personality_traits, ensure_ascii=False)}',
|
|
123
|
+
{avatar.current_age},
|
|
124
|
+
{avatar.birth_year},
|
|
125
|
+
'{avatar.avatar_image or ''}',
|
|
126
|
+
{1 if avatar.is_public else 0},
|
|
127
|
+
{avatar.price if avatar.price else 'NULL'},
|
|
128
|
+
'{avatar.creator_id or ''}',
|
|
129
|
+
'{avatar.created_at}',
|
|
130
|
+
'{avatar.updated_at}',
|
|
131
|
+
'{json.dumps(avatar.settings, ensure_ascii=False)}'
|
|
132
|
+
);
|
|
133
|
+
""")
|
|
134
|
+
|
|
135
|
+
sql_content.append("")
|
|
136
|
+
sql_content.append("-- Insert memories")
|
|
137
|
+
for node in tree.nodes.values():
|
|
138
|
+
sql_content.append(f"""
|
|
139
|
+
INSERT INTO memories (id, avatar_id, content, min_age, max_age, clarity,
|
|
140
|
+
emotional_tone, topic, parent_id, created_at)
|
|
141
|
+
VALUES (
|
|
142
|
+
'{node.memory_id}',
|
|
143
|
+
'{avatar.avatar_id}',
|
|
144
|
+
'{node.content.replace("'", "''")}',
|
|
145
|
+
{node.min_age},
|
|
146
|
+
{node.max_age},
|
|
147
|
+
{node.clarity},
|
|
148
|
+
'{node.emotional_tone}',
|
|
149
|
+
'{node.topic or ''}',
|
|
150
|
+
'{node.parent_id or ''}',
|
|
151
|
+
'{node.created_at}'
|
|
152
|
+
);
|
|
153
|
+
""")
|
|
154
|
+
|
|
155
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
156
|
+
f.write("\n".join(sql_content))
|
|
157
|
+
|
|
158
|
+
return filepath
|
|
159
|
+
|
|
160
|
+
def import_avatar(self, filepath: str, new_creator_id: Optional[str] = None) -> Optional[str]:
|
|
161
|
+
if not os.path.exists(filepath):
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
if filepath.endswith(".avatar") or filepath.endswith(".json"):
|
|
165
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
166
|
+
data = json.load(f)
|
|
167
|
+
|
|
168
|
+
if "type" in data and data["type"] == "avatar_package":
|
|
169
|
+
avatar_data = data["avatar"]
|
|
170
|
+
tree_data = data["memory_tree"]
|
|
171
|
+
else:
|
|
172
|
+
avatar_data = data.get("avatar")
|
|
173
|
+
tree_data = None
|
|
174
|
+
|
|
175
|
+
if avatar_data:
|
|
176
|
+
avatar = Avatar.from_dict(avatar_data)
|
|
177
|
+
avatar.avatar_id = None
|
|
178
|
+
avatar.created_at = datetime.now().isoformat()
|
|
179
|
+
avatar.updated_at = datetime.now().isoformat()
|
|
180
|
+
avatar.is_public = False
|
|
181
|
+
avatar.price = None
|
|
182
|
+
if new_creator_id:
|
|
183
|
+
avatar.creator_id = new_creator_id
|
|
184
|
+
|
|
185
|
+
avatar_manager.save_avatar(avatar)
|
|
186
|
+
|
|
187
|
+
if tree_data:
|
|
188
|
+
tree = InvertedTreeMemory.from_dict(tree_data)
|
|
189
|
+
tree.tree_id = avatar.avatar_id
|
|
190
|
+
for node in tree.nodes.values():
|
|
191
|
+
node.memory_id = None
|
|
192
|
+
node.parent_id = None
|
|
193
|
+
tree_memory_system.save_tree(avatar.avatar_id, tree)
|
|
194
|
+
|
|
195
|
+
return avatar.avatar_id
|
|
196
|
+
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
def list_exports(self) -> list:
|
|
200
|
+
exports = []
|
|
201
|
+
for filename in os.listdir(self.export_dir):
|
|
202
|
+
filepath = os.path.join(self.export_dir, filename)
|
|
203
|
+
if os.path.isfile(filepath):
|
|
204
|
+
stat = os.stat(filepath)
|
|
205
|
+
exports.append({
|
|
206
|
+
"filename": filename,
|
|
207
|
+
"size": stat.st_size,
|
|
208
|
+
"created_at": datetime.fromtimestamp(stat.st_ctime).isoformat(),
|
|
209
|
+
"filepath": filepath
|
|
210
|
+
})
|
|
211
|
+
return sorted(exports, key=lambda x: x["created_at"], reverse=True)
|
|
212
|
+
|
|
213
|
+
def delete_export(self, filename: str) -> bool:
|
|
214
|
+
filepath = os.path.join(self.export_dir, filename)
|
|
215
|
+
if os.path.exists(filepath):
|
|
216
|
+
os.remove(filepath)
|
|
217
|
+
return True
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class MarketPlace:
|
|
222
|
+
def __init__(self, avatar_packer: AvatarPacker):
|
|
223
|
+
self.avatar_packer = avatar_packer
|
|
224
|
+
|
|
225
|
+
def list_for_sale(
|
|
226
|
+
self,
|
|
227
|
+
avatar_id: str,
|
|
228
|
+
price: float,
|
|
229
|
+
description: Optional[str] = None
|
|
230
|
+
) -> bool:
|
|
231
|
+
avatar = avatar_manager.load_avatar(avatar_id)
|
|
232
|
+
if avatar:
|
|
233
|
+
avatar.is_public = True
|
|
234
|
+
avatar.price = price
|
|
235
|
+
if description:
|
|
236
|
+
avatar.description = description
|
|
237
|
+
avatar_manager.save_avatar(avatar)
|
|
238
|
+
return True
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
def remove_from_sale(self, avatar_id: str) -> bool:
|
|
242
|
+
avatar = avatar_manager.load_avatar(avatar_id)
|
|
243
|
+
if avatar:
|
|
244
|
+
avatar.is_public = False
|
|
245
|
+
avatar.price = None
|
|
246
|
+
avatar_manager.save_avatar(avatar)
|
|
247
|
+
return True
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
def get_market_listings(self) -> list:
|
|
251
|
+
return avatar_manager.list_market_avatars()
|
|
252
|
+
|
|
253
|
+
def purchase_avatar(self, avatar_id: str, buyer_id: str) -> Optional[str]:
|
|
254
|
+
new_avatar = avatar_manager.purchase_avatar(avatar_id, buyer_id)
|
|
255
|
+
if new_avatar:
|
|
256
|
+
return new_avatar.avatar_id
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
avatar_packer = AvatarPacker()
|
|
261
|
+
marketplace = MarketPlace(avatar_packer)
|