memorygraphMCP 0.11.7__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.
Files changed (65) hide show
  1. memorygraph/__init__.py +50 -0
  2. memorygraph/__main__.py +12 -0
  3. memorygraph/advanced_tools.py +509 -0
  4. memorygraph/analytics/__init__.py +46 -0
  5. memorygraph/analytics/advanced_queries.py +727 -0
  6. memorygraph/backends/__init__.py +21 -0
  7. memorygraph/backends/base.py +179 -0
  8. memorygraph/backends/cloud.py +75 -0
  9. memorygraph/backends/cloud_backend.py +858 -0
  10. memorygraph/backends/factory.py +577 -0
  11. memorygraph/backends/falkordb_backend.py +749 -0
  12. memorygraph/backends/falkordblite_backend.py +746 -0
  13. memorygraph/backends/ladybugdb_backend.py +242 -0
  14. memorygraph/backends/memgraph_backend.py +327 -0
  15. memorygraph/backends/neo4j_backend.py +298 -0
  16. memorygraph/backends/sqlite_fallback.py +463 -0
  17. memorygraph/backends/turso.py +448 -0
  18. memorygraph/cli.py +743 -0
  19. memorygraph/cloud_database.py +297 -0
  20. memorygraph/config.py +295 -0
  21. memorygraph/database.py +933 -0
  22. memorygraph/graph_analytics.py +631 -0
  23. memorygraph/integration/__init__.py +69 -0
  24. memorygraph/integration/context_capture.py +426 -0
  25. memorygraph/integration/project_analysis.py +583 -0
  26. memorygraph/integration/workflow_tracking.py +492 -0
  27. memorygraph/intelligence/__init__.py +59 -0
  28. memorygraph/intelligence/context_retrieval.py +447 -0
  29. memorygraph/intelligence/entity_extraction.py +386 -0
  30. memorygraph/intelligence/pattern_recognition.py +420 -0
  31. memorygraph/intelligence/temporal.py +374 -0
  32. memorygraph/migration/__init__.py +27 -0
  33. memorygraph/migration/manager.py +579 -0
  34. memorygraph/migration/models.py +142 -0
  35. memorygraph/migration/scripts/__init__.py +17 -0
  36. memorygraph/migration/scripts/bitemporal_migration.py +595 -0
  37. memorygraph/migration/scripts/multitenancy_migration.py +452 -0
  38. memorygraph/migration_tools_module.py +146 -0
  39. memorygraph/models.py +684 -0
  40. memorygraph/proactive/__init__.py +46 -0
  41. memorygraph/proactive/outcome_learning.py +444 -0
  42. memorygraph/proactive/predictive.py +410 -0
  43. memorygraph/proactive/session_briefing.py +399 -0
  44. memorygraph/relationships.py +668 -0
  45. memorygraph/server.py +883 -0
  46. memorygraph/sqlite_database.py +1876 -0
  47. memorygraph/tools/__init__.py +59 -0
  48. memorygraph/tools/activity_tools.py +262 -0
  49. memorygraph/tools/memory_tools.py +315 -0
  50. memorygraph/tools/migration_tools.py +181 -0
  51. memorygraph/tools/relationship_tools.py +147 -0
  52. memorygraph/tools/search_tools.py +406 -0
  53. memorygraph/tools/temporal_tools.py +339 -0
  54. memorygraph/utils/__init__.py +10 -0
  55. memorygraph/utils/context_extractor.py +429 -0
  56. memorygraph/utils/error_handling.py +151 -0
  57. memorygraph/utils/export_import.py +425 -0
  58. memorygraph/utils/graph_algorithms.py +200 -0
  59. memorygraph/utils/pagination.py +149 -0
  60. memorygraph/utils/project_detection.py +133 -0
  61. memorygraphmcp-0.11.7.dist-info/METADATA +970 -0
  62. memorygraphmcp-0.11.7.dist-info/RECORD +65 -0
  63. memorygraphmcp-0.11.7.dist-info/WHEEL +4 -0
  64. memorygraphmcp-0.11.7.dist-info/entry_points.txt +2 -0
  65. memorygraphmcp-0.11.7.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,410 @@
1
+ """
2
+ Predictive Suggestions for Claude Code Memory Server.
3
+
4
+ Provides proactive suggestions based on current context:
5
+ - Predict relevant memories and patterns
6
+ - Warn about potential issues
7
+ - Suggest related context
8
+
9
+ Phase 7 Implementation - Predictive Suggestions
10
+ """
11
+
12
+ from datetime import datetime
13
+ from typing import List, Optional, Dict, Any
14
+ import logging
15
+
16
+ from pydantic import BaseModel, Field
17
+
18
+ from ..backends.base import GraphBackend
19
+ from ..models import Memory, MemoryType, RelationshipType
20
+ from ..intelligence.entity_extraction import extract_entities, Entity
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class Suggestion(BaseModel):
26
+ """
27
+ Proactive suggestion for relevant memory or pattern.
28
+
29
+ Suggestions are ranked by relevance score.
30
+ """
31
+
32
+ suggestion_id: str
33
+ suggestion_type: str # "memory", "pattern", "solution"
34
+ title: str
35
+ description: str
36
+ relevance_score: float = Field(..., ge=0.0, le=1.0)
37
+ reason: str # Why this is suggested
38
+ memory_id: str
39
+ tags: List[str] = Field(default_factory=list)
40
+ effectiveness: Optional[float] = None
41
+
42
+
43
+ class Warning(BaseModel):
44
+ """
45
+ Warning about potential issues.
46
+
47
+ Based on known problems and patterns.
48
+ """
49
+
50
+ warning_id: str
51
+ severity: str # "low", "medium", "high"
52
+ title: str
53
+ description: str
54
+ evidence: List[str] = Field(default_factory=list) # Memory IDs that support this warning
55
+ mitigation: Optional[str] = None # Suggested mitigation
56
+ related_problem_id: Optional[str] = None
57
+
58
+
59
+ async def predict_needs(
60
+ backend: GraphBackend,
61
+ current_context: str,
62
+ max_suggestions: int = 5,
63
+ min_relevance: float = 0.3,
64
+ ) -> List[Suggestion]:
65
+ """
66
+ Predict relevant memories and patterns based on current context.
67
+
68
+ Uses entity extraction and relationship matching to find relevant information.
69
+
70
+ Args:
71
+ backend: Database backend
72
+ current_context: Current work context (e.g., file content, task description)
73
+ max_suggestions: Maximum number of suggestions to return
74
+ min_relevance: Minimum relevance score threshold
75
+
76
+ Returns:
77
+ List of suggestions ranked by relevance
78
+
79
+ Example:
80
+ >>> suggestions = await predict_needs(backend, "Working on authentication with JWT")
81
+ >>> for s in suggestions:
82
+ ... print(f"{s.title}: {s.reason}")
83
+ """
84
+ logger.info("Predicting needs from current context")
85
+
86
+ # Extract entities from current context
87
+ entities = extract_entities(current_context)
88
+ logger.debug(f"Extracted {len(entities)} entities from context")
89
+
90
+ if not entities:
91
+ logger.warning("No entities extracted from context")
92
+ return []
93
+
94
+ suggestions = []
95
+
96
+ # Find memories that mention the same entities
97
+ for entity in entities[:10]: # Limit to top 10 entities to avoid overwhelming queries
98
+ entity_query = """
99
+ MATCH (m:Memory)-[:MENTIONS]->(e:Entity {text: $entity_text})
100
+ WHERE m.type IN ['solution', 'code_pattern', 'fix']
101
+ OPTIONAL MATCH (m)-[r:EFFECTIVE_FOR|SOLVES|ADDRESSES]->()
102
+ RETURN m.id as id, m.type as type, m.title as title,
103
+ m.content as content, m.tags as tags,
104
+ m.effectiveness as effectiveness,
105
+ m.usage_count as usage_count,
106
+ count(r) as effectiveness_links
107
+ ORDER BY m.effectiveness DESC, m.usage_count DESC
108
+ LIMIT 3
109
+ """
110
+
111
+ try:
112
+ results = await backend.execute_query(
113
+ entity_query,
114
+ {"entity_text": entity.text}
115
+ )
116
+
117
+ for record in results:
118
+ # Calculate relevance score based on:
119
+ # - Entity match (base score)
120
+ # - Effectiveness
121
+ # - Usage count
122
+ # - Number of effectiveness relationships
123
+ base_score = entity.confidence
124
+ effectiveness_bonus = record.get("effectiveness", 0.5) * 0.3
125
+ usage_bonus = min(record.get("usage_count", 0) / 10.0, 0.2)
126
+ links_bonus = min(record.get("effectiveness_links", 0) / 5.0, 0.2)
127
+
128
+ relevance = base_score + effectiveness_bonus + usage_bonus + links_bonus
129
+ relevance = min(relevance, 1.0) # Cap at 1.0
130
+
131
+ if relevance >= min_relevance:
132
+ suggestions.append(Suggestion(
133
+ suggestion_id=record["id"],
134
+ suggestion_type=record["type"],
135
+ title=record["title"],
136
+ description=record["content"][:200],
137
+ relevance_score=relevance,
138
+ reason=f"Related to {entity.entity_type.value}: {entity.text}",
139
+ memory_id=record["id"],
140
+ tags=record.get("tags", []),
141
+ effectiveness=record.get("effectiveness"),
142
+ ))
143
+
144
+ except Exception as e:
145
+ logger.error(f"Error querying memories for entity {entity.text}: {e}")
146
+
147
+ # Find patterns that match the context
148
+ pattern_keywords = [(e.text.lower(), e.confidence) for e in entities if e.entity_type.value in ["technology", "concept"]]
149
+
150
+ if pattern_keywords:
151
+ pattern_query = """
152
+ MATCH (p:Memory {type: 'code_pattern'})
153
+ WHERE any(keyword IN $keywords WHERE p.content CONTAINS keyword)
154
+ RETURN p.id as id, p.title as title, p.content as content,
155
+ p.tags as tags, p.effectiveness as effectiveness,
156
+ p.usage_count as usage_count
157
+ ORDER BY p.effectiveness DESC
158
+ LIMIT 3
159
+ """
160
+
161
+ try:
162
+ # Get average confidence of matching entities
163
+ avg_confidence = sum(conf for _, conf in pattern_keywords) / len(pattern_keywords)
164
+ keywords_only = [kw for kw, _ in pattern_keywords]
165
+
166
+ results = await backend.execute_query(
167
+ pattern_query,
168
+ {"keywords": keywords_only}
169
+ )
170
+
171
+ for record in results:
172
+ # Calculate relevance for patterns based on entity confidence
173
+ effectiveness = record.get("effectiveness", 0.5)
174
+ usage_count = record.get("usage_count", 0)
175
+
176
+ # Use entity confidence as base, similar to entity-based suggestions
177
+ relevance = avg_confidence + (effectiveness * 0.3) + min(usage_count / 10.0, 0.1)
178
+ relevance = min(relevance, 1.0) # Cap at 1.0
179
+
180
+ if relevance >= min_relevance:
181
+ suggestions.append(Suggestion(
182
+ suggestion_id=record["id"],
183
+ suggestion_type="pattern",
184
+ title=record["title"],
185
+ description=record["content"][:200],
186
+ relevance_score=relevance,
187
+ reason="Matching pattern for current context",
188
+ memory_id=record["id"],
189
+ tags=record.get("tags", []),
190
+ effectiveness=record.get("effectiveness"),
191
+ ))
192
+
193
+ except Exception as e:
194
+ logger.error(f"Error querying patterns: {e}")
195
+
196
+ # Deduplicate and sort by relevance
197
+ seen_ids = set()
198
+ unique_suggestions = []
199
+ for suggestion in sorted(suggestions, key=lambda s: s.relevance_score, reverse=True):
200
+ if suggestion.memory_id not in seen_ids:
201
+ unique_suggestions.append(suggestion)
202
+ seen_ids.add(suggestion.memory_id)
203
+
204
+ logger.info(f"Generated {len(unique_suggestions)} suggestions")
205
+ return unique_suggestions[:max_suggestions]
206
+
207
+
208
+ async def warn_potential_issues(
209
+ backend: GraphBackend,
210
+ current_context: str,
211
+ severity_threshold: str = "medium",
212
+ ) -> List[Warning]:
213
+ """
214
+ Warn about potential issues based on current context.
215
+
216
+ Matches against known problem patterns and deprecated approaches.
217
+
218
+ Args:
219
+ backend: Database backend
220
+ current_context: Current work context
221
+ severity_threshold: Minimum severity to report ("low", "medium", "high")
222
+
223
+ Returns:
224
+ List of warnings with evidence
225
+
226
+ Example:
227
+ >>> warnings = await warn_potential_issues(backend, "Using JWT authentication")
228
+ >>> for w in warnings:
229
+ ... print(f"{w.severity.upper()}: {w.title}")
230
+ """
231
+ logger.info("Checking for potential issues in current context")
232
+
233
+ # Extract entities from context
234
+ entities = extract_entities(current_context)
235
+
236
+ warnings = []
237
+
238
+ # Check for deprecated approaches
239
+ entity_texts = [e.text for e in entities]
240
+
241
+ if entity_texts:
242
+ deprecated_query = """
243
+ MATCH (old:Memory)-[r:DEPRECATED_BY]->(new:Memory)
244
+ MATCH (old)-[:MENTIONS]->(e:Entity)
245
+ WHERE e.text IN $entity_texts
246
+ RETURN old.id as old_id, old.title as old_title,
247
+ old.content as old_content,
248
+ new.id as new_id, new.title as new_title,
249
+ r.context as reason,
250
+ collect(e.text) as entities
251
+ """
252
+
253
+ try:
254
+ results = await backend.execute_query(
255
+ deprecated_query,
256
+ {"entity_texts": entity_texts}
257
+ )
258
+
259
+ for record in results:
260
+ warnings.append(Warning(
261
+ warning_id=record["old_id"],
262
+ severity="high",
263
+ title=f"Deprecated: {record['old_title']}",
264
+ description=record.get("reason", "This approach is deprecated"),
265
+ evidence=[record["old_id"]],
266
+ mitigation=f"Consider using: {record['new_title']}",
267
+ related_problem_id=record["old_id"],
268
+ ))
269
+
270
+ except Exception as e:
271
+ logger.error(f"Error checking for deprecated approaches: {e}")
272
+
273
+ # Check for known problem patterns
274
+ problem_keywords = [e.text.lower() for e in entities]
275
+
276
+ if problem_keywords:
277
+ problem_query = """
278
+ MATCH (p:Memory {type: 'problem'})
279
+ WHERE any(keyword IN $keywords WHERE p.content CONTAINS keyword)
280
+ OPTIONAL MATCH (p)-[:SOLVES|ADDRESSES]-(s:Memory {type: 'solution'})
281
+ RETURN p.id as problem_id, p.title as problem_title,
282
+ p.content as problem_content, p.tags as tags,
283
+ collect(s.id) as solution_ids,
284
+ collect(s.title) as solution_titles
285
+ LIMIT 5
286
+ """
287
+
288
+ try:
289
+ results = await backend.execute_query(
290
+ problem_query,
291
+ {"keywords": problem_keywords}
292
+ )
293
+
294
+ for record in results:
295
+ # If problem has solutions, suggest them; otherwise warn
296
+ has_solutions = bool(record.get("solution_ids"))
297
+
298
+ if has_solutions:
299
+ mitigation = f"Known solutions: {', '.join(record['solution_titles'][:2])}"
300
+ severity = "medium"
301
+ else:
302
+ mitigation = "No known solution yet - proceed with caution"
303
+ severity = "high"
304
+
305
+ warnings.append(Warning(
306
+ warning_id=record["problem_id"],
307
+ severity=severity,
308
+ title=f"Known issue: {record['problem_title']}",
309
+ description=record["problem_content"][:200],
310
+ evidence=[record["problem_id"]],
311
+ mitigation=mitigation,
312
+ related_problem_id=record["problem_id"],
313
+ ))
314
+
315
+ except Exception as e:
316
+ logger.error(f"Error checking for known problems: {e}")
317
+
318
+ # Filter by severity threshold
319
+ severity_levels = {"low": 0, "medium": 1, "high": 2}
320
+ threshold_level = severity_levels.get(severity_threshold, 1)
321
+
322
+ filtered_warnings = [
323
+ w for w in warnings
324
+ if severity_levels.get(w.severity, 0) >= threshold_level
325
+ ]
326
+
327
+ logger.info(f"Generated {len(filtered_warnings)} warnings")
328
+ return filtered_warnings
329
+
330
+
331
+ async def suggest_related_context(
332
+ backend: GraphBackend,
333
+ memory_id: str,
334
+ max_suggestions: int = 5,
335
+ ) -> List[Suggestion]:
336
+ """
337
+ Suggest related context that the user might want to know about.
338
+
339
+ "You might also want to know..." suggestions based on relationship graph.
340
+
341
+ Args:
342
+ backend: Database backend
343
+ memory_id: Current memory being viewed
344
+ max_suggestions: Maximum number of suggestions
345
+
346
+ Returns:
347
+ List of related suggestions
348
+
349
+ Example:
350
+ >>> suggestions = await suggest_related_context(backend, "mem_123")
351
+ >>> for s in suggestions:
352
+ ... print(f"Related: {s.title}")
353
+ """
354
+ logger.info(f"Suggesting related context for memory {memory_id}")
355
+
356
+ # Find related memories through strong relationships
357
+ related_query = """
358
+ MATCH (m:Memory {id: $memory_id})-[r]->(related:Memory)
359
+ WHERE r.strength >= 0.5
360
+ AND type(r) IN ['BUILDS_ON', 'CONFIRMS', 'SIMILAR_TO', 'RELATED_TO',
361
+ 'ALTERNATIVE_TO', 'IMPROVES']
362
+ RETURN related.id as id, related.type as type, related.title as title,
363
+ related.content as content, related.tags as tags,
364
+ related.effectiveness as effectiveness,
365
+ type(r) as rel_type, r.strength as strength
366
+ ORDER BY r.strength DESC
367
+ LIMIT $limit
368
+ """
369
+
370
+ suggestions = []
371
+
372
+ try:
373
+ results = await backend.execute_query(
374
+ related_query,
375
+ {"memory_id": memory_id, "limit": max_suggestions}
376
+ )
377
+
378
+ for record in results:
379
+ rel_type = record["rel_type"]
380
+ strength = record.get("strength", 0.5)
381
+
382
+ # Map relationship type to reason
383
+ reasons = {
384
+ "BUILDS_ON": "Builds on this concept",
385
+ "CONFIRMS": "Confirms this approach",
386
+ "SIMILAR_TO": "Similar approach",
387
+ "RELATED_TO": "Related information",
388
+ "ALTERNATIVE_TO": "Alternative approach",
389
+ "IMPROVES": "Improved version",
390
+ }
391
+
392
+ reason = reasons.get(rel_type, "Related")
393
+
394
+ suggestions.append(Suggestion(
395
+ suggestion_id=record["id"],
396
+ suggestion_type=record["type"],
397
+ title=record["title"],
398
+ description=record["content"][:200],
399
+ relevance_score=strength,
400
+ reason=reason,
401
+ memory_id=record["id"],
402
+ tags=record.get("tags", []),
403
+ effectiveness=record.get("effectiveness"),
404
+ ))
405
+
406
+ except Exception as e:
407
+ logger.error(f"Error finding related context: {e}")
408
+
409
+ logger.info(f"Found {len(suggestions)} related suggestions")
410
+ return suggestions[:max_suggestions]