basic-memory 0.4.3__tar.gz → 0.6.0__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 basic-memory might be problematic. Click here for more details.
- {basic_memory-0.4.3 → basic_memory-0.6.0}/CHANGELOG.md +21 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/Makefile +2 -2
- {basic_memory-0.4.3 → basic_memory-0.6.0}/PKG-INFO +2 -1
- {basic_memory-0.4.3 → basic_memory-0.6.0}/installer/installer.py +8 -1
- {basic_memory-0.4.3 → basic_memory-0.6.0}/pyproject.toml +2 -1
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/__init__.py +1 -1
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/app.py +7 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/routers/resource_router.py +1 -1
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/main.py +0 -4
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/config.py +5 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/tools/notes.py +41 -23
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/utils.py +35 -4
- basic_memory-0.6.0/tests/__init__.py +4 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/api/test_resource_router.py +0 -1
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/mcp/test_tool_knowledge.py +11 -10
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/mcp/test_tool_notes.py +129 -66
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/mcp/test_tool_search.py +3 -2
- basic_memory-0.6.0/tests/test_basic_memory.py +47 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/uv.lock +384 -1
- basic_memory-0.4.3/tests/mcp/test_tool_get_entity.py +0 -45
- basic_memory-0.4.3/tests/test_basic_memory.py +0 -8
- {basic_memory-0.4.3 → basic_memory-0.6.0}/.github/workflows/pr-title.yml +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/.github/workflows/release.yml +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/.github/workflows/test.yml +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/.gitignore +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/.python-version +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/CITATION.cff +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/CODE_OF_CONDUCT.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/CONTRIBUTING.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/LICENSE +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/README.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/basic-memory.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/installer/Basic.icns +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/installer/README.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/installer/icon.svg +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/installer/make_icons.sh +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/installer/setup.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/memory.json +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/scripts/install.sh +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/alembic/README +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/alembic/env.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/alembic/migrations.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/alembic/script.py.mako +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/routers/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/routers/knowledge_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/routers/memory_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/api/routers/search_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/app.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/db.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/import_chatgpt.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/import_claude_conversations.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/import_claude_projects.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/import_memory_json.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/mcp.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/status.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/cli/commands/sync.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/db.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/deps.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/file_utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/markdown/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/markdown/entity_parser.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/markdown/markdown_processor.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/markdown/plugins.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/markdown/schemas.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/markdown/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/async_client.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/server.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/tools/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/tools/knowledge.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/tools/memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/tools/search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/mcp/tools/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/models/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/models/base.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/models/knowledge.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/models/search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/repository/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/repository/entity_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/repository/observation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/repository/relation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/repository/repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/repository/search_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/base.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/delete.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/discovery.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/request.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/response.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/schemas/search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/context_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/entity_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/exceptions.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/file_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/link_resolver.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/search_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/services/service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/sync/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/sync/file_change_scanner.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/sync/sync_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/sync/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/src/basic_memory/sync/watch_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/api/conftest.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/api/test_knowledge_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/api/test_memory_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/api/test_search_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/cli/test_import_chatgpt.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/cli/test_import_claude_conversations.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/cli/test_import_claude_projects.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/cli/test_import_memory_json.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/cli/test_status.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/cli/test_sync.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/conftest.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/edit_file_test.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_entity_parser.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_markdown_plugins.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_markdown_processor.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_observation_edge_cases.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_parser_edge_cases.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_relation_edge_cases.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/markdown/test_task_detection.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/mcp/conftest.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/mcp/test_tool_memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/mcp/test_tool_utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/repository/test_entity_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/repository/test_observation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/repository/test_relation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/repository/test_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/schemas/test_memory_url.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/schemas/test_schemas.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/schemas/test_search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/services/test_context_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/services/test_entity_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/services/test_file_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/services/test_link_resolver.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/services/test_search_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/sync/test_file_change_scanner.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/sync/test_sync_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/sync/test_watch_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/utils/test_file_utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.6.0}/tests/utils/test_permalink_formatting.py +0 -0
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.6.0 (2025-02-18)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
- Configure logfire telemetry ([#12](https://github.com/basicmachines-co/basic-memory/pull/12),
|
|
9
|
+
[`6da1438`](https://github.com/basicmachines-co/basic-memory/commit/6da143898bd45cdab8db95b5f2b75810fbb741ba))
|
|
10
|
+
|
|
11
|
+
Co-authored-by: phernandez <phernandez@basicmachines.co>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## v0.5.0 (2025-02-18)
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- Return semantic info in markdown after write_note
|
|
19
|
+
([#11](https://github.com/basicmachines-co/basic-memory/pull/11),
|
|
20
|
+
[`0689e7a`](https://github.com/basicmachines-co/basic-memory/commit/0689e7a730497827bf4e16156ae402ddc5949077))
|
|
21
|
+
|
|
22
|
+
Co-authored-by: phernandez <phernandez@basicmachines.co>
|
|
23
|
+
|
|
24
|
+
|
|
4
25
|
## v0.4.3 (2025-02-18)
|
|
5
26
|
|
|
6
27
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-memory
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
5
|
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
@@ -15,6 +15,7 @@ Requires-Dist: dateparser>=1.2.0
|
|
|
15
15
|
Requires-Dist: fastapi[standard]>=0.115.8
|
|
16
16
|
Requires-Dist: greenlet>=3.1.1
|
|
17
17
|
Requires-Dist: icecream>=2.1.3
|
|
18
|
+
Requires-Dist: logfire[fastapi,sqlalchemy,sqlite3]>=3.6.0
|
|
18
19
|
Requires-Dist: loguru>=0.7.3
|
|
19
20
|
Requires-Dist: markdown-it-py>=3.0.0
|
|
20
21
|
Requires-Dist: mcp>=1.2.0
|
|
@@ -49,7 +49,14 @@ def update_claude_config():
|
|
|
49
49
|
config = {"mcpServers": {}}
|
|
50
50
|
|
|
51
51
|
# Add/update basic-memory config
|
|
52
|
-
config["mcpServers"]["basic-memory"] = {
|
|
52
|
+
config["mcpServers"]["basic-memory"] = {
|
|
53
|
+
"command": "uvx",
|
|
54
|
+
"args": ["basic-memory@latest", "mcp"],
|
|
55
|
+
"env": {
|
|
56
|
+
"BASIC_MEMORY_ENV": "user",
|
|
57
|
+
"LOGFIRE_TOKEN": "n2Fpvn34LjKYq8TdF1ZrXMgdBPXGn4HfXy6tYghZ55dB",
|
|
58
|
+
},
|
|
59
|
+
}
|
|
53
60
|
|
|
54
61
|
# Write back config
|
|
55
62
|
config_path.write_text(json.dumps(config, indent=2))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "basic-memory"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "Local-first knowledge management combining Zettelkasten with knowledge graphs"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12.1"
|
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
|
29
29
|
"fastapi[standard]>=0.115.8",
|
|
30
30
|
"alembic>=1.14.1",
|
|
31
31
|
"qasync>=0.27.1",
|
|
32
|
+
"logfire[fastapi,sqlalchemy,sqlite3]>=3.6.0",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from contextlib import asynccontextmanager
|
|
4
4
|
|
|
5
|
+
import logfire
|
|
5
6
|
from fastapi import FastAPI, HTTPException
|
|
6
7
|
from fastapi.exception_handlers import http_exception_handler
|
|
7
8
|
from loguru import logger
|
|
@@ -10,11 +11,13 @@ import basic_memory
|
|
|
10
11
|
from basic_memory import db
|
|
11
12
|
from basic_memory.config import config as app_config
|
|
12
13
|
from basic_memory.api.routers import knowledge, search, memory, resource
|
|
14
|
+
from basic_memory.utils import setup_logging
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
@asynccontextmanager
|
|
16
18
|
async def lifespan(app: FastAPI): # pragma: no cover
|
|
17
19
|
"""Lifecycle manager for the FastAPI app."""
|
|
20
|
+
setup_logging(log_file=".basic-memory/basic-memory.log")
|
|
18
21
|
logger.info(f"Starting Basic Memory API {basic_memory.__version__}")
|
|
19
22
|
await db.run_migrations(app_config)
|
|
20
23
|
yield
|
|
@@ -30,6 +33,10 @@ app = FastAPI(
|
|
|
30
33
|
lifespan=lifespan,
|
|
31
34
|
)
|
|
32
35
|
|
|
36
|
+
if app_config != "test":
|
|
37
|
+
logfire.instrument_fastapi(app)
|
|
38
|
+
|
|
39
|
+
|
|
33
40
|
# Include routers
|
|
34
41
|
app.include_router(knowledge.router)
|
|
35
42
|
app.include_router(search.router)
|
|
@@ -31,7 +31,7 @@ def get_entity_ids(item: SearchIndexRow) -> list[int]:
|
|
|
31
31
|
from_entity = item.from_id
|
|
32
32
|
to_entity = item.to_id # pyright: ignore [reportReturnType]
|
|
33
33
|
return [from_entity, to_entity] if to_entity else [from_entity] # pyright: ignore [reportReturnType]
|
|
34
|
-
case _:
|
|
34
|
+
case _: # pragma: no cover
|
|
35
35
|
raise ValueError(f"Unexpected type: {item.type}")
|
|
36
36
|
|
|
37
37
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Main CLI entry point for basic-memory.""" # pragma: no cover
|
|
2
2
|
|
|
3
3
|
from basic_memory.cli.app import app # pragma: no cover
|
|
4
|
-
from basic_memory.utils import setup_logging # pragma: no cover
|
|
5
4
|
|
|
6
5
|
# Register commands
|
|
7
6
|
from basic_memory.cli.commands import ( # noqa: F401 # pragma: no cover
|
|
@@ -16,8 +15,5 @@ from basic_memory.cli.commands import ( # noqa: F401 # pragma: no cover
|
|
|
16
15
|
)
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
# Set up logging when module is imported
|
|
20
|
-
setup_logging(log_file=".basic-memory/basic-memory-cli.log") # pragma: no cover
|
|
21
|
-
|
|
22
18
|
if __name__ == "__main__": # pragma: no cover
|
|
23
19
|
app()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Configuration management for basic-memory."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
4
5
|
|
|
5
6
|
from pydantic import Field, field_validator
|
|
6
7
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
@@ -8,10 +9,14 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
8
9
|
DATABASE_NAME = "memory.db"
|
|
9
10
|
DATA_DIR_NAME = ".basic-memory"
|
|
10
11
|
|
|
12
|
+
Environment = Literal["test", "dev", "prod"]
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
class ProjectConfig(BaseSettings):
|
|
13
16
|
"""Configuration for a specific basic-memory project."""
|
|
14
17
|
|
|
18
|
+
env: Environment = Field(default="dev", description="Environment name")
|
|
19
|
+
|
|
15
20
|
# Default to ~/basic-memory but allow override with env var: BASIC_MEMORY_HOME
|
|
16
21
|
home: Path = Field(
|
|
17
22
|
default_factory=lambda: Path.home() / "basic-memory",
|
|
@@ -17,15 +17,14 @@ from basic_memory.schemas.memory import memory_url_path
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@mcp.tool(
|
|
20
|
-
description="Create or update a markdown note. Returns the
|
|
20
|
+
description="Create or update a markdown note. Returns a markdown formatted summary of the semantic content.",
|
|
21
21
|
)
|
|
22
22
|
async def write_note(
|
|
23
23
|
title: str,
|
|
24
24
|
content: str,
|
|
25
25
|
folder: str,
|
|
26
26
|
tags: Optional[List[str]] = None,
|
|
27
|
-
|
|
28
|
-
) -> EntityResponse | str:
|
|
27
|
+
) -> str:
|
|
29
28
|
"""Write a markdown note to the knowledge base.
|
|
30
29
|
|
|
31
30
|
The content can include semantic observations and relations using markdown syntax.
|
|
@@ -53,14 +52,16 @@ async def write_note(
|
|
|
53
52
|
content: Markdown content for the note, can include observations and relations
|
|
54
53
|
folder: the folder where the file should be saved
|
|
55
54
|
tags: Optional list of tags to categorize the note
|
|
56
|
-
verbose: If True, returns full EntityResponse with semantic info
|
|
57
55
|
|
|
58
56
|
Returns:
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
A markdown formatted summary of the semantic content, including:
|
|
58
|
+
- Creation/update status
|
|
59
|
+
- File path and checksum
|
|
60
|
+
- Observation counts by category
|
|
61
|
+
- Relation counts (resolved/unresolved)
|
|
62
|
+
- Tags if present
|
|
61
63
|
|
|
62
64
|
Examples:
|
|
63
|
-
# Note with both explicit and inline relations
|
|
64
65
|
write_note(
|
|
65
66
|
title="Search Implementation",
|
|
66
67
|
content="# Search Component\\n\\n"
|
|
@@ -73,20 +74,6 @@ async def write_note(
|
|
|
73
74
|
"- depends_on [[Database Schema]]",
|
|
74
75
|
folder="docs/components"
|
|
75
76
|
)
|
|
76
|
-
|
|
77
|
-
# Note with tags
|
|
78
|
-
write_note(
|
|
79
|
-
title="Error Handling Design",
|
|
80
|
-
content="# Error Handling\\n\\n"
|
|
81
|
-
"This design builds on [[Reliability Design]].\\n\\n"
|
|
82
|
-
"## Approach\\n"
|
|
83
|
-
"- [design] Use error codes #architecture\\n"
|
|
84
|
-
"- [tech] Implement retry logic #implementation\\n\\n"
|
|
85
|
-
"## Relations\\n"
|
|
86
|
-
"- extends [[Base Error Handling]]",
|
|
87
|
-
folder="docs/design",
|
|
88
|
-
tags=["architecture", "reliability"]
|
|
89
|
-
)
|
|
90
77
|
"""
|
|
91
78
|
logger.info(f"Writing note folder:'{folder}' title: '{title}'")
|
|
92
79
|
|
|
@@ -101,12 +88,43 @@ async def write_note(
|
|
|
101
88
|
entity_metadata=metadata,
|
|
102
89
|
)
|
|
103
90
|
|
|
104
|
-
#
|
|
91
|
+
# Create or update via knowledge API
|
|
105
92
|
logger.info(f"Creating {entity.permalink}")
|
|
106
93
|
url = f"/knowledge/entities/{entity.permalink}"
|
|
107
94
|
response = await call_put(client, url, json=entity.model_dump())
|
|
108
95
|
result = EntityResponse.model_validate(response.json())
|
|
109
|
-
|
|
96
|
+
|
|
97
|
+
# Format semantic summary based on status code
|
|
98
|
+
action = "Created" if response.status_code == 201 else "Updated"
|
|
99
|
+
assert result.checksum is not None
|
|
100
|
+
summary = [
|
|
101
|
+
f"# {action} {result.file_path} ({result.checksum[:8]})",
|
|
102
|
+
f"permalink: {result.permalink}",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
if result.observations:
|
|
106
|
+
categories = {}
|
|
107
|
+
for obs in result.observations:
|
|
108
|
+
categories[obs.category] = categories.get(obs.category, 0) + 1
|
|
109
|
+
|
|
110
|
+
summary.append("\n## Observations")
|
|
111
|
+
for category, count in sorted(categories.items()):
|
|
112
|
+
summary.append(f"- {category}: {count}")
|
|
113
|
+
|
|
114
|
+
if result.relations:
|
|
115
|
+
unresolved = sum(1 for r in result.relations if not r.to_id)
|
|
116
|
+
resolved = len(result.relations) - unresolved
|
|
117
|
+
|
|
118
|
+
summary.append("\n## Relations")
|
|
119
|
+
summary.append(f"- Resolved: {resolved}")
|
|
120
|
+
if unresolved:
|
|
121
|
+
summary.append(f"- Unresolved: {unresolved}")
|
|
122
|
+
summary.append("\nUnresolved relations will be retried on next sync.")
|
|
123
|
+
|
|
124
|
+
if tags:
|
|
125
|
+
summary.append(f"\n## Tags\n- {', '.join(tags)}")
|
|
126
|
+
|
|
127
|
+
return "\n".join(summary)
|
|
110
128
|
|
|
111
129
|
|
|
112
130
|
@mcp.tool(description="Read note content by title, permalink, relation, or pattern")
|
|
@@ -9,8 +9,11 @@ from typing import Optional, Union
|
|
|
9
9
|
from loguru import logger
|
|
10
10
|
from unidecode import unidecode
|
|
11
11
|
|
|
12
|
+
import basic_memory
|
|
12
13
|
from basic_memory.config import config
|
|
13
14
|
|
|
15
|
+
import logfire
|
|
16
|
+
|
|
14
17
|
|
|
15
18
|
def generate_permalink(file_path: Union[Path, str]) -> str:
|
|
16
19
|
"""Generate a stable permalink from a file path.
|
|
@@ -61,19 +64,45 @@ def generate_permalink(file_path: Union[Path, str]) -> str:
|
|
|
61
64
|
return "/".join(clean_segments)
|
|
62
65
|
|
|
63
66
|
|
|
64
|
-
def setup_logging(
|
|
67
|
+
def setup_logging(
|
|
68
|
+
home_dir: Path = config.home, log_file: Optional[str] = None
|
|
69
|
+
) -> None: # pragma: no cover
|
|
65
70
|
"""
|
|
66
71
|
Configure logging for the application.
|
|
72
|
+
:param home_dir: the root directory for the application
|
|
73
|
+
:param log_file: the name of the log file to write to
|
|
74
|
+
:param app: the fastapi application instance
|
|
67
75
|
"""
|
|
68
76
|
|
|
69
77
|
# Remove default handler and any existing handlers
|
|
70
78
|
logger.remove()
|
|
71
79
|
|
|
72
|
-
# Add file handler
|
|
73
|
-
if log_file:
|
|
80
|
+
# Add file handler if we are not running tests
|
|
81
|
+
if log_file and config.env != "test":
|
|
82
|
+
# enable pydantic logfire
|
|
83
|
+
logfire.configure(
|
|
84
|
+
code_source=logfire.CodeSource(
|
|
85
|
+
repository="https://github.com/basicmachines-co/basic-memory",
|
|
86
|
+
revision=basic_memory.__version__,
|
|
87
|
+
root_path="/src/basic_memory",
|
|
88
|
+
),
|
|
89
|
+
environment=config.env,
|
|
90
|
+
)
|
|
91
|
+
logger.configure(handlers=[logfire.loguru_handler()])
|
|
92
|
+
|
|
93
|
+
# instrument code spans
|
|
94
|
+
logfire.instrument_sqlite3()
|
|
95
|
+
logfire.instrument_pydantic()
|
|
96
|
+
|
|
97
|
+
from basic_memory.db import _engine as engine
|
|
98
|
+
|
|
99
|
+
if engine:
|
|
100
|
+
logfire.instrument_sqlalchemy(engine=engine)
|
|
101
|
+
|
|
102
|
+
# setup logger
|
|
74
103
|
log_path = home_dir / log_file
|
|
75
104
|
logger.add(
|
|
76
|
-
str(log_path),
|
|
105
|
+
str(log_path),
|
|
77
106
|
level=config.log_level,
|
|
78
107
|
rotation="100 MB",
|
|
79
108
|
retention="10 days",
|
|
@@ -85,3 +114,5 @@ def setup_logging(home_dir: Path = config.home, log_file: Optional[str] = None)
|
|
|
85
114
|
|
|
86
115
|
# Add stderr handler
|
|
87
116
|
logger.add(sys.stderr, level=config.log_level, backtrace=True, diagnose=True, colorize=True)
|
|
117
|
+
|
|
118
|
+
logger.info(f"ENV: '{config.env}' Log level: '{config.log_level}' Logging to {log_file}")
|
|
@@ -158,7 +158,6 @@ async def test_get_resource_entities(client, test_config, entity_repository):
|
|
|
158
158
|
entity2 = EntityResponse(**entity_response)
|
|
159
159
|
|
|
160
160
|
assert len(entity2.relations) == 1
|
|
161
|
-
relation = entity2.relations[0]
|
|
162
161
|
|
|
163
162
|
# Test getting the content via the relation
|
|
164
163
|
response = await client.get("/resource/test/*")
|
|
@@ -13,7 +13,7 @@ from basic_memory.schemas.delete import DeleteEntitiesRequest
|
|
|
13
13
|
async def test_get_single_entity(client):
|
|
14
14
|
"""Test retrieving a single entity."""
|
|
15
15
|
# First create an entity
|
|
16
|
-
|
|
16
|
+
result = await notes.write_note(
|
|
17
17
|
title="Test Note",
|
|
18
18
|
folder="test",
|
|
19
19
|
content="""
|
|
@@ -22,9 +22,10 @@ async def test_get_single_entity(client):
|
|
|
22
22
|
""",
|
|
23
23
|
tags=["test", "documentation"],
|
|
24
24
|
)
|
|
25
|
+
assert result
|
|
25
26
|
|
|
26
27
|
# Get the entity
|
|
27
|
-
entity = await get_entity(
|
|
28
|
+
entity = await get_entity("test/test-note")
|
|
28
29
|
|
|
29
30
|
# Verify entity details
|
|
30
31
|
assert entity.title == "Test Note"
|
|
@@ -36,40 +37,40 @@ async def test_get_single_entity(client):
|
|
|
36
37
|
async def test_get_multiple_entities(client):
|
|
37
38
|
"""Test retrieving multiple entities."""
|
|
38
39
|
# Create two test entities
|
|
39
|
-
|
|
40
|
+
await notes.write_note(
|
|
40
41
|
title="Test Note 1",
|
|
41
42
|
folder="test",
|
|
42
43
|
content="# Test 1",
|
|
43
44
|
)
|
|
44
|
-
|
|
45
|
+
await notes.write_note(
|
|
45
46
|
title="Test Note 2",
|
|
46
47
|
folder="test",
|
|
47
48
|
content="# Test 2",
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
# Get both entities
|
|
51
|
-
request = GetEntitiesRequest(permalinks=[
|
|
52
|
+
request = GetEntitiesRequest(permalinks=["test/test-note-1", "test/test-note-2"])
|
|
52
53
|
response = await get_entities(request)
|
|
53
54
|
|
|
54
55
|
# Verify we got both entities
|
|
55
56
|
assert len(response.entities) == 2
|
|
56
57
|
permalinks = {e.permalink for e in response.entities}
|
|
57
|
-
assert
|
|
58
|
-
assert
|
|
58
|
+
assert "test/test-note-1" in permalinks
|
|
59
|
+
assert "test/test-note-2" in permalinks
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
@pytest.mark.asyncio
|
|
62
63
|
async def test_delete_entities(client):
|
|
63
64
|
"""Test deleting entities."""
|
|
64
65
|
# Create a test entity
|
|
65
|
-
|
|
66
|
+
await notes.write_note(
|
|
66
67
|
title="Test Note",
|
|
67
68
|
folder="test",
|
|
68
69
|
content="# Test Note to Delete",
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
# Delete the entity
|
|
72
|
-
request = DeleteEntitiesRequest(permalinks=[
|
|
73
|
+
request = DeleteEntitiesRequest(permalinks=["test/test-note"])
|
|
73
74
|
response = await delete_entities(request)
|
|
74
75
|
|
|
75
76
|
# Verify deletion
|
|
@@ -77,7 +78,7 @@ async def test_delete_entities(client):
|
|
|
77
78
|
|
|
78
79
|
# Verify entity no longer exists
|
|
79
80
|
with pytest.raises(ToolError):
|
|
80
|
-
await get_entity(
|
|
81
|
+
await get_entity("test/test-note")
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
@pytest.mark.asyncio
|