Skip to content

Commit 36b0ff7

Browse files
devwdaveactions-usergithub-actions[bot]
authored
Update README to replace GibsonAI logo with Memori Labs logo (#140)
* Update README to replace GibsonAI logo with Memori Labs logo * Auto-format code with Black, isort, and Ruff - Applied Black formatting (line-length: 88) - Sorted imports with isort (black profile) - Applied Ruff auto-fixes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: GitHub Action <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 95f5925 commit 36b0ff7

File tree

8 files changed

+81
-56
lines changed

8 files changed

+81
-56
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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/)
22

33
# memori
44

memori/core/memory.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ def __init__(
114114
self.database_suffix = database_suffix
115115

116116
# Validate conscious_memory_limit parameter
117-
if not isinstance(conscious_memory_limit, int) or isinstance(conscious_memory_limit, bool):
117+
if not isinstance(conscious_memory_limit, int) or isinstance(
118+
conscious_memory_limit, bool
119+
):
118120
raise TypeError("conscious_memory_limit must be an integer (not bool)")
119121

120122
if not (1 <= conscious_memory_limit <= 2000):
@@ -2639,7 +2641,7 @@ def get_auto_ingest_system_prompt(self, user_input: str) -> str:
26392641
Get auto-ingest context as system prompt for direct injection.
26402642
Returns relevant memories based on user input as formatted system prompt.
26412643
Use this for auto_ingest mode.
2642-
2644+
26432645
Note: Context retrieval is handled by _get_auto_ingest_context().
26442646
This function only formats pre-retrieved context.
26452647
"""

memori/utils/logging.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ def setup_logging(cls, settings: LoggingSettings, verbose: bool = False) -> None
4040
else:
4141
logger.add(
4242
sys.stderr,
43-
level="WARNING",
44-
format="<level>{level}</level>: {message}",
43+
level="WARNING",
44+
format="<level>{level}</level>: {message}",
4545
colorize=False,
4646
backtrace=False,
4747
diagnose=False,
@@ -124,7 +124,6 @@ def _disable_other_loggers(cls) -> None:
124124
Intercept all logs from the standard `logging` module and redirect them to Loguru.
125125
This ensures all log output is controlled and formatted by Loguru.
126126
"""
127-
import logging
128127

129128
class InterceptStandardLoggingHandler(logging.Handler):
130129
def emit(self, record: logging.LogRecord) -> None:
@@ -134,7 +133,9 @@ def emit(self, record: logging.LogRecord) -> None:
134133
level = record.levelno
135134

136135
frame, depth = logging.currentframe(), 2
137-
while frame is not None and frame.f_code.co_filename == logging.__file__:
136+
while (
137+
frame is not None and frame.f_code.co_filename == logging.__file__
138+
):
138139
frame = frame.f_back
139140
depth += 1
140141

@@ -144,9 +145,11 @@ def emit(self, record: logging.LogRecord) -> None:
144145
level, formatted_message
145146
)
146147

147-
logging.basicConfig(handlers=[InterceptStandardLoggingHandler()], level=0, force=True)
148+
logging.basicConfig(
149+
handlers=[InterceptStandardLoggingHandler()], level=0, force=True
150+
)
148151

149152

150153
def get_logger(name: str = "memori") -> "logger":
151154
"""Convenience function to get a logger"""
152-
return LoggingManager.get_logger(name)
155+
return LoggingManager.get_logger(name)

memori/utils/validators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ def validate_openai_api_key(cls, value: str, field_name: str = "API key") -> str
5858
@classmethod
5959
def validate_namespace(cls, value: str, field_name: str = "namespace") -> str:
6060
"""Validate namespace format"""
61-
if value is None or (isinstance(value,str) and value.strip() == ""):
61+
if value is None or (isinstance(value, str) and value.strip() == ""):
6262
return "default"
6363

64-
if not isinstance(value,str):
64+
if not isinstance(value, str):
6565
raise ValidationError(f"{field_name} must be a string")
6666

6767
if len(value) > 64:

tests/openai_support/openai_test.py

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -301,15 +301,15 @@ def main():
301301
def test_auto_ingest_intelligent_retrieval():
302302
"""
303303
Test _get_auto_ingest_context() for intelligent context retrieval.
304-
304+
305305
This function is the actual implementation that handles:
306306
- Database search with user_input
307307
- Fallback to recent memories
308308
- Recursion guard protection
309309
- Search engine integration
310310
- Error handling
311311
"""
312-
from unittest.mock import patch, MagicMock
312+
from unittest.mock import MagicMock, patch
313313

314314
print("\n" + "=" * 60)
315315
print("Testing _get_auto_ingest_context() Intelligent Retrieval")
@@ -344,16 +344,21 @@ def test_auto_ingest_intelligent_retrieval():
344344
result = memori._get_auto_ingest_context("What are my preferences?")
345345

346346
# Verify results returned with metadata
347-
if len(result) == 3 and result[0].get("retrieval_method") == "direct_database_search":
347+
if (
348+
len(result) == 3
349+
and result[0].get("retrieval_method") == "direct_database_search"
350+
):
348351
print("[OK] Test 1 passed: Direct search returns 3 results with metadata")
349352
test_passed += 1
350353
else:
351-
print(f"[FAIL] Test 1 failed: got {len(result)} results, metadata: {result[0].get('retrieval_method') if result else 'N/A'}")
354+
print(
355+
f"[FAIL] Test 1 failed: got {len(result)} results, metadata: {result[0].get('retrieval_method') if result else 'N/A'}"
356+
)
352357

353358
# Test 2: Empty input returns empty list
354359
print("\n[Test 2/8] Empty input returns empty list...")
355360
result = memori._get_auto_ingest_context("")
356-
361+
357362
if result == []:
358363
print("[OK] Test 2 passed: Empty input returns []")
359364
test_passed += 1
@@ -374,16 +379,21 @@ def test_auto_ingest_intelligent_retrieval():
374379
result = memori._get_auto_ingest_context("query with no results")
375380

376381
# Check fallback was used and metadata added
377-
if len(result) == 2 and result[0].get("retrieval_method") == "recent_memories_fallback":
382+
if (
383+
len(result) == 2
384+
and result[0].get("retrieval_method") == "recent_memories_fallback"
385+
):
378386
print("[OK] Test 3 passed: Fallback to recent memories works")
379387
test_passed += 1
380388
else:
381-
print(f"[FAIL] Test 3 failed: got {len(result)} results, metadata: {result[0].get('retrieval_method') if result else 'N/A'}")
389+
print(
390+
f"[FAIL] Test 3 failed: got {len(result)} results, metadata: {result[0].get('retrieval_method') if result else 'N/A'}"
391+
)
382392

383393
# Test 4: Recursion guard prevents infinite loops
384394
print("\n[Test 4/8] Recursion guard prevents infinite loops...")
385395
memori._in_context_retrieval = True
386-
396+
387397
mock_results = [{"searchable_content": "Safe result", "category_primary": "fact"}]
388398
with patch.object(
389399
memori.db_manager, "search_memories", return_value=mock_results
@@ -395,8 +405,8 @@ def test_auto_ingest_intelligent_retrieval():
395405
print("[OK] Test 4 passed: Recursion guard triggers direct search")
396406
test_passed += 1
397407
else:
398-
print(f"[FAIL] Test 4 failed: Expected direct search results")
399-
408+
print("[FAIL] Test 4 failed: Expected direct search results")
409+
400410
# Reset recursion guard
401411
memori._in_context_retrieval = False
402412

@@ -410,7 +420,9 @@ def test_auto_ingest_intelligent_retrieval():
410420
memori.search_engine = mock_search_engine
411421

412422
with patch.object(
413-
memori.db_manager, "search_memories", side_effect=[[], []] # Both direct and fallback empty
423+
memori.db_manager,
424+
"search_memories",
425+
side_effect=[[], []], # Both direct and fallback empty
414426
):
415427
result = memori._get_auto_ingest_context("advanced query")
416428

@@ -419,25 +431,30 @@ def test_auto_ingest_intelligent_retrieval():
419431
print("[OK] Test 5 passed: Search engine fallback works")
420432
test_passed += 1
421433
else:
422-
print(f"[FAIL] Test 5 failed: got {len(result)} results, metadata: {result[0].get('retrieval_method') if result else 'N/A'}")
423-
434+
print(
435+
f"[FAIL] Test 5 failed: got {len(result)} results, metadata: {result[0].get('retrieval_method') if result else 'N/A'}"
436+
)
437+
424438
# Reset search engine
425439
memori.search_engine = None
426440

427441
# Test 6: Error handling - graceful degradation
428442
print("\n[Test 6/8] Error handling with graceful degradation...")
429-
443+
430444
# First call fails, fallback succeeds
431445
mock_fallback = [{"searchable_content": "Fallback", "category_primary": "fact"}]
432446
with patch.object(
433447
memori.db_manager,
434448
"search_memories",
435-
side_effect=[Exception("DB error"), mock_fallback]
449+
side_effect=[Exception("DB error"), mock_fallback],
436450
):
437451
result = memori._get_auto_ingest_context("test error handling")
438452

439453
# Should fallback to recent memories
440-
if len(result) == 1 and result[0].get("retrieval_method") == "recent_memories_fallback":
454+
if (
455+
len(result) == 1
456+
and result[0].get("retrieval_method") == "recent_memories_fallback"
457+
):
441458
print("[OK] Test 6 passed: Error handled, fallback used")
442459
test_passed += 1
443460
else:
@@ -448,7 +465,7 @@ def test_auto_ingest_intelligent_retrieval():
448465
with patch.object(
449466
memori.db_manager,
450467
"search_memories",
451-
return_value=[{"searchable_content": "Test", "category_primary": "fact"}]
468+
return_value=[{"searchable_content": "Test", "category_primary": "fact"}],
452469
) as mock_search:
453470
user_query = "find my API keys"
454471
result = memori._get_auto_ingest_context(user_query)
@@ -468,7 +485,9 @@ def test_auto_ingest_intelligent_retrieval():
468485
print("[OK] Test 7 passed: search_memories called with correct params")
469486
test_passed += 1
470487
else:
471-
print(f"[FAIL] Test 7 failed: query={query_match}, ns={namespace_match}, limit={limit_match}")
488+
print(
489+
f"[FAIL] Test 7 failed: query={query_match}, ns={namespace_match}, limit={limit_match}"
490+
)
472491
else:
473492
print("[FAIL] Test 7 failed: search_memories not called")
474493

@@ -479,22 +498,19 @@ def test_auto_ingest_intelligent_retrieval():
479498
{"searchable_content": "Item 2", "category_primary": "preference"},
480499
]
481500

482-
with patch.object(
483-
memori.db_manager, "search_memories", return_value=mock_results
484-
):
501+
with patch.object(memori.db_manager, "search_memories", return_value=mock_results):
485502
result = memori._get_auto_ingest_context("metadata test")
486503

487504
# Check all results have metadata
488505
all_have_metadata = all(
489-
r.get("retrieval_method") and r.get("retrieval_query")
490-
for r in result
506+
r.get("retrieval_method") and r.get("retrieval_query") for r in result
491507
)
492508

493509
if all_have_metadata and result[0]["retrieval_query"] == "metadata test":
494510
print("[OK] Test 8 passed: All results have retrieval metadata")
495511
test_passed += 1
496512
else:
497-
print(f"[FAIL] Test 8 failed: metadata missing or incorrect")
513+
print("[FAIL] Test 8 failed: metadata missing or incorrect")
498514

499515
# Summary
500516
print("\n" + "=" * 60)

tests/test_memory_validation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
from pathlib import Path
3+
34
import pytest
45

56
# Add the root project folder to the Python path
@@ -35,4 +36,4 @@ def test_conscious_memory_limit_invalid_high():
3536
def test_conscious_memory_limit_invalid_types(invalid_value):
3637
"""Non-integer or boolean types should raise TypeError"""
3738
with pytest.raises(TypeError):
38-
Memori(conscious_memory_limit=invalid_value)
39+
Memori(conscious_memory_limit=invalid_value)

tests/utils/test_logging.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,70 @@
11
import logging
22
import re
3+
34
import pytest
45
from loguru import logger
5-
from memori.utils.logging import LoggingManager
6+
67
from memori.config.settings import LoggingSettings
8+
from memori.utils.logging import LoggingManager
9+
10+
TIMESTAMP_REGEX = re.compile(r"\d{2}:\d{2}:\d{2}")
711

8-
TIMESTAMP_REGEX = re.compile(r'\d{2}:\d{2}:\d{2}')
912

1013
@pytest.fixture(autouse=True)
1114
def reset_logging():
1215
"""A fixture to ensure logging is reset after each test."""
1316
yield
1417
logger.remove()
1518

19+
1620
def test_memori_logger_works_in_verbose(capsys):
1721
"""
1822
Tests that Memori's own Loguru logger still works
1923
and includes the new formatting (e.g., timestamp).
2024
"""
2125
settings = LoggingSettings()
2226
LoggingManager.setup_logging(settings, verbose=True)
23-
27+
2428
test_message = "This is a Memori info log."
2529
logger.info(test_message)
26-
30+
2731
captured = capsys.readouterr()
2832
log_output = captured.err
29-
33+
3034
assert test_message in log_output
3135
assert "INFO" in log_output
3236
assert TIMESTAMP_REGEX.search(log_output), "Log output should contain a timestamp"
3337

38+
3439
def test_standard_logger_intercepts_all_levels(capsys):
3540
"""
3641
Tests that the intercept handler correctly captures
3742
different log levels from the standard logging module.
3843
"""
3944
settings = LoggingSettings()
4045
LoggingManager.setup_logging(settings, verbose=True)
41-
46+
4247
std_logger = logging.getLogger("a_standard_test_logger")
43-
48+
4449
std_logger.debug("This is a DEBUG message.")
4550
std_logger.warning("This is a WARNING message.")
4651
std_logger.error("This is an ERROR message.")
47-
52+
4853
captured = capsys.readouterr()
4954
log_output = captured.err
50-
55+
5156
assert "This is a DEBUG message." in log_output
5257
assert "This is a WARNING message." in log_output
5358
assert "This is an ERROR message." in log_output
54-
59+
5560
assert "[a_standard_test_logger]" in log_output
56-
61+
5762
assert "DEBUG" in log_output
5863
assert "WARNING" in log_output
5964
assert "ERROR" in log_output
6065
assert "[WARNING]" not in log_output, "Log level should not be duplicated"
6166

67+
6268
def test_standard_logger_exception_handling(capsys):
6369
"""
6470
Tests that the intercept handler correctly captures
@@ -71,22 +77,19 @@ def test_standard_logger_exception_handling(capsys):
7177

7278
std_logger = logging.getLogger("an_exception_logger")
7379
test_message = "An error occurred"
74-
75-
80+
7681
try:
7782
raise ValueError("This is a test exception")
7883
except ValueError:
7984
std_logger.exception(test_message)
80-
81-
85+
8286
captured = capsys.readouterr()
8387
log_output = captured.err
84-
88+
8589
assert test_message in log_output
8690
assert "[an_exception_logger]" in log_output
8791
assert "ERROR" in log_output
8892
assert "Traceback (most recent call last)" in log_output
89-
90-
93+
9194
assert "ValueError" in log_output
92-
assert "This is a test exception" in log_output
95+
assert "This is a test exception" in log_output

tests/utils/test_validators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ def test_validator_namespace_with_valid_value():
2222

2323
def test_validator_namespace_with_long_characters():
2424
with pytest.raises(ValidationError):
25-
assert DataValidator.validate_namespace("d" * 78)
25+
assert DataValidator.validate_namespace("d" * 78)

0 commit comments

Comments
 (0)