memorisdk 2.3.0__tar.gz → 2.3.1__tar.gz

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 memorisdk might be problematic. Click here for more details.

Files changed (77) hide show
  1. {memorisdk-2.3.0 → memorisdk-2.3.1}/PKG-INFO +20 -7
  2. {memorisdk-2.3.0 → memorisdk-2.3.1}/README.md +19 -6
  3. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/agents/memory_agent.py +4 -0
  4. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/agents/retrieval_agent.py +4 -0
  5. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/core/conversation.py +7 -1
  6. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/core/memory.py +16 -7
  7. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/mongodb_manager.py +29 -0
  8. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/sqlalchemy_manager.py +33 -0
  9. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/integrations/openai_integration.py +12 -27
  10. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/logging.py +32 -81
  11. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/validators.py +4 -4
  12. {memorisdk-2.3.0 → memorisdk-2.3.1}/memorisdk.egg-info/PKG-INFO +20 -7
  13. {memorisdk-2.3.0 → memorisdk-2.3.1}/memorisdk.egg-info/SOURCES.txt +2 -1
  14. {memorisdk-2.3.0 → memorisdk-2.3.1}/pyproject.toml +1 -1
  15. memorisdk-2.3.1/tests/test_memory_validation.py +39 -0
  16. {memorisdk-2.3.0 → memorisdk-2.3.1}/LICENSE +0 -0
  17. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/__init__.py +0 -0
  18. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/agents/__init__.py +0 -0
  19. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/agents/conscious_agent.py +0 -0
  20. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/config/__init__.py +0 -0
  21. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/config/manager.py +0 -0
  22. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/config/memory_manager.py +0 -0
  23. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/config/settings.py +0 -0
  24. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/core/__init__.py +0 -0
  25. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/core/database.py +0 -0
  26. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/core/providers.py +0 -0
  27. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/__init__.py +0 -0
  28. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/adapters/__init__.py +0 -0
  29. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/adapters/mongodb_adapter.py +0 -0
  30. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/adapters/mysql_adapter.py +0 -0
  31. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/adapters/postgresql_adapter.py +0 -0
  32. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/adapters/sqlite_adapter.py +0 -0
  33. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/auto_creator.py +0 -0
  34. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connection_utils.py +0 -0
  35. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connectors/__init__.py +0 -0
  36. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connectors/base_connector.py +0 -0
  37. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connectors/mongodb_connector.py +0 -0
  38. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connectors/mysql_connector.py +0 -0
  39. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connectors/postgres_connector.py +0 -0
  40. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/connectors/sqlite_connector.py +0 -0
  41. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/models.py +0 -0
  42. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/queries/__init__.py +0 -0
  43. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/queries/base_queries.py +0 -0
  44. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/queries/chat_queries.py +0 -0
  45. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/queries/entity_queries.py +0 -0
  46. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/queries/memory_queries.py +0 -0
  47. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/query_translator.py +0 -0
  48. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/schema_generators/__init__.py +0 -0
  49. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/schema_generators/mongodb_schema_generator.py +0 -0
  50. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/schema_generators/mysql_schema_generator.py +0 -0
  51. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/search/__init__.py +0 -0
  52. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/search/mongodb_search_adapter.py +0 -0
  53. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/search/mysql_search_adapter.py +0 -0
  54. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/search/sqlite_search_adapter.py +0 -0
  55. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/search_service.py +0 -0
  56. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/templates/__init__.py +0 -0
  57. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/templates/basic_template.py +0 -0
  58. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/database/templates/schemas/__init__.py +0 -0
  59. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/integrations/__init__.py +0 -0
  60. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/integrations/anthropic_integration.py +0 -0
  61. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/integrations/litellm_integration.py +0 -0
  62. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/tools/__init__.py +0 -0
  63. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/tools/memory_tool.py +0 -0
  64. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/__init__.py +0 -0
  65. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/exceptions.py +0 -0
  66. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/helpers.py +0 -0
  67. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/input_validator.py +0 -0
  68. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/pydantic_models.py +0 -0
  69. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/query_builder.py +0 -0
  70. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/schemas.py +0 -0
  71. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/security_audit.py +0 -0
  72. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/security_integration.py +0 -0
  73. {memorisdk-2.3.0 → memorisdk-2.3.1}/memori/utils/transaction_manager.py +0 -0
  74. {memorisdk-2.3.0 → memorisdk-2.3.1}/memorisdk.egg-info/dependency_links.txt +0 -0
  75. {memorisdk-2.3.0 → memorisdk-2.3.1}/memorisdk.egg-info/requires.txt +0 -0
  76. {memorisdk-2.3.0 → memorisdk-2.3.1}/memorisdk.egg-info/top_level.txt +0 -0
  77. {memorisdk-2.3.0 → memorisdk-2.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorisdk
3
- Version: 2.3.0
3
+ Version: 2.3.1
4
4
  Summary: The Open-Source Memory Layer for AI Agents & Multi-Agent Systems
5
5
  Author-email: GibsonAI Team <noc@gibsonai.com>
6
6
  License: Apache-2.0
@@ -95,7 +95,7 @@ Requires-Dist: pandas>=2.0.0; extra == "all"
95
95
  Requires-Dist: plotly>=5.17.0; extra == "all"
96
96
  Dynamic: license-file
97
97
 
98
- [![GibsonAI](https://github.com/user-attachments/assets/878e341b-5a93-4489-a398-abeca91b6b11)](https://gibsonai.com/)
98
+ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/)
99
99
 
100
100
  # memori
101
101
 
@@ -110,7 +110,7 @@ Dynamic: license-file
110
110
  <p align="center">
111
111
  <a href="https://memori.gibsonai.com/docs">Learn more</a>
112
112
  ·
113
- <a href="https://www.gibsonai.com/discord">Join Discord</a>
113
+ <a href="https://discord.gg/abD4eGym6v">Join Discord</a>
114
114
  </p>
115
115
 
116
116
  <p align="center">
@@ -120,14 +120,20 @@ Dynamic: license-file
120
120
  <a href="https://pepy.tech/projects/memorisdk">
121
121
  <img src="https://static.pepy.tech/badge/memorisdk" alt="Downloads">
122
122
  </a>
123
- <a href="https://opensource.org/licenses/MIT">
124
- <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
123
+ <a href="https://opensource.org/license/apache-2-0">
124
+ <img src="https://img.shields.io/badge/license-Apache%20License%202.0-blue" alt="License: Apache 2.0">
125
125
  </a>
126
126
  <a href="https://www.python.org/downloads/">
127
127
  <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+">
128
128
  </a>
129
129
  </p>
130
130
 
131
+ <p align="center">
132
+ <a href="https://github.com/GibsonAI/memori/stargazers">
133
+ <img src="https://img.shields.io/badge/⭐%20Give%20a%20Star-Support%20the%20project-orange?style=for-the-badge" alt="Give a Star">
134
+ </a>
135
+ </p>
136
+
131
137
  ---
132
138
 
133
139
  ## What is Memori
@@ -206,6 +212,8 @@ print("\n💡 Notice: Memori automatically knows about your FastAPI Python proje
206
212
 
207
213
  ---
208
214
 
215
+ ⭐️ **Enjoying Memori?** Give us a star to support open development
216
+
209
217
  > By default, Memori uses in-memory SQLite database. Get **FREE** serverless database instance in [GibsonAI](https://app.gibsonai.com/signup) platform.
210
218
 
211
219
  **🚀 Ready to explore more?**
@@ -326,6 +334,7 @@ memori = Memori(
326
334
  database_connect="sqlite:///my_memory.db",
327
335
  template="basic",
328
336
  conscious_ingest=True, # One-shot context injection
337
+ conscious_memory_limit=100, # Must be an integer between 1 and 500
329
338
  openai_api_key="sk-..."
330
339
  )
331
340
 
@@ -503,11 +512,15 @@ Explore Memori's capabilities through these interactive demonstrations:
503
512
  ## 🤝 Contributing
504
513
 
505
514
  - See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
506
- - Community: [Discord](https://www.gibsonai.com/discord)
515
+ - Community: [Discord](https://discord.gg/abD4eGym6v)
516
+
517
+ ## ⭐️ Star us on GitHub to support the project
518
+
519
+ [![Star History Chart](https://api.star-history.com/svg?repos=GibsonAI/memori&type=date&legend=top-left)](https://www.star-history.com/#GibsonAI/memori&type=date&legend=top-left)
507
520
 
508
521
  ## 📄 License
509
522
 
510
- MIT License - see [LICENSE](./LICENSE) for details.
523
+ Apache 2.0 License - see [LICENSE](./LICENSE) for details.
511
524
 
512
525
  ---
513
526
 
@@ -1,4 +1,4 @@
1
- [![GibsonAI](https://github.com/user-attachments/assets/878e341b-5a93-4489-a398-abeca91b6b11)](https://gibsonai.com/)
1
+ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/)
2
2
 
3
3
  # memori
4
4
 
@@ -13,7 +13,7 @@
13
13
  <p align="center">
14
14
  <a href="https://memori.gibsonai.com/docs">Learn more</a>
15
15
  ·
16
- <a href="https://www.gibsonai.com/discord">Join Discord</a>
16
+ <a href="https://discord.gg/abD4eGym6v">Join Discord</a>
17
17
  </p>
18
18
 
19
19
  <p align="center">
@@ -23,14 +23,20 @@
23
23
  <a href="https://pepy.tech/projects/memorisdk">
24
24
  <img src="https://static.pepy.tech/badge/memorisdk" alt="Downloads">
25
25
  </a>
26
- <a href="https://opensource.org/licenses/MIT">
27
- <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
26
+ <a href="https://opensource.org/license/apache-2-0">
27
+ <img src="https://img.shields.io/badge/license-Apache%20License%202.0-blue" alt="License: Apache 2.0">
28
28
  </a>
29
29
  <a href="https://www.python.org/downloads/">
30
30
  <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+">
31
31
  </a>
32
32
  </p>
33
33
 
34
+ <p align="center">
35
+ <a href="https://github.com/GibsonAI/memori/stargazers">
36
+ <img src="https://img.shields.io/badge/⭐%20Give%20a%20Star-Support%20the%20project-orange?style=for-the-badge" alt="Give a Star">
37
+ </a>
38
+ </p>
39
+
34
40
  ---
35
41
 
36
42
  ## What is Memori
@@ -109,6 +115,8 @@ print("\n💡 Notice: Memori automatically knows about your FastAPI Python proje
109
115
 
110
116
  ---
111
117
 
118
+ ⭐️ **Enjoying Memori?** Give us a star to support open development
119
+
112
120
  > By default, Memori uses in-memory SQLite database. Get **FREE** serverless database instance in [GibsonAI](https://app.gibsonai.com/signup) platform.
113
121
 
114
122
  **🚀 Ready to explore more?**
@@ -229,6 +237,7 @@ memori = Memori(
229
237
  database_connect="sqlite:///my_memory.db",
230
238
  template="basic",
231
239
  conscious_ingest=True, # One-shot context injection
240
+ conscious_memory_limit=100, # Must be an integer between 1 and 500
232
241
  openai_api_key="sk-..."
233
242
  )
234
243
 
@@ -406,11 +415,15 @@ Explore Memori's capabilities through these interactive demonstrations:
406
415
  ## 🤝 Contributing
407
416
 
408
417
  - See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
409
- - Community: [Discord](https://www.gibsonai.com/discord)
418
+ - Community: [Discord](https://discord.gg/abD4eGym6v)
419
+
420
+ ## ⭐️ Star us on GitHub to support the project
421
+
422
+ [![Star History Chart](https://api.star-history.com/svg?repos=GibsonAI/memori&type=date&legend=top-left)](https://www.star-history.com/#GibsonAI/memori&type=date&legend=top-left)
410
423
 
411
424
  ## 📄 License
412
425
 
413
- MIT License - see [LICENSE](./LICENSE) for details.
426
+ Apache 2.0 License - see [LICENSE](./LICENSE) for details.
414
427
 
415
428
  ---
416
429
 
@@ -204,6 +204,9 @@ CONVERSATION CONTEXT:
204
204
  "content": f"Process this conversation for enhanced memory storage:\n\n{conversation_text}\n{context_info}",
205
205
  },
206
206
  ],
207
+ metadata=[
208
+ "INTERNAL_MEMORY_PROCESSING"
209
+ ], # Internal metadata tag
207
210
  response_format=ProcessedLongTermMemory,
208
211
  temperature=0.1, # Low temperature for consistent processing
209
212
  )
@@ -417,6 +420,7 @@ CONVERSATION CONTEXT:
417
420
  "content": f"Process this conversation for enhanced memory storage:\n\n{conversation_text}\n{context_info}",
418
421
  },
419
422
  ],
423
+ metadata=["INTERNAL_MEMORY_PROCESSING"], # Internal metadata tag
420
424
  temperature=0.1, # Low temperature for consistent processing
421
425
  max_tokens=2000, # Ensure enough tokens for full response
422
426
  )
@@ -149,6 +149,9 @@ Be strategic and comprehensive in your search planning."""
149
149
  "content": prompt,
150
150
  },
151
151
  ],
152
+ metadata=[
153
+ "INTERNAL_MEMORY_PROCESSING"
154
+ ], # Internal metadata tag
152
155
  response_format=MemorySearchQuery,
153
156
  temperature=0.1,
154
157
  )
@@ -656,6 +659,7 @@ Be strategic and comprehensive in your search planning."""
656
659
  "content": prompt,
657
660
  },
658
661
  ],
662
+ metadata=["INTERNAL_MEMORY_PROCESSING"], # Internal metadata tag
659
663
  temperature=0.1,
660
664
  max_tokens=1000, # Ensure enough tokens for full response
661
665
  )
@@ -242,8 +242,14 @@ class ConversationManager:
242
242
  if previous_messages:
243
243
  system_content += "\n--- Conversation History ---\n"
244
244
  for msg in previous_messages:
245
- role_label = "You" if msg["role"] == "assistant" else "User"
245
+ if msg["role"] == "assistant":
246
+ role_label = "Assistant"
247
+ elif msg["role"] == "user":
248
+ role_label = "User"
249
+ else:
250
+ role_label = msg["role"].capitalize()
246
251
  system_content += f"{role_label}: {msg['content']}\n"
252
+
247
253
  system_content += "--- End History ---\n"
248
254
  logger.debug(
249
255
  f"[CONTEXT] Added {len(previous_messages)} history messages | Session: {session_id[:8]}..."
@@ -15,6 +15,7 @@ try:
15
15
  import litellm # noqa: F401
16
16
  from litellm import success_callback # noqa: F401
17
17
 
18
+ _ = litellm # Mark as intentionally imported
18
19
  LITELLM_AVAILABLE = True
19
20
  except ImportError:
20
21
  LITELLM_AVAILABLE = False
@@ -111,9 +112,15 @@ class Memori:
111
112
  self.schema_init = schema_init
112
113
  self.database_prefix = database_prefix
113
114
  self.database_suffix = database_suffix
115
+
114
116
  # Validate conscious_memory_limit parameter
115
- if not isinstance(conscious_memory_limit, int) or conscious_memory_limit < 1:
116
- raise ValueError("conscious_memory_limit must be a positive integer")
117
+ if not isinstance(conscious_memory_limit, int) or isinstance(
118
+ conscious_memory_limit, bool
119
+ ):
120
+ raise TypeError("conscious_memory_limit must be an integer (not bool)")
121
+
122
+ if not (1 <= conscious_memory_limit <= 2000):
123
+ raise ValueError("conscious_memory_limit must be between 1 and 2000")
117
124
 
118
125
  self.conscious_memory_limit = conscious_memory_limit
119
126
 
@@ -1213,7 +1220,7 @@ class Memori:
1213
1220
  results[:3]
1214
1221
  ): # Log first 3 results for debugging
1215
1222
  logger.debug(
1216
- f"Auto-ingest: Result {i+1}: {type(result)} with keys: {list(result.keys()) if isinstance(result, dict) else 'N/A'}"
1223
+ f"Auto-ingest: Result {i + 1}: {type(result)} with keys: {list(result.keys()) if isinstance(result, dict) else 'N/A'}"
1217
1224
  )
1218
1225
  except Exception as db_search_e:
1219
1226
  logger.error(f"Auto-ingest: Database search failed: {db_search_e}")
@@ -2634,12 +2641,14 @@ class Memori:
2634
2641
  Get auto-ingest context as system prompt for direct injection.
2635
2642
  Returns relevant memories based on user input as formatted system prompt.
2636
2643
  Use this for auto_ingest mode.
2644
+
2645
+ Note: Context retrieval is handled by _get_auto_ingest_context().
2646
+ This function only formats pre-retrieved context.
2637
2647
  """
2638
2648
  try:
2639
- # For now, use recent short-term memories as a simple approach
2640
- # This avoids the search engine issues and still provides context
2641
- # TODO: Use user_input for intelligent context retrieval
2642
- context = self._get_conscious_context() # Get recent short-term memories
2649
+ # Get recent short-term memories as fallback context
2650
+ # The actual intelligent retrieval is handled by _get_auto_ingest_context()
2651
+ context = self._get_conscious_context()
2643
2652
 
2644
2653
  if not context:
2645
2654
  return ""
@@ -928,6 +928,28 @@ class MongoDBDatabaseManager:
928
928
  except Exception as e:
929
929
  logger.error(f"Failed to mark conscious memories processed: {e}")
930
930
 
931
+ def _check_milestone(self, memory_count: int):
932
+ """
933
+ Check and celebrate memory storage milestones to encourage user engagement.
934
+ Displays celebration messages at key milestones: 10, 50, 100, 500, 1000 memories.
935
+
936
+ Args:
937
+ memory_count: Current count of long-term memories
938
+ """
939
+ milestones = [10, 50, 100, 500, 1000]
940
+
941
+ if memory_count in milestones:
942
+ celebration_msg = (
943
+ f"\n{'=' * 60}\n"
944
+ f"🎉 Milestone Achieved: {memory_count} memories stored!\n"
945
+ f"{'=' * 60}\n"
946
+ f"⭐️ Loving Memori? Give us a star on GitHub!\n"
947
+ f"👉 https://github.com/GibsonAI/memori\n"
948
+ f"Your support helps us build better open AI memory tools ❤️\n"
949
+ f"{'=' * 60}\n"
950
+ )
951
+ logger.info(celebration_msg)
952
+
931
953
  def store_long_term_memory_enhanced(
932
954
  self, memory: ProcessedLongTermMemory, chat_id: str, namespace: str = "default"
933
955
  ) -> str:
@@ -1000,6 +1022,13 @@ class MongoDBDatabaseManager:
1000
1022
  collection.insert_one(document)
1001
1023
 
1002
1024
  logger.debug(f"Stored enhanced long-term memory {memory_id}")
1025
+
1026
+ # Get current memory count and check for milestones
1027
+ total_memories = collection.count_documents({"namespace": namespace})
1028
+
1029
+ # Celebrate milestone if reached
1030
+ self._check_milestone(total_memories)
1031
+
1003
1032
  return memory_id
1004
1033
 
1005
1034
  except Exception as e:
@@ -572,6 +572,28 @@ class SQLAlchemyDatabaseManager:
572
572
  except SQLAlchemyError as e:
573
573
  raise DatabaseError(f"Failed to get chat history: {e}")
574
574
 
575
+ def _check_milestone(self, memory_count: int):
576
+ """
577
+ Check and celebrate memory storage milestones to encourage user engagement.
578
+ Displays celebration messages at key milestones: 10, 50, 100, 500, 1000 memories.
579
+
580
+ Args:
581
+ memory_count: Current count of long-term memories
582
+ """
583
+ milestones = [10, 50, 100, 500, 1000]
584
+
585
+ if memory_count in milestones:
586
+ celebration_msg = (
587
+ f"\n{'=' * 60}\n"
588
+ f"🎉 Milestone Achieved: {memory_count} memories stored!\n"
589
+ f"{'=' * 60}\n"
590
+ f"⭐️ Loving Memori? Give us a star on GitHub!\n"
591
+ f"👉 https://github.com/GibsonAI/memori\n"
592
+ f"Your support helps us build better open AI memory tools ❤️\n"
593
+ f"{'=' * 60}\n"
594
+ )
595
+ logger.info(celebration_msg)
596
+
575
597
  def store_long_term_memory_enhanced(
576
598
  self, memory: ProcessedLongTermMemory, chat_id: str, namespace: str = "default"
577
599
  ) -> str:
@@ -618,6 +640,17 @@ class SQLAlchemyDatabaseManager:
618
640
  session.commit()
619
641
 
620
642
  logger.debug(f"Stored enhanced long-term memory {memory_id}")
643
+
644
+ # Get current memory count and check for milestones
645
+ total_memories = (
646
+ session.query(LongTermMemory)
647
+ .filter(LongTermMemory.namespace == namespace)
648
+ .count()
649
+ )
650
+
651
+ # Celebrate milestone if reached
652
+ self._check_milestone(total_memories)
653
+
621
654
  return memory_id
622
655
 
623
656
  except SQLAlchemyError as e:
@@ -246,33 +246,18 @@ class OpenAIInterceptor:
246
246
  def _is_internal_agent_call(cls, json_data):
247
247
  """Check if this is an internal agent processing call that should not be recorded."""
248
248
  try:
249
- messages = json_data.get("messages", [])
250
- for message in messages:
251
- content = message.get("content", "")
252
- if isinstance(content, str):
253
- # Check for specific internal agent processing patterns
254
- # Made patterns more specific to avoid false positives
255
- internal_patterns = [
256
- "Process this conversation for enhanced memory storage:",
257
- "Enhanced memory processing:",
258
- "Memory classification:",
259
- "Search for relevant memories:",
260
- "Analyze conversation for:",
261
- "Extract entities from:",
262
- "Categorize the following conversation:",
263
- # More specific patterns to avoid blocking legitimate conversations
264
- "INTERNAL_MEMORY_PROCESSING:",
265
- "AGENT_PROCESSING_MODE:",
266
- "MEMORY_AGENT_TASK:",
267
- ]
268
-
269
- # Only flag as internal if it matches specific patterns AND has no user role
270
- for pattern in internal_patterns:
271
- if pattern in content:
272
- # Double-check: if this is a user message, don't filter it
273
- if message.get("role") == "user":
274
- continue
275
- return True
249
+ openai_metadata = json_data.get("metadata", [])
250
+
251
+ # Check for specific internal agent metadata flags
252
+ if isinstance(openai_metadata, list):
253
+ internal_metadata = [
254
+ "INTERNAL_MEMORY_PROCESSING", # used in memory agent and retrieval agent
255
+ "AGENT_PROCESSING_MODE",
256
+ "MEMORY_AGENT_TASK",
257
+ ]
258
+ for internal in internal_metadata:
259
+ if internal in openai_metadata:
260
+ return True
276
261
 
277
262
  return False
278
263
 
@@ -2,6 +2,7 @@
2
2
  Centralized logging configuration for Memoriai
3
3
  """
4
4
 
5
+ import logging
5
6
  import sys
6
7
  from pathlib import Path
7
8
  from typing import Any
@@ -22,41 +23,35 @@ class LoggingManager:
22
23
  def setup_logging(cls, settings: LoggingSettings, verbose: bool = False) -> None:
23
24
  """Setup logging configuration"""
24
25
  try:
25
- # Remove default handler if it exists
26
26
  if not cls._initialized:
27
27
  logger.remove()
28
28
 
29
29
  if verbose:
30
- # When verbose mode is enabled, disable all other loggers and show only loguru
31
30
  cls._disable_other_loggers()
32
31
 
33
- # Configure console logging with DEBUG level and full formatting
34
32
  logger.add(
35
33
  sys.stderr,
36
34
  level="DEBUG",
37
- format=settings.format,
35
+ format="<green>{time:HH:mm:ss}</green> | <level>{level:8}</level> | {message}",
38
36
  colorize=True,
39
37
  backtrace=True,
40
38
  diagnose=True,
41
39
  )
42
40
  else:
43
- # When verbose is False, minimize loguru output to essential logs only
44
41
  logger.add(
45
42
  sys.stderr,
46
- level="WARNING", # Only show warnings and errors
47
- format="<level>{level}</level>: {message}", # Simplified format
43
+ level="WARNING",
44
+ format="<level>{level}</level>: {message}",
48
45
  colorize=False,
49
46
  backtrace=False,
50
47
  diagnose=False,
51
48
  )
52
49
 
53
- # Configure file logging if enabled
54
50
  if settings.log_to_file:
55
51
  log_path = Path(settings.log_file_path)
56
52
  log_path.parent.mkdir(parents=True, exist_ok=True)
57
53
 
58
54
  if settings.structured_logging:
59
- # JSON structured logging
60
55
  logger.add(
61
56
  log_path,
62
57
  level=settings.level.value,
@@ -67,7 +62,6 @@ class LoggingManager:
67
62
  serialize=True,
68
63
  )
69
64
  else:
70
- # Regular text logging
71
65
  logger.add(
72
66
  log_path,
73
67
  level=settings.level.value,
@@ -96,7 +90,6 @@ class LoggingManager:
96
90
  raise ConfigurationError("Logging not initialized")
97
91
 
98
92
  try:
99
- # Remove existing handlers and recreate with new level
100
93
  logger.remove()
101
94
 
102
95
  if cls._current_config:
@@ -127,76 +120,34 @@ class LoggingManager:
127
120
 
128
121
  @classmethod
129
122
  def _disable_other_loggers(cls) -> None:
130
- """Disable all other loggers when verbose mode is enabled"""
131
- import logging
132
-
133
- # Set the root logger to CRITICAL and disable it
134
- root_logger = logging.getLogger()
135
- root_logger.setLevel(logging.CRITICAL)
136
- root_logger.disabled = True
137
-
138
- # Remove all handlers from the root logger
139
- for handler in root_logger.handlers[:]:
140
- root_logger.removeHandler(handler)
141
-
142
- # Disable common third-party library loggers
143
- third_party_loggers = [
144
- "urllib3",
145
- "requests",
146
- "httpx",
147
- "httpcore",
148
- "openai",
149
- "anthropic",
150
- "litellm",
151
- "sqlalchemy",
152
- "alembic",
153
- "asyncio",
154
- "concurrent.futures",
155
- "charset_normalizer",
156
- "certifi",
157
- "idna",
158
- ]
159
-
160
- for logger_name in third_party_loggers:
161
- lib_logger = logging.getLogger(logger_name)
162
- lib_logger.disabled = True
163
- lib_logger.setLevel(logging.CRITICAL)
164
- # Remove all handlers
165
- for handler in lib_logger.handlers[:]:
166
- lib_logger.removeHandler(handler)
167
-
168
- # Set all existing loggers to CRITICAL level and disable them
169
- for name in list(logging.Logger.manager.loggerDict.keys()):
170
- existing_logger = logging.getLogger(name)
171
- existing_logger.setLevel(logging.CRITICAL)
172
- existing_logger.disabled = True
173
- # Remove all handlers
174
- for handler in existing_logger.handlers[:]:
175
- existing_logger.removeHandler(handler)
176
-
177
- # Also disable warnings from the warnings module
178
- import warnings
179
-
180
- warnings.filterwarnings("ignore")
181
-
182
- # Override the logging module's basicConfig to prevent new loggers
183
- def disabled_basicConfig(*args, **kwargs):
184
- pass
185
-
186
- logging.basicConfig = disabled_basicConfig
187
-
188
- # Override the getLogger function to disable new loggers immediately
189
- original_getLogger = logging.getLogger
190
-
191
- def disabled_getLogger(name=None):
192
- logger_instance = original_getLogger(name)
193
- logger_instance.disabled = True
194
- logger_instance.setLevel(logging.CRITICAL)
195
- for handler in logger_instance.handlers[:]:
196
- logger_instance.removeHandler(handler)
197
- return logger_instance
198
-
199
- logging.getLogger = disabled_getLogger
123
+ """
124
+ Intercept all logs from the standard `logging` module and redirect them to Loguru.
125
+ This ensures all log output is controlled and formatted by Loguru.
126
+ """
127
+
128
+ class InterceptStandardLoggingHandler(logging.Handler):
129
+ def emit(self, record: logging.LogRecord) -> None:
130
+ try:
131
+ level = logger.level(record.levelname).name
132
+ except ValueError:
133
+ level = record.levelno
134
+
135
+ frame, depth = logging.currentframe(), 2
136
+ while (
137
+ frame is not None and frame.f_code.co_filename == logging.__file__
138
+ ):
139
+ frame = frame.f_back
140
+ depth += 1
141
+
142
+ formatted_message = f"[{record.name}] {record.getMessage()}"
143
+
144
+ logger.opt(depth=depth, exception=record.exc_info).log(
145
+ level, formatted_message
146
+ )
147
+
148
+ logging.basicConfig(
149
+ handlers=[InterceptStandardLoggingHandler()], level=0, force=True
150
+ )
200
151
 
201
152
 
202
153
  def get_logger(name: str = "memori") -> "logger":
@@ -58,12 +58,12 @@ class DataValidator:
58
58
  @classmethod
59
59
  def validate_namespace(cls, value: str, field_name: str = "namespace") -> str:
60
60
  """Validate namespace format"""
61
+ if value is None or (isinstance(value, str) and value.strip() == ""):
62
+ return "default"
63
+
61
64
  if not isinstance(value, str):
62
65
  raise ValidationError(f"{field_name} must be a string")
63
66
 
64
- if not value:
65
- raise ValidationError(f"{field_name} cannot be empty")
66
-
67
67
  if len(value) > 64:
68
68
  raise ValidationError(f"{field_name} cannot exceed 64 characters")
69
69
 
@@ -73,7 +73,7 @@ class DataValidator:
73
73
  f"{field_name} can only contain letters, numbers, underscores, and hyphens"
74
74
  )
75
75
 
76
- return value
76
+ return value.strip()
77
77
 
78
78
  @classmethod
79
79
  def validate_importance_score(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorisdk
3
- Version: 2.3.0
3
+ Version: 2.3.1
4
4
  Summary: The Open-Source Memory Layer for AI Agents & Multi-Agent Systems
5
5
  Author-email: GibsonAI Team <noc@gibsonai.com>
6
6
  License: Apache-2.0
@@ -95,7 +95,7 @@ Requires-Dist: pandas>=2.0.0; extra == "all"
95
95
  Requires-Dist: plotly>=5.17.0; extra == "all"
96
96
  Dynamic: license-file
97
97
 
98
- [![GibsonAI](https://github.com/user-attachments/assets/878e341b-5a93-4489-a398-abeca91b6b11)](https://gibsonai.com/)
98
+ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/)
99
99
 
100
100
  # memori
101
101
 
@@ -110,7 +110,7 @@ Dynamic: license-file
110
110
  <p align="center">
111
111
  <a href="https://memori.gibsonai.com/docs">Learn more</a>
112
112
  ·
113
- <a href="https://www.gibsonai.com/discord">Join Discord</a>
113
+ <a href="https://discord.gg/abD4eGym6v">Join Discord</a>
114
114
  </p>
115
115
 
116
116
  <p align="center">
@@ -120,14 +120,20 @@ Dynamic: license-file
120
120
  <a href="https://pepy.tech/projects/memorisdk">
121
121
  <img src="https://static.pepy.tech/badge/memorisdk" alt="Downloads">
122
122
  </a>
123
- <a href="https://opensource.org/licenses/MIT">
124
- <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
123
+ <a href="https://opensource.org/license/apache-2-0">
124
+ <img src="https://img.shields.io/badge/license-Apache%20License%202.0-blue" alt="License: Apache 2.0">
125
125
  </a>
126
126
  <a href="https://www.python.org/downloads/">
127
127
  <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+">
128
128
  </a>
129
129
  </p>
130
130
 
131
+ <p align="center">
132
+ <a href="https://github.com/GibsonAI/memori/stargazers">
133
+ <img src="https://img.shields.io/badge/⭐%20Give%20a%20Star-Support%20the%20project-orange?style=for-the-badge" alt="Give a Star">
134
+ </a>
135
+ </p>
136
+
131
137
  ---
132
138
 
133
139
  ## What is Memori
@@ -206,6 +212,8 @@ print("\n💡 Notice: Memori automatically knows about your FastAPI Python proje
206
212
 
207
213
  ---
208
214
 
215
+ ⭐️ **Enjoying Memori?** Give us a star to support open development
216
+
209
217
  > By default, Memori uses in-memory SQLite database. Get **FREE** serverless database instance in [GibsonAI](https://app.gibsonai.com/signup) platform.
210
218
 
211
219
  **🚀 Ready to explore more?**
@@ -326,6 +334,7 @@ memori = Memori(
326
334
  database_connect="sqlite:///my_memory.db",
327
335
  template="basic",
328
336
  conscious_ingest=True, # One-shot context injection
337
+ conscious_memory_limit=100, # Must be an integer between 1 and 500
329
338
  openai_api_key="sk-..."
330
339
  )
331
340
 
@@ -503,11 +512,15 @@ Explore Memori's capabilities through these interactive demonstrations:
503
512
  ## 🤝 Contributing
504
513
 
505
514
  - See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
506
- - Community: [Discord](https://www.gibsonai.com/discord)
515
+ - Community: [Discord](https://discord.gg/abD4eGym6v)
516
+
517
+ ## ⭐️ Star us on GitHub to support the project
518
+
519
+ [![Star History Chart](https://api.star-history.com/svg?repos=GibsonAI/memori&type=date&legend=top-left)](https://www.star-history.com/#GibsonAI/memori&type=date&legend=top-left)
507
520
 
508
521
  ## 📄 License
509
522
 
510
- MIT License - see [LICENSE](./LICENSE) for details.
523
+ Apache 2.0 License - see [LICENSE](./LICENSE) for details.
511
524
 
512
525
  ---
513
526
 
@@ -71,4 +71,5 @@ memorisdk.egg-info/PKG-INFO
71
71
  memorisdk.egg-info/SOURCES.txt
72
72
  memorisdk.egg-info/dependency_links.txt
73
73
  memorisdk.egg-info/requires.txt
74
- memorisdk.egg-info/top_level.txt
74
+ memorisdk.egg-info/top_level.txt
75
+ tests/test_memory_validation.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memorisdk"
7
- version = "2.3.0"
7
+ version = "2.3.1"
8
8
  description = "The Open-Source Memory Layer for AI Agents & Multi-Agent Systems"
9
9
  authors = [{name = "GibsonAI Team", email = "noc@gibsonai.com"}]
10
10
  license = {text = "Apache-2.0"}
@@ -0,0 +1,39 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+
6
+ # Add the root project folder to the Python path
7
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
8
+
9
+ from memori.core.memory import Memori
10
+
11
+
12
+ def test_conscious_memory_limit_valid_values():
13
+ """Valid conscious_memory_limit values should not raise errors"""
14
+ try:
15
+ Memori(conscious_memory_limit=1)
16
+ Memori(conscious_memory_limit=500)
17
+ Memori(conscious_memory_limit=1500)
18
+ Memori(conscious_memory_limit=2000)
19
+ except Exception as e:
20
+ pytest.fail(f"Unexpected exception raised: {e}")
21
+
22
+
23
+ def test_conscious_memory_limit_invalid_low():
24
+ """Values below 1 should raise ValueError"""
25
+ with pytest.raises(ValueError):
26
+ Memori(conscious_memory_limit=0)
27
+
28
+
29
+ def test_conscious_memory_limit_invalid_high():
30
+ """Values above 2000 should raise ValueError"""
31
+ with pytest.raises(ValueError):
32
+ Memori(conscious_memory_limit=3000)
33
+
34
+
35
+ @pytest.mark.parametrize("invalid_value", ["high", 3.14, None, True, False])
36
+ def test_conscious_memory_limit_invalid_types(invalid_value):
37
+ """Non-integer or boolean types should raise TypeError"""
38
+ with pytest.raises(TypeError):
39
+ Memori(conscious_memory_limit=invalid_value)
File without changes
File without changes
File without changes