mem-llm 2.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.
- mem_llm/__init__.py +98 -0
- mem_llm/api_server.py +595 -0
- mem_llm/base_llm_client.py +201 -0
- mem_llm/builtin_tools.py +311 -0
- mem_llm/cli.py +254 -0
- mem_llm/clients/__init__.py +22 -0
- mem_llm/clients/lmstudio_client.py +393 -0
- mem_llm/clients/ollama_client.py +354 -0
- mem_llm/config.yaml.example +52 -0
- mem_llm/config_from_docs.py +180 -0
- mem_llm/config_manager.py +231 -0
- mem_llm/conversation_summarizer.py +372 -0
- mem_llm/data_export_import.py +640 -0
- mem_llm/dynamic_prompt.py +298 -0
- mem_llm/knowledge_loader.py +88 -0
- mem_llm/llm_client.py +225 -0
- mem_llm/llm_client_factory.py +260 -0
- mem_llm/logger.py +129 -0
- mem_llm/mem_agent.py +1611 -0
- mem_llm/memory_db.py +612 -0
- mem_llm/memory_manager.py +321 -0
- mem_llm/memory_tools.py +253 -0
- mem_llm/prompt_security.py +304 -0
- mem_llm/response_metrics.py +221 -0
- mem_llm/retry_handler.py +193 -0
- mem_llm/thread_safe_db.py +301 -0
- mem_llm/tool_system.py +429 -0
- mem_llm/vector_store.py +278 -0
- mem_llm/web_launcher.py +129 -0
- mem_llm/web_ui/README.md +44 -0
- mem_llm/web_ui/__init__.py +7 -0
- mem_llm/web_ui/index.html +641 -0
- mem_llm/web_ui/memory.html +569 -0
- mem_llm/web_ui/metrics.html +75 -0
- mem_llm-2.0.0.dist-info/METADATA +667 -0
- mem_llm-2.0.0.dist-info/RECORD +39 -0
- mem_llm-2.0.0.dist-info/WHEEL +5 -0
- mem_llm-2.0.0.dist-info/entry_points.txt +3 -0
- mem_llm-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base LLM Client Interface
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
Abstract base class for all LLM client implementations.
|
|
6
|
+
Ensures consistent interface across different backends (Ollama, LM Studio)
|
|
7
|
+
|
|
8
|
+
Author: C. Emre Karataş
|
|
9
|
+
Version: 1.3.0
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from typing import List, Dict, Optional, Any, Iterator, Union
|
|
14
|
+
import logging
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseLLMClient(ABC):
|
|
18
|
+
"""
|
|
19
|
+
Abstract base class for LLM clients
|
|
20
|
+
|
|
21
|
+
All LLM backends must implement these methods to ensure
|
|
22
|
+
compatibility with MemAgent and other components.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, model: str = None, **kwargs):
|
|
26
|
+
"""
|
|
27
|
+
Initialize LLM client
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
model: Model name/identifier
|
|
31
|
+
**kwargs: Backend-specific configuration
|
|
32
|
+
"""
|
|
33
|
+
self.model = model
|
|
34
|
+
self.logger = logging.getLogger(self.__class__.__name__)
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def chat(self, messages: List[Dict[str, str]],
|
|
38
|
+
temperature: float = 0.7,
|
|
39
|
+
max_tokens: int = 2000,
|
|
40
|
+
**kwargs) -> str:
|
|
41
|
+
"""
|
|
42
|
+
Send chat request and return response
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
messages: List of messages in format:
|
|
46
|
+
[{"role": "system/user/assistant", "content": "..."}]
|
|
47
|
+
temperature: Sampling temperature (0.0-1.0)
|
|
48
|
+
max_tokens: Maximum tokens in response
|
|
49
|
+
**kwargs: Additional backend-specific parameters
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Model response text
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
ConnectionError: If cannot connect to service
|
|
56
|
+
ValueError: If invalid parameters
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def check_connection(self) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Check if LLM service is available and responding
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if service is available, False otherwise
|
|
67
|
+
"""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def chat_stream(self, messages: List[Dict[str, str]],
|
|
71
|
+
temperature: float = 0.7,
|
|
72
|
+
max_tokens: int = 2000,
|
|
73
|
+
**kwargs) -> Iterator[str]:
|
|
74
|
+
"""
|
|
75
|
+
Send chat request and stream response chunks (optional, not all backends support)
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
messages: List of messages in format:
|
|
79
|
+
[{"role": "system/user/assistant", "content": "..."}]
|
|
80
|
+
temperature: Sampling temperature (0.0-1.0)
|
|
81
|
+
max_tokens: Maximum tokens in response
|
|
82
|
+
**kwargs: Additional backend-specific parameters
|
|
83
|
+
|
|
84
|
+
Yields:
|
|
85
|
+
Response text chunks as they arrive
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
NotImplementedError: If backend doesn't support streaming
|
|
89
|
+
ConnectionError: If cannot connect to service
|
|
90
|
+
ValueError: If invalid parameters
|
|
91
|
+
"""
|
|
92
|
+
# Default implementation: fall back to non-streaming
|
|
93
|
+
response = self.chat(messages, temperature, max_tokens, **kwargs)
|
|
94
|
+
yield response
|
|
95
|
+
|
|
96
|
+
def generate(self, prompt: str,
|
|
97
|
+
system_prompt: Optional[str] = None,
|
|
98
|
+
temperature: float = 0.7,
|
|
99
|
+
max_tokens: int = 500,
|
|
100
|
+
**kwargs) -> str:
|
|
101
|
+
"""
|
|
102
|
+
Generate text from a simple prompt (convenience method)
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
prompt: User prompt
|
|
106
|
+
system_prompt: Optional system prompt
|
|
107
|
+
temperature: Sampling temperature
|
|
108
|
+
max_tokens: Maximum tokens
|
|
109
|
+
**kwargs: Additional parameters
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Generated text
|
|
113
|
+
"""
|
|
114
|
+
# Convert to chat format
|
|
115
|
+
messages = []
|
|
116
|
+
if system_prompt:
|
|
117
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
118
|
+
messages.append({"role": "user", "content": prompt})
|
|
119
|
+
|
|
120
|
+
return self.chat(messages, temperature, max_tokens, **kwargs)
|
|
121
|
+
|
|
122
|
+
def list_models(self) -> List[str]:
|
|
123
|
+
"""
|
|
124
|
+
List available models (optional, not all backends support this)
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
List of model names
|
|
128
|
+
"""
|
|
129
|
+
return [self.model] if self.model else []
|
|
130
|
+
|
|
131
|
+
def _format_messages_to_text(self, messages: List[Dict]) -> str:
|
|
132
|
+
"""
|
|
133
|
+
Helper: Convert message list to text format
|
|
134
|
+
|
|
135
|
+
Useful for backends that don't support chat format natively.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
messages: Message list
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Formatted text prompt
|
|
142
|
+
"""
|
|
143
|
+
result = []
|
|
144
|
+
for msg in messages:
|
|
145
|
+
role = msg.get('role', 'user').upper()
|
|
146
|
+
content = msg.get('content', '').strip()
|
|
147
|
+
if content:
|
|
148
|
+
result.append(f"{role}: {content}")
|
|
149
|
+
return "\n\n".join(result)
|
|
150
|
+
|
|
151
|
+
def _validate_messages(self, messages: List[Dict]) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
Validate message format
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
messages: Messages to validate
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
True if valid
|
|
160
|
+
|
|
161
|
+
Raises:
|
|
162
|
+
ValueError: If invalid format
|
|
163
|
+
"""
|
|
164
|
+
if not isinstance(messages, list):
|
|
165
|
+
raise ValueError("Messages must be a list")
|
|
166
|
+
|
|
167
|
+
if not messages:
|
|
168
|
+
raise ValueError("Messages list cannot be empty")
|
|
169
|
+
|
|
170
|
+
for i, msg in enumerate(messages):
|
|
171
|
+
if not isinstance(msg, dict):
|
|
172
|
+
raise ValueError(f"Message {i} must be a dictionary")
|
|
173
|
+
|
|
174
|
+
if 'role' not in msg:
|
|
175
|
+
raise ValueError(f"Message {i} missing 'role' field")
|
|
176
|
+
|
|
177
|
+
if 'content' not in msg:
|
|
178
|
+
raise ValueError(f"Message {i} missing 'content' field")
|
|
179
|
+
|
|
180
|
+
if msg['role'] not in ['system', 'user', 'assistant']:
|
|
181
|
+
raise ValueError(f"Message {i} has invalid role: {msg['role']}")
|
|
182
|
+
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
def get_info(self) -> Dict[str, Any]:
|
|
186
|
+
"""
|
|
187
|
+
Get client information
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Dictionary with client metadata
|
|
191
|
+
"""
|
|
192
|
+
return {
|
|
193
|
+
'backend': self.__class__.__name__,
|
|
194
|
+
'model': self.model,
|
|
195
|
+
'available': self.check_connection()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
def __repr__(self) -> str:
|
|
199
|
+
"""String representation"""
|
|
200
|
+
return f"{self.__class__.__name__}(model='{self.model}')"
|
|
201
|
+
|
mem_llm/builtin_tools.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Built-in Tools
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
Common tools that are available by default.
|
|
6
|
+
|
|
7
|
+
Author: C. Emre Karataş
|
|
8
|
+
Version: 2.0.0
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import math
|
|
12
|
+
import os
|
|
13
|
+
import json
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from typing import List, Dict, Any
|
|
16
|
+
from .tool_system import tool
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# Math & Calculation Tools
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
@tool(name="calculate", description="Evaluate mathematical expressions", category="math")
|
|
24
|
+
def calculate(expression: str) -> float:
|
|
25
|
+
"""
|
|
26
|
+
Evaluate a mathematical expression safely.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
expression: Mathematical expression (e.g., "2 + 2", "sqrt(16)", "pi * 2", "(25 * 4) + 10")
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Result of the calculation
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
calculate("2 + 2") -> 4.0
|
|
36
|
+
calculate("sqrt(16)") -> 4.0
|
|
37
|
+
calculate("pi * 2") -> 6.283185307179586
|
|
38
|
+
calculate("(25 * 4) + 10") -> 110.0
|
|
39
|
+
"""
|
|
40
|
+
# Safe eval with math functions
|
|
41
|
+
allowed_names = {
|
|
42
|
+
k: v for k, v in math.__dict__.items() if not k.startswith("__")
|
|
43
|
+
}
|
|
44
|
+
allowed_names["abs"] = abs
|
|
45
|
+
allowed_names["round"] = round
|
|
46
|
+
allowed_names["min"] = min
|
|
47
|
+
allowed_names["max"] = max
|
|
48
|
+
allowed_names["sum"] = sum
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# Clean up expression - replace common text with symbols
|
|
52
|
+
clean_expr = expression.strip()
|
|
53
|
+
clean_expr = clean_expr.replace(" divided by ", " / ")
|
|
54
|
+
clean_expr = clean_expr.replace(" times ", " * ")
|
|
55
|
+
clean_expr = clean_expr.replace(" plus ", " + ")
|
|
56
|
+
clean_expr = clean_expr.replace(" minus ", " - ")
|
|
57
|
+
|
|
58
|
+
result = eval(clean_expr, {"__builtins__": {}}, allowed_names)
|
|
59
|
+
return float(result)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
raise ValueError(f"Invalid expression '{expression}': {str(e)}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ============================================================================
|
|
65
|
+
# Text Processing Tools
|
|
66
|
+
# ============================================================================
|
|
67
|
+
|
|
68
|
+
@tool(name="count_words", description="Count words in text", category="text")
|
|
69
|
+
def count_words(text: str) -> int:
|
|
70
|
+
"""
|
|
71
|
+
Count the number of words in a text.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
text: Text to count words in
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Number of words
|
|
78
|
+
"""
|
|
79
|
+
return len(text.split())
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@tool(name="reverse_text", description="Reverse a text string", category="text")
|
|
83
|
+
def reverse_text(text: str) -> str:
|
|
84
|
+
"""
|
|
85
|
+
Reverse the order of characters in text.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
text: Text to reverse
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Reversed text
|
|
92
|
+
"""
|
|
93
|
+
return text[::-1]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@tool(name="to_uppercase", description="Convert text to uppercase", category="text")
|
|
97
|
+
def to_uppercase(text: str) -> str:
|
|
98
|
+
"""
|
|
99
|
+
Convert text to uppercase.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
text: Text to convert
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Uppercase text
|
|
106
|
+
"""
|
|
107
|
+
return text.upper()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@tool(name="to_lowercase", description="Convert text to lowercase", category="text")
|
|
111
|
+
def to_lowercase(text: str) -> str:
|
|
112
|
+
"""
|
|
113
|
+
Convert text to lowercase.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
text: Text to convert
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Lowercase text
|
|
120
|
+
"""
|
|
121
|
+
return text.lower()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ============================================================================
|
|
125
|
+
# File System Tools
|
|
126
|
+
# ============================================================================
|
|
127
|
+
|
|
128
|
+
@tool(name="read_file", description="Read contents of a text file", category="file")
|
|
129
|
+
def read_file(filepath: str) -> str:
|
|
130
|
+
"""
|
|
131
|
+
Read and return the contents of a text file.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
filepath: Path to the file to read
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
File contents as string
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
141
|
+
return f.read()
|
|
142
|
+
except Exception as e:
|
|
143
|
+
raise ValueError(f"Error reading file: {e}")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@tool(name="write_file", description="Write text to a file", category="file")
|
|
147
|
+
def write_file(filepath: str, content: str) -> str:
|
|
148
|
+
"""
|
|
149
|
+
Write text content to a file.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
filepath: Path to the file to write
|
|
153
|
+
content: Content to write to the file
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Success message
|
|
157
|
+
"""
|
|
158
|
+
try:
|
|
159
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
160
|
+
f.write(content)
|
|
161
|
+
return f"Successfully wrote {len(content)} characters to {filepath}"
|
|
162
|
+
except Exception as e:
|
|
163
|
+
raise ValueError(f"Error writing file: {e}")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@tool(name="list_files", description="List files in a directory", category="file")
|
|
167
|
+
def list_files(directory: str) -> List[str]:
|
|
168
|
+
"""
|
|
169
|
+
List all files in a directory.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
directory: Path to the directory
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
List of filenames
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
return os.listdir(directory)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
raise ValueError(f"Error listing directory: {e}")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# ============================================================================
|
|
184
|
+
# Utility Tools
|
|
185
|
+
# ============================================================================
|
|
186
|
+
|
|
187
|
+
@tool(name="get_current_time", description="Get current date and time", category="utility")
|
|
188
|
+
def get_current_time() -> str:
|
|
189
|
+
"""
|
|
190
|
+
Get the current date and time.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Current datetime as string
|
|
194
|
+
"""
|
|
195
|
+
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@tool(name="create_json", description="Create JSON from text", category="utility")
|
|
199
|
+
def create_json(data: str) -> str:
|
|
200
|
+
"""
|
|
201
|
+
Parse and format text as JSON.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
data: Text to convert to JSON
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Formatted JSON string
|
|
208
|
+
"""
|
|
209
|
+
try:
|
|
210
|
+
parsed = json.loads(data)
|
|
211
|
+
return json.dumps(parsed, indent=2)
|
|
212
|
+
except:
|
|
213
|
+
# Try to create simple key-value JSON
|
|
214
|
+
lines = data.strip().split('\n')
|
|
215
|
+
result = {}
|
|
216
|
+
for line in lines:
|
|
217
|
+
if ':' in line:
|
|
218
|
+
key, value = line.split(':', 1)
|
|
219
|
+
result[key.strip()] = value.strip()
|
|
220
|
+
return json.dumps(result, indent=2)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# ============================================================================
|
|
224
|
+
# Memory & Context Tools (NEW in v2.0.0)
|
|
225
|
+
# ============================================================================
|
|
226
|
+
|
|
227
|
+
@tool(name="search_memory", description="Search through conversation history", category="memory")
|
|
228
|
+
def search_memory(keyword: str) -> str:
|
|
229
|
+
"""
|
|
230
|
+
Search through conversation history for a keyword.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
keyword: The keyword to search for in conversation history
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Search results or error message
|
|
237
|
+
|
|
238
|
+
Examples:
|
|
239
|
+
search_memory("weather") -> "Found 2 conversations about weather..."
|
|
240
|
+
|
|
241
|
+
Note:
|
|
242
|
+
This tool requires MemAgent context. Will return instructions if called standalone.
|
|
243
|
+
"""
|
|
244
|
+
# This is a placeholder - actual implementation happens in MemAgent._execute_tool_calls
|
|
245
|
+
return f"MEMORY_SEARCH:{keyword}"
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@tool(name="get_user_info", description="Get current user profile information", category="memory")
|
|
249
|
+
def get_user_info() -> str:
|
|
250
|
+
"""
|
|
251
|
+
Get information about the current user.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
User profile information
|
|
255
|
+
|
|
256
|
+
Examples:
|
|
257
|
+
get_user_info() -> "Current user: john_doe (active since 2025-01-15)"
|
|
258
|
+
|
|
259
|
+
Note:
|
|
260
|
+
This tool requires MemAgent context. Will return instructions if called standalone.
|
|
261
|
+
"""
|
|
262
|
+
# This is a placeholder - actual implementation happens in MemAgent._execute_tool_calls
|
|
263
|
+
return "MEMORY_USER_INFO"
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@tool(name="list_conversations", description="List recent conversations", category="memory")
|
|
267
|
+
def list_conversations(limit: int = 5) -> str:
|
|
268
|
+
"""
|
|
269
|
+
List recent conversation sessions.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
limit: Maximum number of conversations to list (default: 5)
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
List of recent conversations
|
|
276
|
+
|
|
277
|
+
Examples:
|
|
278
|
+
list_conversations(3) -> "Last 3 conversations: ..."
|
|
279
|
+
|
|
280
|
+
Note:
|
|
281
|
+
This tool requires MemAgent context. Will return instructions if called standalone.
|
|
282
|
+
"""
|
|
283
|
+
# This is a placeholder - actual implementation happens in MemAgent._execute_tool_calls
|
|
284
|
+
return f"MEMORY_LIST_CONVERSATIONS:{limit}"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# ============================================================================
|
|
288
|
+
# Export all tools
|
|
289
|
+
# ============================================================================
|
|
290
|
+
|
|
291
|
+
BUILTIN_TOOLS = [
|
|
292
|
+
# Math
|
|
293
|
+
calculate,
|
|
294
|
+
# Text
|
|
295
|
+
count_words,
|
|
296
|
+
reverse_text,
|
|
297
|
+
to_uppercase,
|
|
298
|
+
to_lowercase,
|
|
299
|
+
# File
|
|
300
|
+
read_file,
|
|
301
|
+
write_file,
|
|
302
|
+
list_files,
|
|
303
|
+
# Utility
|
|
304
|
+
get_current_time,
|
|
305
|
+
create_json,
|
|
306
|
+
# Memory (v2.0.0+)
|
|
307
|
+
search_memory,
|
|
308
|
+
get_user_info,
|
|
309
|
+
list_conversations,
|
|
310
|
+
]
|
|
311
|
+
|