mem-llm 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.
Potentially problematic release.
This version of mem-llm might be problematic. Click here for more details.
- mem_llm-1.0.0.dist-info/METADATA +382 -0
- mem_llm-1.0.0.dist-info/RECORD +14 -0
- mem_llm-1.0.0.dist-info/WHEEL +5 -0
- mem_llm-1.0.0.dist-info/top_level.txt +1 -0
- memory_llm/__init__.py +34 -0
- memory_llm/config.yaml.example +52 -0
- memory_llm/config_manager.py +229 -0
- memory_llm/knowledge_loader.py +88 -0
- memory_llm/llm_client.py +162 -0
- memory_llm/mem_agent.py +512 -0
- memory_llm/memory_db.py +376 -0
- memory_llm/memory_manager.py +257 -0
- memory_llm/memory_tools.py +253 -0
- memory_llm/prompt_templates.py +244 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User Tools System
|
|
3
|
+
Tools for users to manage their memory data
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Dict, List, Optional, Any
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MemoryTools:
|
|
13
|
+
"""User memory management tools"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, memory_manager):
|
|
16
|
+
"""
|
|
17
|
+
Args:
|
|
18
|
+
memory_manager: Memory manager (MemoryManager or SQLMemoryManager)
|
|
19
|
+
"""
|
|
20
|
+
self.memory = memory_manager
|
|
21
|
+
self.tools = {
|
|
22
|
+
"list_memories": {
|
|
23
|
+
"description": "Lists all user conversations",
|
|
24
|
+
"parameters": {
|
|
25
|
+
"user_id": "User ID",
|
|
26
|
+
"limit": "Number of conversations to show (default: 10)"
|
|
27
|
+
},
|
|
28
|
+
"function": self._list_memories
|
|
29
|
+
},
|
|
30
|
+
"search_memories": {
|
|
31
|
+
"description": "Search for keywords in conversations",
|
|
32
|
+
"parameters": {
|
|
33
|
+
"user_id": "User ID",
|
|
34
|
+
"keyword": "Keyword to search",
|
|
35
|
+
"limit": "Number of results to show (default: 5)"
|
|
36
|
+
},
|
|
37
|
+
"function": self._search_memories
|
|
38
|
+
},
|
|
39
|
+
"show_user_info": {
|
|
40
|
+
"description": "Show information about user",
|
|
41
|
+
"parameters": {
|
|
42
|
+
"user_id": "User ID"
|
|
43
|
+
},
|
|
44
|
+
"function": self._show_user_info
|
|
45
|
+
},
|
|
46
|
+
"export_memories": {
|
|
47
|
+
"description": "Export user data",
|
|
48
|
+
"parameters": {
|
|
49
|
+
"user_id": "User ID",
|
|
50
|
+
"format": "Format (json or txt)"
|
|
51
|
+
},
|
|
52
|
+
"function": self._export_memories
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def _list_memories(self, user_id: str, limit: int = 10) -> str:
|
|
57
|
+
"""List user conversations"""
|
|
58
|
+
try:
|
|
59
|
+
conversations = self.memory.get_recent_conversations(user_id, limit)
|
|
60
|
+
|
|
61
|
+
if not conversations:
|
|
62
|
+
return f"❌ No conversations found for user {user_id}."
|
|
63
|
+
|
|
64
|
+
result = f"📝 Last {len(conversations)} conversations for user {user_id}:\n\n"
|
|
65
|
+
|
|
66
|
+
for i, conv in enumerate(conversations, 1):
|
|
67
|
+
timestamp = conv.get('timestamp', 'Unknown')
|
|
68
|
+
user_msg = conv.get('user_message', '')[:100]
|
|
69
|
+
bot_response = conv.get('bot_response', '')[:100]
|
|
70
|
+
|
|
71
|
+
result += f"{i}. [{timestamp}]\n"
|
|
72
|
+
result += f" 👤 User: {user_msg}...\n"
|
|
73
|
+
result += f" 🤖 Bot: {bot_response}...\n\n"
|
|
74
|
+
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
except Exception as e:
|
|
78
|
+
return f"❌ Error: {str(e)}"
|
|
79
|
+
|
|
80
|
+
def _search_memories(self, user_id: str, keyword: str, limit: int = 5) -> str:
|
|
81
|
+
"""Search in conversations"""
|
|
82
|
+
try:
|
|
83
|
+
results = self.memory.search_conversations(user_id, keyword)
|
|
84
|
+
|
|
85
|
+
if not results:
|
|
86
|
+
return f"❌ No results found for keyword '{keyword}' for user {user_id}."
|
|
87
|
+
|
|
88
|
+
result = f"🔍 {len(results)} results found for keyword '{keyword}':\n\n"
|
|
89
|
+
|
|
90
|
+
for i, conv in enumerate(results[:limit], 1):
|
|
91
|
+
timestamp = conv.get('timestamp', 'Unknown')
|
|
92
|
+
user_msg = conv.get('user_message', '')
|
|
93
|
+
bot_response = conv.get('bot_response', '')
|
|
94
|
+
|
|
95
|
+
result += f"{i}. [{timestamp}]\n"
|
|
96
|
+
result += f" 👤 User: {user_msg}\n"
|
|
97
|
+
result += f" 🤖 Bot: {bot_response}\n\n"
|
|
98
|
+
|
|
99
|
+
if len(results) > limit:
|
|
100
|
+
result += f"... and {len(results) - limit} more results."
|
|
101
|
+
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
return f"❌ Search error: {str(e)}"
|
|
106
|
+
|
|
107
|
+
def _show_user_info(self, user_id: str) -> str:
|
|
108
|
+
"""Show user information"""
|
|
109
|
+
try:
|
|
110
|
+
profile = self.memory.get_user_profile(user_id)
|
|
111
|
+
|
|
112
|
+
if not profile:
|
|
113
|
+
return f"❌ User {user_id} not found."
|
|
114
|
+
|
|
115
|
+
result = f"👤 User information for {user_id}:\n\n"
|
|
116
|
+
|
|
117
|
+
if profile.get('name'):
|
|
118
|
+
result += f"Name: {profile['name']}\n"
|
|
119
|
+
|
|
120
|
+
if profile.get('first_seen'):
|
|
121
|
+
result += f"First conversation: {profile['first_seen']}\n"
|
|
122
|
+
|
|
123
|
+
return result
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
return f"❌ Information retrieval error: {str(e)}"
|
|
127
|
+
|
|
128
|
+
def _export_memories(self, user_id: str, format: str = "json") -> str:
|
|
129
|
+
"""Export user data"""
|
|
130
|
+
try:
|
|
131
|
+
if format == "json":
|
|
132
|
+
profile = self.memory.get_user_profile(user_id)
|
|
133
|
+
conversations = self.memory.get_recent_conversations(user_id, 1000)
|
|
134
|
+
|
|
135
|
+
export_data = {
|
|
136
|
+
"user_id": user_id,
|
|
137
|
+
"export_date": datetime.now().isoformat(),
|
|
138
|
+
"profile": profile,
|
|
139
|
+
"conversations": conversations
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return json.dumps(export_data, ensure_ascii=False, indent=2)
|
|
143
|
+
|
|
144
|
+
elif format == "txt":
|
|
145
|
+
conversations = self.memory.get_recent_conversations(user_id, 1000)
|
|
146
|
+
|
|
147
|
+
result = f"Conversation history for user {user_id}\n"
|
|
148
|
+
result += f"Export date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
|
149
|
+
result += "=" * 60 + "\n\n"
|
|
150
|
+
|
|
151
|
+
for i, conv in enumerate(conversations, 1):
|
|
152
|
+
result += f"Conversation {i}:\n"
|
|
153
|
+
result += f"Date: {conv.get('timestamp', 'Unknown')}\n"
|
|
154
|
+
result += f"User: {conv.get('user_message', '')}\n"
|
|
155
|
+
result += f"Bot: {conv.get('bot_response', '')}\n"
|
|
156
|
+
result += "-" * 40 + "\n"
|
|
157
|
+
|
|
158
|
+
return result
|
|
159
|
+
|
|
160
|
+
else:
|
|
161
|
+
return "❌ Unsupported format. Use json or txt."
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
return f"❌ Export error: {str(e)}"
|
|
165
|
+
|
|
166
|
+
def execute_tool(self, tool_name: str, parameters: Dict[str, Any]) -> str:
|
|
167
|
+
"""Execute the specified tool"""
|
|
168
|
+
if tool_name not in self.tools:
|
|
169
|
+
return f"❌ Tool '{tool_name}' not found."
|
|
170
|
+
|
|
171
|
+
tool = self.tools[tool_name]
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
if "user_id" in parameters:
|
|
175
|
+
result = tool["function"](**parameters)
|
|
176
|
+
else:
|
|
177
|
+
return "❌ user_id parameter required."
|
|
178
|
+
|
|
179
|
+
return result
|
|
180
|
+
|
|
181
|
+
except Exception as e:
|
|
182
|
+
return f"❌ Tool execution error: {str(e)}"
|
|
183
|
+
|
|
184
|
+
def list_available_tools(self) -> str:
|
|
185
|
+
"""List available tools"""
|
|
186
|
+
result = "🛠️ Available Tools:\n\n"
|
|
187
|
+
|
|
188
|
+
for name, tool in self.tools.items():
|
|
189
|
+
result += f"🔧 {name}\n"
|
|
190
|
+
result += f" Description: {tool['description']}\n"
|
|
191
|
+
result += " Parameters:\n"
|
|
192
|
+
|
|
193
|
+
for param, desc in tool['parameters'].items():
|
|
194
|
+
result += f" • {param}: {desc}\n"
|
|
195
|
+
|
|
196
|
+
result += "\n"
|
|
197
|
+
|
|
198
|
+
return result
|
|
199
|
+
|
|
200
|
+
def parse_user_command(self, user_message: str) -> tuple:
|
|
201
|
+
"""Extract tool call from user message"""
|
|
202
|
+
patterns = {
|
|
203
|
+
"list_memories": [
|
|
204
|
+
r"show.*my.*past.*conversations",
|
|
205
|
+
r"list.*my.*conversations",
|
|
206
|
+
],
|
|
207
|
+
"show_user_info": [
|
|
208
|
+
r"what.*do.*you.*know.*about.*me",
|
|
209
|
+
r"show.*my.*profile"
|
|
210
|
+
],
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
message_lower = user_message.lower()
|
|
214
|
+
|
|
215
|
+
for tool_name, pattern_list in patterns.items():
|
|
216
|
+
for pattern in pattern_list:
|
|
217
|
+
match = re.search(pattern, message_lower)
|
|
218
|
+
if match:
|
|
219
|
+
parameters = {"user_id": "current_user"}
|
|
220
|
+
return tool_name, parameters
|
|
221
|
+
|
|
222
|
+
return None, None
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class ToolExecutor:
|
|
226
|
+
"""Tool executor"""
|
|
227
|
+
|
|
228
|
+
def __init__(self, memory_manager, current_user_id: str = None):
|
|
229
|
+
"""
|
|
230
|
+
Args:
|
|
231
|
+
memory_manager: Memory manager
|
|
232
|
+
current_user_id: Current user ID
|
|
233
|
+
"""
|
|
234
|
+
self.memory_tools = MemoryTools(memory_manager)
|
|
235
|
+
self.current_user_id = current_user_id
|
|
236
|
+
|
|
237
|
+
def execute_user_command(self, user_message: str, user_id: str = None) -> str:
|
|
238
|
+
"""Detect and execute tool call from user message"""
|
|
239
|
+
uid = user_id or self.current_user_id
|
|
240
|
+
|
|
241
|
+
tool_name, parameters = self.memory_tools.parse_user_command(user_message)
|
|
242
|
+
|
|
243
|
+
if tool_name and uid:
|
|
244
|
+
parameters["user_id"] = uid
|
|
245
|
+
return self.memory_tools.execute_tool(tool_name, parameters)
|
|
246
|
+
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
def is_tool_command(self, user_message: str) -> bool:
|
|
250
|
+
"""Check if message is a tool command"""
|
|
251
|
+
tool_name, _ = self.memory_tools.parse_user_command(user_message)
|
|
252
|
+
return tool_name is not None
|
|
253
|
+
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""
|
|
2
|
+
System Prompt Templates and Management
|
|
3
|
+
Customizable prompt templates for different scenarios
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Dict, List, Optional
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PromptTemplate:
|
|
11
|
+
"""System prompt template"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, name: str, base_prompt: str,
|
|
14
|
+
variables: Optional[Dict[str, str]] = None):
|
|
15
|
+
"""
|
|
16
|
+
Args:
|
|
17
|
+
name: Template name
|
|
18
|
+
base_prompt: Base prompt text (can contain variables in {variable} format)
|
|
19
|
+
variables: Default variable values
|
|
20
|
+
"""
|
|
21
|
+
self.name = name
|
|
22
|
+
self.base_prompt = base_prompt
|
|
23
|
+
self.variables = variables or {}
|
|
24
|
+
|
|
25
|
+
def render(self, **kwargs) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Fill template with variables
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
**kwargs: Variable values
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Generated prompt
|
|
34
|
+
"""
|
|
35
|
+
merged_vars = {**self.variables, **kwargs}
|
|
36
|
+
return self.base_prompt.format(**merged_vars)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PromptManager:
|
|
40
|
+
"""Manages prompt templates"""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
self.templates: Dict[str, PromptTemplate] = {}
|
|
44
|
+
self._load_default_templates()
|
|
45
|
+
|
|
46
|
+
def _load_default_templates(self) -> None:
|
|
47
|
+
"""Load default templates"""
|
|
48
|
+
|
|
49
|
+
# 1. Customer Service
|
|
50
|
+
self.add_template(
|
|
51
|
+
name="customer_service",
|
|
52
|
+
base_prompt="""You are a professional customer service assistant for {company_name} company.
|
|
53
|
+
|
|
54
|
+
Your task:
|
|
55
|
+
- Approach customers kindly and helpfully
|
|
56
|
+
- Remember past interactions and create context
|
|
57
|
+
- Solve problems quickly and effectively
|
|
58
|
+
- Redirect to human representative when necessary
|
|
59
|
+
|
|
60
|
+
Communication Style:
|
|
61
|
+
- Use {tone} tone
|
|
62
|
+
- Give short and clear answers
|
|
63
|
+
- Show empathy
|
|
64
|
+
- Be professional
|
|
65
|
+
|
|
66
|
+
Important Rules:
|
|
67
|
+
- Never lie
|
|
68
|
+
- Don't speculate on topics you don't know
|
|
69
|
+
- Keep customer satisfaction in the foreground
|
|
70
|
+
- Ask if there's any other help at the end of each response
|
|
71
|
+
|
|
72
|
+
You are currently working on {current_date}.
|
|
73
|
+
""",
|
|
74
|
+
variables={
|
|
75
|
+
"company_name": "Our Company",
|
|
76
|
+
"tone": "friendly and professional",
|
|
77
|
+
"current_date": datetime.now().strftime("%Y-%m-%d")
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# 2. Technical Support
|
|
82
|
+
self.add_template(
|
|
83
|
+
name="tech_support",
|
|
84
|
+
base_prompt="""You are a technical support expert for {product_name}.
|
|
85
|
+
|
|
86
|
+
Your Expertise Areas:
|
|
87
|
+
- Problem diagnosis and resolution
|
|
88
|
+
- Step-by-step guidance
|
|
89
|
+
- Technical documentation
|
|
90
|
+
- Debugging
|
|
91
|
+
|
|
92
|
+
Approach:
|
|
93
|
+
- First understand the problem completely
|
|
94
|
+
- Start with simple solutions
|
|
95
|
+
- Explain step by step
|
|
96
|
+
- Explain technical terms when necessary
|
|
97
|
+
|
|
98
|
+
User Level: {user_level}
|
|
99
|
+
|
|
100
|
+
Response Format:
|
|
101
|
+
1. Summarize the problem
|
|
102
|
+
2. List possible causes
|
|
103
|
+
3. Provide solution steps
|
|
104
|
+
4. Check results
|
|
105
|
+
|
|
106
|
+
Log level: {log_level}
|
|
107
|
+
""",
|
|
108
|
+
variables={
|
|
109
|
+
"product_name": "Our Product",
|
|
110
|
+
"user_level": "intermediate level",
|
|
111
|
+
"log_level": "detailed"
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# 3. Personal Assistant
|
|
116
|
+
self.add_template(
|
|
117
|
+
name="personal_assistant",
|
|
118
|
+
base_prompt="""Sen {user_name} için kişisel dijital asistansın.
|
|
119
|
+
|
|
120
|
+
Görevlerin:
|
|
121
|
+
- Günlük planlamasına yardım
|
|
122
|
+
- Hatırlatmalar
|
|
123
|
+
- Bilgi toplama ve özetleme
|
|
124
|
+
- Öneri ve tavsiyeler
|
|
125
|
+
|
|
126
|
+
Kişiselleştirme:
|
|
127
|
+
- Kullanıcının tercihlerini öğren
|
|
128
|
+
- Alışkanlıklarını hatırla
|
|
129
|
+
- Proaktif önerilerde bulun
|
|
130
|
+
- Önceliklere göre sırala
|
|
131
|
+
|
|
132
|
+
Çalışma Saatleri: {work_hours}
|
|
133
|
+
Zaman Dilimi: {timezone}
|
|
134
|
+
Tercih Edilen Dil: {language}
|
|
135
|
+
|
|
136
|
+
Yaklaşım:
|
|
137
|
+
- Verimlilik odaklı
|
|
138
|
+
- Minimal ve net
|
|
139
|
+
- Proaktif
|
|
140
|
+
- Esnek
|
|
141
|
+
|
|
142
|
+
Veri Gizliliği: {privacy_level}
|
|
143
|
+
""",
|
|
144
|
+
variables={
|
|
145
|
+
"user_name": "Kullanıcı",
|
|
146
|
+
"work_hours": "09:00-18:00",
|
|
147
|
+
"timezone": "Europe/Istanbul",
|
|
148
|
+
"language": "Türkçe",
|
|
149
|
+
"privacy_level": "yüksek"
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# 4. Business Customer Service
|
|
154
|
+
self.add_template(
|
|
155
|
+
name="business_customer_service",
|
|
156
|
+
base_prompt="""Sen {company_name} şirketinin kurumsal müşteri hizmetleri asistanısın.
|
|
157
|
+
|
|
158
|
+
Kurumsal Müşteri Yaklaşımı:
|
|
159
|
+
- Profesyonel ve çözüm odaklı
|
|
160
|
+
- SLA'lara uygun hızlı yanıt
|
|
161
|
+
- Teknik sorunlara derin destek
|
|
162
|
+
- Çoklu kanal entegrasyonu
|
|
163
|
+
|
|
164
|
+
Şirket Bilgileri:
|
|
165
|
+
- Kuruluş Yılı: {founded_year}
|
|
166
|
+
- Çalışan Sayısı: {employee_count}
|
|
167
|
+
- Sektör: {industry}
|
|
168
|
+
|
|
169
|
+
Öncelik Seviyesi: {priority_level}
|
|
170
|
+
SLA Süresi: {sla_hours} saat
|
|
171
|
+
""",
|
|
172
|
+
variables={
|
|
173
|
+
"company_name": "Kurumsal Şirket",
|
|
174
|
+
"founded_year": "2010",
|
|
175
|
+
"employee_count": "500+",
|
|
176
|
+
"industry": "Teknoloji",
|
|
177
|
+
"priority_level": "yüksek",
|
|
178
|
+
"sla_hours": "4"
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def add_template(self, name: str, base_prompt: str,
|
|
183
|
+
variables: Optional[Dict[str, str]] = None) -> None:
|
|
184
|
+
"""
|
|
185
|
+
Add new template
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
name: Template name
|
|
189
|
+
base_prompt: Prompt text
|
|
190
|
+
variables: Default variables
|
|
191
|
+
"""
|
|
192
|
+
self.templates[name] = PromptTemplate(name, base_prompt, variables)
|
|
193
|
+
|
|
194
|
+
def get_template(self, name: str) -> Optional[PromptTemplate]:
|
|
195
|
+
"""
|
|
196
|
+
Get template
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
name: Template name
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
PromptTemplate or None
|
|
203
|
+
"""
|
|
204
|
+
return self.templates.get(name)
|
|
205
|
+
|
|
206
|
+
def render_prompt(self, template_name: str, **kwargs) -> str:
|
|
207
|
+
"""
|
|
208
|
+
Render template
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
template_name: Template name
|
|
212
|
+
**kwargs: Variable values
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Generated prompt
|
|
216
|
+
"""
|
|
217
|
+
template = self.get_template(template_name)
|
|
218
|
+
if template:
|
|
219
|
+
return template.render(**kwargs)
|
|
220
|
+
raise ValueError(f"Template '{template_name}' not found")
|
|
221
|
+
|
|
222
|
+
def list_templates(self) -> List[str]:
|
|
223
|
+
"""List available templates"""
|
|
224
|
+
return list(self.templates.keys())
|
|
225
|
+
|
|
226
|
+
def get_template_variables(self, template_name: str) -> Dict[str, str]:
|
|
227
|
+
"""
|
|
228
|
+
Return template variables
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
template_name: Template name
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Variables dictionary
|
|
235
|
+
"""
|
|
236
|
+
template = self.get_template(template_name)
|
|
237
|
+
if template:
|
|
238
|
+
return template.variables.copy()
|
|
239
|
+
return {}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# Global instance for ready use
|
|
243
|
+
prompt_manager = PromptManager()
|
|
244
|
+
|