mcp-sqlite-memory-bank 1.5.1__py3-none-any.whl → 1.6.2__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.
@@ -7,12 +7,11 @@ semantic search, embedding management, and intelligent search capabilities.
7
7
 
8
8
  import logging
9
9
  import traceback
10
- from typing import Any, Dict, List, Optional, cast
10
+ from typing import List, Optional, cast
11
11
 
12
12
  from ..database import get_database
13
- from ..types import MemoryBankError, DatabaseError, ToolResponse
13
+ from ..types import ToolResponse
14
14
  from ..utils import catch_errors
15
- from ..semantic import is_semantic_search_available
16
15
 
17
16
 
18
17
  @catch_errors
@@ -23,6 +22,7 @@ def search_content(
23
22
  ) -> ToolResponse:
24
23
  """Perform full-text search across table content using natural language queries."""
25
24
  from .. import server
25
+
26
26
  return cast(ToolResponse, get_database(server.DB_PATH).search_content(query, tables, limit))
27
27
 
28
28
 
@@ -33,7 +33,11 @@ def explore_tables(
33
33
  ) -> ToolResponse:
34
34
  """Explore and discover table structures and content for better searchability."""
35
35
  from .. import server
36
- return cast(ToolResponse, get_database(server.DB_PATH).explore_tables(pattern, include_row_counts))
36
+
37
+ return cast(
38
+ ToolResponse,
39
+ get_database(server.DB_PATH).explore_tables(pattern, include_row_counts),
40
+ )
37
41
 
38
42
 
39
43
  @catch_errors
@@ -45,9 +49,13 @@ def add_embeddings(
45
49
  ) -> ToolResponse:
46
50
  """Generate and store vector embeddings for semantic search on table content."""
47
51
  from .. import server
48
- return cast(ToolResponse, get_database(server.DB_PATH).generate_embeddings(
49
- table_name, text_columns, embedding_column, model_name
50
- ))
52
+
53
+ return cast(
54
+ ToolResponse,
55
+ get_database(server.DB_PATH).generate_embeddings(
56
+ table_name, text_columns, embedding_column, model_name
57
+ ),
58
+ )
51
59
 
52
60
 
53
61
  @catch_errors
@@ -60,9 +68,13 @@ def semantic_search(
60
68
  ) -> ToolResponse:
61
69
  """Find content using natural language semantic similarity rather than exact keyword matching."""
62
70
  from .. import server
63
- return cast(ToolResponse, get_database(server.DB_PATH).semantic_search(
64
- query, tables, "embedding", None, similarity_threshold, limit, model_name
65
- ))
71
+
72
+ return cast(
73
+ ToolResponse,
74
+ get_database(server.DB_PATH).semantic_search(
75
+ query, tables, "embedding", None, similarity_threshold, limit, model_name
76
+ ),
77
+ )
66
78
 
67
79
 
68
80
  @catch_errors
@@ -75,9 +87,13 @@ def find_related(
75
87
  ) -> ToolResponse:
76
88
  """Find content related to a specific row by semantic similarity."""
77
89
  from .. import server
78
- return cast(ToolResponse, get_database(server.DB_PATH).find_related_content(
79
- table_name, row_id, "embedding", similarity_threshold, limit, model_name
80
- ))
90
+
91
+ return cast(
92
+ ToolResponse,
93
+ get_database(server.DB_PATH).find_related_content(
94
+ table_name, row_id, "embedding", similarity_threshold, limit, model_name
95
+ ),
96
+ )
81
97
 
82
98
 
83
99
  @catch_errors
@@ -91,9 +107,20 @@ def smart_search(
91
107
  ) -> ToolResponse:
92
108
  """Intelligent hybrid search combining semantic understanding with keyword matching."""
93
109
  from .. import server
94
- return cast(ToolResponse, get_database(server.DB_PATH).hybrid_search(
95
- query, tables, None, "embedding", semantic_weight, text_weight, limit, model_name
96
- ))
110
+
111
+ return cast(
112
+ ToolResponse,
113
+ get_database(server.DB_PATH).hybrid_search(
114
+ query,
115
+ tables,
116
+ None,
117
+ "embedding",
118
+ semantic_weight,
119
+ text_weight,
120
+ limit,
121
+ model_name,
122
+ ),
123
+ )
97
124
 
98
125
 
99
126
  @catch_errors
@@ -103,7 +130,11 @@ def embedding_stats(
103
130
  ) -> ToolResponse:
104
131
  """Get statistics about semantic search readiness for a table."""
105
132
  from .. import server
106
- return cast(ToolResponse, get_database(server.DB_PATH).get_embedding_stats(table_name, embedding_column))
133
+
134
+ return cast(
135
+ ToolResponse,
136
+ get_database(server.DB_PATH).get_embedding_stats(table_name, embedding_column),
137
+ )
107
138
 
108
139
 
109
140
  @catch_errors
@@ -153,9 +184,10 @@ def auto_semantic_search(
153
184
  """
154
185
  try:
155
186
  from .. import server
187
+
156
188
  db = get_database(server.DB_PATH)
157
189
  auto_embedded_tables: List[str] = []
158
-
190
+
159
191
  # Get tables to search
160
192
  search_tables: List[str]
161
193
  if tables:
@@ -169,21 +201,25 @@ def auto_semantic_search(
169
201
  search_tables = all_tables
170
202
  else:
171
203
  search_tables = []
172
-
204
+
173
205
  # Auto-embed text columns in tables that don't have embeddings
174
206
  for table_name in search_tables:
175
207
  try:
176
208
  # Check if table has embeddings
177
209
  stats_result = db.get_embedding_stats(table_name, "embedding")
178
210
  coverage_percent = stats_result.get("coverage_percent", 0)
179
- if stats_result.get("success") and isinstance(coverage_percent, (int, float)) and coverage_percent > 0:
211
+ if (
212
+ stats_result.get("success")
213
+ and isinstance(coverage_percent, (int, float))
214
+ and coverage_percent > 0
215
+ ):
180
216
  continue # Table already has embeddings
181
-
217
+
182
218
  # Get table schema to find text columns
183
219
  schema_result = db.describe_table(table_name)
184
220
  if not schema_result.get("success"):
185
221
  continue
186
-
222
+
187
223
  # Find text columns
188
224
  text_columns = []
189
225
  columns = schema_result.get("columns", [])
@@ -191,37 +227,50 @@ def auto_semantic_search(
191
227
  for col in columns:
192
228
  if isinstance(col, dict) and "TEXT" in col.get("type", "").upper():
193
229
  text_columns.append(col["name"])
194
-
230
+
195
231
  # Auto-embed text columns
196
232
  if text_columns:
197
- embed_result = db.generate_embeddings(table_name, text_columns, "embedding", model_name)
233
+ embed_result = db.generate_embeddings(
234
+ table_name, text_columns, "embedding", model_name
235
+ )
198
236
  if embed_result.get("success"):
199
237
  auto_embedded_tables.append(table_name)
200
-
238
+
201
239
  except Exception:
202
240
  # If auto-embedding fails, continue without it
203
241
  continue
204
-
242
+
205
243
  # Perform semantic search
206
244
  search_result = db.semantic_search(
207
- query, search_tables, "embedding", None, similarity_threshold, limit, model_name
245
+ query,
246
+ search_tables,
247
+ "embedding",
248
+ None,
249
+ similarity_threshold,
250
+ limit,
251
+ model_name,
208
252
  )
209
-
253
+
210
254
  # Add auto-embedding info to result
211
255
  if isinstance(search_result, dict):
212
256
  search_result["auto_embedded_tables"] = auto_embedded_tables
213
257
  if auto_embedded_tables:
214
- search_result["auto_embedding_note"] = f"Automatically generated embeddings for {len(auto_embedded_tables)} table(s)"
215
-
258
+ search_result["auto_embedding_note"] = (
259
+ f"Automatically generated embeddings for {len(auto_embedded_tables)} table(s)"
260
+ )
261
+
216
262
  return cast(ToolResponse, search_result)
217
-
263
+
218
264
  except Exception as e:
219
- return cast(ToolResponse, {
220
- "success": False,
221
- "error": f"Auto semantic search failed: {str(e)}",
222
- "category": "SEMANTIC_SEARCH_ERROR",
223
- "details": {"query": query, "tables": tables}
224
- })
265
+ return cast(
266
+ ToolResponse,
267
+ {
268
+ "success": False,
269
+ "error": f"Auto semantic search failed: {str(e)}",
270
+ "category": "SEMANTIC_SEARCH_ERROR",
271
+ "details": {"query": query, "tables": tables},
272
+ },
273
+ )
225
274
 
226
275
 
227
276
  @catch_errors
@@ -269,9 +318,10 @@ def auto_smart_search(
269
318
  """
270
319
  try:
271
320
  from .. import server
321
+
272
322
  db = get_database(server.DB_PATH)
273
323
  auto_embedded_tables: List[str] = []
274
-
324
+
275
325
  # Get tables to search
276
326
  search_tables: List[str]
277
327
  if tables:
@@ -285,21 +335,25 @@ def auto_smart_search(
285
335
  search_tables = all_tables
286
336
  else:
287
337
  search_tables = []
288
-
338
+
289
339
  # Auto-embed text columns in tables that don't have embeddings
290
340
  for table_name in search_tables:
291
341
  try:
292
342
  # Check if table has embeddings
293
343
  stats_result = db.get_embedding_stats(table_name, "embedding")
294
344
  coverage_percent = stats_result.get("coverage_percent", 0)
295
- if stats_result.get("success") and isinstance(coverage_percent, (int, float)) and coverage_percent > 0:
345
+ if (
346
+ stats_result.get("success")
347
+ and isinstance(coverage_percent, (int, float))
348
+ and coverage_percent > 0
349
+ ):
296
350
  continue # Table already has embeddings
297
-
351
+
298
352
  # Get table schema to find text columns
299
353
  schema_result = db.describe_table(table_name)
300
354
  if not schema_result.get("success"):
301
355
  continue
302
-
356
+
303
357
  # Find text columns
304
358
  text_columns = []
305
359
  columns = schema_result.get("columns", [])
@@ -307,27 +361,38 @@ def auto_smart_search(
307
361
  for col in columns:
308
362
  if isinstance(col, dict) and "TEXT" in col.get("type", "").upper():
309
363
  text_columns.append(col["name"])
310
-
364
+
311
365
  # Auto-embed text columns
312
366
  if text_columns:
313
- embed_result = db.generate_embeddings(table_name, text_columns, "embedding", model_name)
367
+ embed_result = db.generate_embeddings(
368
+ table_name, text_columns, "embedding", model_name
369
+ )
314
370
  if embed_result.get("success"):
315
371
  auto_embedded_tables.append(table_name)
316
-
372
+
317
373
  except Exception:
318
374
  # If auto-embedding fails, continue without it
319
375
  continue
320
-
376
+
321
377
  # Now perform hybrid search using the same pattern as smart_search
322
378
  try:
323
379
  hybrid_result = get_database(server.DB_PATH).hybrid_search(
324
- query, search_tables, None, "embedding", semantic_weight, text_weight, limit, model_name
380
+ query,
381
+ search_tables,
382
+ None,
383
+ "embedding",
384
+ semantic_weight,
385
+ text_weight,
386
+ limit,
387
+ model_name,
325
388
  )
326
389
  except Exception as search_error:
327
390
  # If hybrid search fails, fall back to regular content search
328
391
  logging.warning(f"Hybrid search failed, falling back to content search: {search_error}")
329
392
  try:
330
- fallback_result = get_database(server.DB_PATH).search_content(query, search_tables, limit)
393
+ fallback_result = get_database(server.DB_PATH).search_content(
394
+ query, search_tables, limit
395
+ )
331
396
  if fallback_result.get("success"):
332
397
  # Create a new dictionary to avoid type issues
333
398
  enhanced_fallback = dict(fallback_result)
@@ -336,21 +401,27 @@ def auto_smart_search(
336
401
  enhanced_fallback["fallback_reason"] = str(search_error)
337
402
  return cast(ToolResponse, enhanced_fallback)
338
403
  except Exception as fallback_error:
339
- return cast(ToolResponse, {
404
+ return cast(
405
+ ToolResponse,
406
+ {
407
+ "success": False,
408
+ "error": f"Both hybrid and fallback search failed. Hybrid: {search_error}, Fallback: {fallback_error}",
409
+ "category": "HYBRID_SEARCH_ERROR",
410
+ "details": {"query": query, "tables": tables},
411
+ },
412
+ )
413
+
414
+ # If we get here, both searches failed
415
+ return cast(
416
+ ToolResponse,
417
+ {
340
418
  "success": False,
341
- "error": f"Both hybrid and fallback search failed. Hybrid: {search_error}, Fallback: {fallback_error}",
419
+ "error": f"Hybrid search failed: {search_error}",
342
420
  "category": "HYBRID_SEARCH_ERROR",
343
- "details": {"query": query, "tables": tables}
344
- })
345
-
346
- # If we get here, both searches failed
347
- return cast(ToolResponse, {
348
- "success": False,
349
- "error": f"Hybrid search failed: {search_error}",
350
- "category": "HYBRID_SEARCH_ERROR",
351
- "details": {"query": query, "tables": tables}
352
- })
353
-
421
+ "details": {"query": query, "tables": tables},
422
+ },
423
+ )
424
+
354
425
  # Add auto-embedding info to result
355
426
  if isinstance(hybrid_result, dict) and hybrid_result.get("success"):
356
427
  # Convert to mutable dict to add extra fields
@@ -358,23 +429,28 @@ def auto_smart_search(
358
429
  final_result["search_type"] = "auto_hybrid"
359
430
  final_result["auto_embedded_tables"] = auto_embedded_tables
360
431
  if auto_embedded_tables:
361
- final_result["auto_embedding_note"] = f"Automatically generated embeddings for {len(auto_embedded_tables)} table(s)"
432
+ final_result["auto_embedding_note"] = (
433
+ f"Automatically generated embeddings for {len(auto_embedded_tables)} table(s)"
434
+ )
362
435
  return cast(ToolResponse, final_result)
363
436
  else:
364
437
  return cast(ToolResponse, hybrid_result)
365
-
438
+
366
439
  except Exception as e:
367
440
  # Add detailed error information for debugging
368
441
  error_details = {
369
- "query": query,
442
+ "query": query,
370
443
  "tables": tables,
371
444
  "error_type": type(e).__name__,
372
445
  "error_str": str(e),
373
- "traceback": traceback.format_exc()
446
+ "traceback": traceback.format_exc(),
374
447
  }
375
- return cast(ToolResponse, {
376
- "success": False,
377
- "error": f"Auto smart search failed: {str(e)}",
378
- "category": "HYBRID_SEARCH_ERROR",
379
- "details": error_details
380
- })
448
+ return cast(
449
+ ToolResponse,
450
+ {
451
+ "success": False,
452
+ "error": f"Auto smart search failed: {str(e)}",
453
+ "category": "HYBRID_SEARCH_ERROR",
454
+ "details": error_details,
455
+ },
456
+ )
@@ -42,7 +42,12 @@ class MemoryBankError(Exception):
42
42
 
43
43
  def to_dict(self) -> Dict[str, Any]:
44
44
  """Convert error to a dict format suitable for FastMCP responses."""
45
- return {"success": False, "error": self.message, "category": self.category.name, "details": self.details or {}}
45
+ return {
46
+ "success": False,
47
+ "error": self.message,
48
+ "category": self.category.name,
49
+ "details": self.details or {},
50
+ }
46
51
 
47
52
 
48
53
  class ValidationError(MemoryBankError):