From b61a94236117cc71bc2c585f4a2873eb0c675639 Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:08:40 +0800 Subject: [PATCH 01/11] ci: include mac and win --- .github/workflows/python-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 4bf9ef7ba..723c08aed 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -25,6 +25,8 @@ jobs: matrix: os: - "ubuntu-latest" + - "windows-latest" + - "macos-latest" python-version: - "3.10" - "3.11" @@ -51,4 +53,4 @@ jobs: poetry run ruff format --check - name: Test with pytest run: | - PYTHONPATH=src poetry run pytest tests -vv + poetry run pytest tests -vv From 6ab169866e53809fa525ec00a7e69131148bbd9c Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:33:21 +0800 Subject: [PATCH 02/11] fix: use pathlib for cross-platform path handling in cube creation test --- tests/mem_user/test_mem_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mem_user/test_mem_user.py b/tests/mem_user/test_mem_user.py index 570d92fbe..1686f59df 100644 --- a/tests/mem_user/test_mem_user.py +++ b/tests/mem_user/test_mem_user.py @@ -264,7 +264,7 @@ def test_create_cube_with_path_and_custom_id(self, user_manager): owner_id = user_manager.create_user("cube_owner", UserRole.USER) custom_cube_id = "custom_cube_123" - cube_path = "/path/to/cube" + cube_path = str(Path("/path/to/cube")) # Use pathlib for cross-platform path handling cube_id = user_manager.create_cube( "custom_cube", owner_id, cube_path=cube_path, cube_id=custom_cube_id From e0b8b7ef5e7f74fc9ab64bdc2ed208880015320d Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:31:22 +0800 Subject: [PATCH 03/11] fix: improve file path handling in test_dump using os.path.join --- tests/memories/textual/test_general.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/memories/textual/test_general.py b/tests/memories/textual/test_general.py index fefbe72df..7019b1216 100644 --- a/tests/memories/textual/test_general.py +++ b/tests/memories/textual/test_general.py @@ -1,5 +1,6 @@ # TODO: Overcomplex. Use pytest fixtures instead of setUp/tearDown. import json +import os import unittest import uuid @@ -455,9 +456,9 @@ def test_load(self): def test_dump(self): """Test dump functionality for GeneralTextMemory.""" - test_dir = "/test/directory" + test_dir = "test/directory" memory_filename = "textual_memory.json" - memory_file_path = test_dir + "/" + memory_filename + memory_file_path = os.path.join(test_dir, memory_filename) # Set the config's memory_filename self.config.memory_filename = memory_filename From 96f5f6df8882bd49d493d7f20800cf7a39a8ecf2 Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:46:00 +0800 Subject: [PATCH 04/11] fix: use a small hf model instead --- tests/mem_os/test_memos_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mem_os/test_memos_core.py b/tests/mem_os/test_memos_core.py index fa4f92f05..5677ff0ba 100644 --- a/tests/mem_os/test_memos_core.py +++ b/tests/mem_os/test_memos_core.py @@ -22,7 +22,7 @@ def mock_config(): "chat_model": { "backend": "huggingface", "config": { - "model_name_or_path": "Qwen/Qwen3-1.7B", + "model_name_or_path": "hf-internal-testing/tiny-random-gpt2", "temperature": 0.1, "remove_think_prefix": True, "max_tokens": 4096, From fccdee047440e78b0032154103d1528995cdccb8 Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:51:58 +0800 Subject: [PATCH 05/11] fix: add LLMFactory mock to test MOS initialization with invalid user --- tests/mem_os/test_memos_core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/mem_os/test_memos_core.py b/tests/mem_os/test_memos_core.py index 5677ff0ba..3623bd900 100644 --- a/tests/mem_os/test_memos_core.py +++ b/tests/mem_os/test_memos_core.py @@ -188,8 +188,10 @@ def test_mos_init_success( mock_user_manager.validate_user.assert_called_once_with("test_user") @patch("memos.mem_os.core.UserManager") - def test_mos_init_invalid_user(self, mock_user_manager_class, mock_config): + @patch("memos.mem_os.core.LLMFactory") + def test_mos_init_invalid_user(self, mock_llm_factory, mock_user_manager_class, mock_config): """Test MOS initialization with invalid user.""" + mock_llm_factory.from_config.return_value = MagicMock() mock_user_manager = MagicMock() mock_user_manager.validate_user.return_value = False mock_user_manager_class.return_value = mock_user_manager From fbda23f5d0c257ec90d60b55db4aafe995de0025 Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 18:13:02 +0800 Subject: [PATCH 06/11] fix: remove warning about platform support --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index d5be3324d..05929cea4 100644 --- a/README.md +++ b/README.md @@ -150,9 +150,6 @@ For more detailed examples, please check out the [`examples`](./examples) direct ## 📦 Installation -> [!WARNING] -> Currently, MemOS primarily supports Linux platforms. You may encounter issues on Windows and macOS temporarily. - ### Install via pip ```bash From c1a1a8d6d8230341a9304f6ffe8e0a622b2ff854 Mon Sep 17 00:00:00 2001 From: fridayL Date: Tue, 8 Jul 2025 19:05:45 +0800 Subject: [PATCH 07/11] fix:db close in windows for sqlitedb --- src/memos/mem_user/user_manager.py | 10 +++++++++ tests/mem_user/test_mem_user.py | 33 ++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/memos/mem_user/user_manager.py b/src/memos/mem_user/user_manager.py index 31d4dfc87..8e7335af0 100644 --- a/src/memos/mem_user/user_manager.py +++ b/src/memos/mem_user/user_manager.py @@ -476,3 +476,13 @@ def delete_cube(self, cube_id: str) -> bool: return False finally: session.close() + + def close(self) -> None: + """Close the database engine and dispose of all connections. + + This method should be called when the UserManager is no longer needed + to ensure proper cleanup of database connections. + """ + if hasattr(self, 'engine'): + self.engine.dispose() + logger.info("UserManager database connections closed") diff --git a/tests/mem_user/test_mem_user.py b/tests/mem_user/test_mem_user.py index 1686f59df..eda214ea2 100644 --- a/tests/mem_user/test_mem_user.py +++ b/tests/mem_user/test_mem_user.py @@ -27,15 +27,22 @@ def temp_db(self): temp_dir = tempfile.mkdtemp() db_path = os.path.join(temp_dir, "test_memos.db") yield db_path - # Cleanup - if os.path.exists(db_path): - os.remove(db_path) - os.rmdir(temp_dir) + # Cleanup - note: file cleanup is handled by user_manager fixture + try: + if os.path.exists(db_path): + os.remove(db_path) + os.rmdir(temp_dir) + except (OSError, PermissionError): + # On Windows, files might still be locked, ignore cleanup errors + pass @pytest.fixture def user_manager(self, temp_db): """Create UserManager instance with temporary database.""" - return UserManager(db_path=temp_db) + manager = UserManager(db_path=temp_db) + yield manager + # Ensure database connections are closed + manager.close() def test_initialization(self, temp_db): """Test UserManager initialization.""" @@ -93,7 +100,9 @@ def temp_db(self): @pytest.fixture def user_manager(self, temp_db): """Create UserManager instance with temporary database.""" - return UserManager(db_path=temp_db) + manager = UserManager(db_path=temp_db) + yield manager + manager.close() def test_create_user(self, user_manager): """Test user creation.""" @@ -239,7 +248,9 @@ def temp_db(self): @pytest.fixture def user_manager(self, temp_db): """Create UserManager instance with temporary database.""" - return UserManager(db_path=temp_db) + manager = UserManager(db_path=temp_db) + yield manager + manager.close() def test_create_cube(self, user_manager): """Test cube creation.""" @@ -433,7 +444,9 @@ def temp_db(self): @pytest.fixture def user_manager(self, temp_db): """Create UserManager instance with temporary database.""" - return UserManager(db_path=temp_db) + manager = UserManager(db_path=temp_db) + yield manager + manager.close() def test_user_roles(self, user_manager): """Test different user roles.""" @@ -483,7 +496,9 @@ def temp_db(self): @pytest.fixture def user_manager(self, temp_db): """Create UserManager instance with temporary database.""" - return UserManager(db_path=temp_db) + manager = UserManager(db_path=temp_db) + yield manager + manager.close() def test_cascade_delete_user_cubes(self, user_manager): """Test that user's owned cubes are handled when user is deleted.""" From 5817a29bb03493d185f4986fe8f1af3e3fc61808 Mon Sep 17 00:00:00 2001 From: fridayL Date: Tue, 8 Jul 2025 19:12:21 +0800 Subject: [PATCH 08/11] fix: fix ci --- src/memos/mem_user/user_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memos/mem_user/user_manager.py b/src/memos/mem_user/user_manager.py index 8e7335af0..e5ca73b58 100644 --- a/src/memos/mem_user/user_manager.py +++ b/src/memos/mem_user/user_manager.py @@ -479,10 +479,10 @@ def delete_cube(self, cube_id: str) -> bool: def close(self) -> None: """Close the database engine and dispose of all connections. - + This method should be called when the UserManager is no longer needed to ensure proper cleanup of database connections. """ - if hasattr(self, 'engine'): + if hasattr(self, "engine"): self.engine.dispose() logger.info("UserManager database connections closed") From caefa04377bae2af5757c37b7231adae5145180a Mon Sep 17 00:00:00 2001 From: fridayL Date: Tue, 8 Jul 2025 19:19:16 +0800 Subject: [PATCH 09/11] fix: delete db after close and get permission --- tests/mem_user/test_mem_user.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/mem_user/test_mem_user.py b/tests/mem_user/test_mem_user.py index eda214ea2..1298e2fbf 100644 --- a/tests/mem_user/test_mem_user.py +++ b/tests/mem_user/test_mem_user.py @@ -70,18 +70,27 @@ class MockSettings: # Replace the settings import monkeypatch.setattr("memos.mem_user.user_manager.settings", MockSettings()) + manager = None try: manager = UserManager() expected_path = mock_memos_dir / "memos_users.db" assert manager.db_path == str(expected_path) assert os.path.exists(expected_path) finally: + # Close database connections first + if manager: + manager.close() + # Cleanup - expected_path = mock_memos_dir / "memos_users.db" - if os.path.exists(expected_path): - os.remove(expected_path) - if os.path.exists(temp_dir): - os.rmdir(temp_dir) + try: + expected_path = mock_memos_dir / "memos_users.db" + if os.path.exists(expected_path): + os.remove(expected_path) + if os.path.exists(temp_dir): + os.rmdir(temp_dir) + except (OSError, PermissionError): + # On Windows, files might still be locked, ignore cleanup errors + pass class TestUserOperations: From 0e9830e88186ac5704ac89bced951cef6ee2a5e4 Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 23:28:38 +0800 Subject: [PATCH 10/11] ci: explicitly checks macos-14 and macos-15 --- .github/workflows/python-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 723c08aed..83ff35572 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -26,7 +26,9 @@ jobs: os: - "ubuntu-latest" - "windows-latest" - - "macos-latest" + - "macos-14" + - "macos-15" + # Ref: https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-where-your-workflow-runs/choosing-the-runner-for-a-job python-version: - "3.10" - "3.11" From 4e67244026799c0b279c3d959cded03ac703fe83 Mon Sep 17 00:00:00 2001 From: Shichao Song <60967965+Ki-Seki@users.noreply.github.com> Date: Tue, 8 Jul 2025 23:42:30 +0800 Subject: [PATCH 11/11] docs: add warning about macOS compatibility --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 87244c0d9..a54edd21a 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,13 @@ For more detailed examples, please check out the [`examples`](./examples) direct ## 📦 Installation +> [!WARNING] +> MemOS is compatible with Linux, Windows, and macOS. +> +> However, if you're using macOS, please note that there may be dependency issues that are difficult to resolve. +> +> For example, compatibility with macOS 13 Ventura is currently challenging. + ### Install via pip ```bash