basic-memory 0.4.3__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- basic_memory/__init__.py +1 -1
- basic_memory/api/app.py +7 -0
- basic_memory/api/routers/resource_router.py +1 -1
- basic_memory/cli/main.py +0 -4
- basic_memory/config.py +5 -0
- basic_memory/mcp/tools/notes.py +41 -23
- basic_memory/utils.py +35 -4
- {basic_memory-0.4.3.dist-info → basic_memory-0.6.0.dist-info}/METADATA +2 -1
- {basic_memory-0.4.3.dist-info → basic_memory-0.6.0.dist-info}/RECORD +12 -12
- {basic_memory-0.4.3.dist-info → basic_memory-0.6.0.dist-info}/WHEEL +0 -0
- {basic_memory-0.4.3.dist-info → basic_memory-0.6.0.dist-info}/entry_points.txt +0 -0
- {basic_memory-0.4.3.dist-info → basic_memory-0.6.0.dist-info}/licenses/LICENSE +0 -0
basic_memory/__init__.py
CHANGED
basic_memory/api/app.py
CHANGED
|
@@ -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
|
|
basic_memory/cli/main.py
CHANGED
|
@@ -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()
|
basic_memory/config.py
CHANGED
|
@@ -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",
|
basic_memory/mcp/tools/notes.py
CHANGED
|
@@ -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")
|
basic_memory/utils.py
CHANGED
|
@@ -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}")
|
|
@@ -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
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
basic_memory/__init__.py,sha256=
|
|
2
|
-
basic_memory/config.py,sha256=
|
|
1
|
+
basic_memory/__init__.py,sha256=dDb_uz3MKuWHLxcMY3ytbIQPv3aAHuYxKZ3jAvt2rPo,122
|
|
2
|
+
basic_memory/config.py,sha256=NGalTXjTw6OlIDdmWRygy-2jBqGfl9AuXGHy3MUdM-I,1793
|
|
3
3
|
basic_memory/db.py,sha256=IK_gz8Uiwcgxe8TjarW7kpl8cVVNtEnR0lm1LemgZ8I,5283
|
|
4
4
|
basic_memory/deps.py,sha256=UzivBw6e6iYcU_8SQ8LNCmSsmFyHfjdzfWvnfNzqbRc,5375
|
|
5
5
|
basic_memory/file_utils.py,sha256=gp7RCFWaddFnELIyTc1E19Rk8jJsrKshG2n8ZZR-kKA,5751
|
|
6
|
-
basic_memory/utils.py,sha256=
|
|
6
|
+
basic_memory/utils.py,sha256=UriwNFpuHXaWLjEQwIitb1yelzeMhkuKDnIpUh6Zfbw,3388
|
|
7
7
|
basic_memory/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
|
8
8
|
basic_memory/alembic/env.py,sha256=XqJVQhS41ba7NCPmmaSZ09_tbSLnwsY2bcpJpqx_ZTc,2107
|
|
9
9
|
basic_memory/alembic/migrations.py,sha256=CIbkMHEKZ60aDUhFGSQjv8kDNM7sazfvEYHGGcy1DBk,858
|
|
10
10
|
basic_memory/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
11
11
|
basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py,sha256=lTbWlAnd1es7xU99DoJgfaRe1_Kte8TL98riqeKGV80,4363
|
|
12
12
|
basic_memory/api/__init__.py,sha256=wCpj-21j1D0KzKl9Ql6unLBVFY0K1uGp_FeSZRKtqpk,72
|
|
13
|
-
basic_memory/api/app.py,sha256=
|
|
13
|
+
basic_memory/api/app.py,sha256=0vmDJDhKkRN1f7XDO3hqcUpXrLRmcHOH79O5x4hPtho,1573
|
|
14
14
|
basic_memory/api/routers/__init__.py,sha256=iviQ1QVYobC8huUuyRhEjcA0BDjrOUm1lXHXhJkxP9A,239
|
|
15
15
|
basic_memory/api/routers/knowledge_router.py,sha256=cMLhRczOfSRnsZdyR0bSS8PENPRTu70dlwaV27O34bs,5705
|
|
16
16
|
basic_memory/api/routers/memory_router.py,sha256=pF0GzmWoxmjhtxZM8jCmfLwqjey_fmXER5vYbD8fsQw,4556
|
|
17
|
-
basic_memory/api/routers/resource_router.py,sha256=
|
|
17
|
+
basic_memory/api/routers/resource_router.py,sha256=MoW8LEjBfNbJsp6Nt2JnE4LKe3ysiVqwgY5BMCFuPCQ,4360
|
|
18
18
|
basic_memory/api/routers/search_router.py,sha256=dCRnBbp3r966U8UYwgAaxZBbg7yX7pC8QJqagdACUi0,1086
|
|
19
19
|
basic_memory/cli/__init__.py,sha256=arcKLAWRDhPD7x5t80MlviZeYzwHZ0GZigyy3NKVoGk,33
|
|
20
20
|
basic_memory/cli/app.py,sha256=NG6gs_UzyXBiQLHbiZRZlew3nb7G7i_8gwPh1383EnA,450
|
|
21
|
-
basic_memory/cli/main.py,sha256=
|
|
21
|
+
basic_memory/cli/main.py,sha256=4RElTaSskLZvS7EAk4KbkU7inDRhoQqHcr_UR5I6lMo,423
|
|
22
22
|
basic_memory/cli/commands/__init__.py,sha256=OQGLaKTsOdPsp2INM_pHzmOlbVfdL0sytBNgvqTqCDY,159
|
|
23
23
|
basic_memory/cli/commands/db.py,sha256=XW2ujzas5j2Gf01NOPQI89L4NK-21GksO_OIekKxv6c,770
|
|
24
24
|
basic_memory/cli/commands/import_chatgpt.py,sha256=Jnqj_kswM9S-qauPCHqLiMIQMvY4PXULHZSiqVJ_veQ,8150
|
|
@@ -40,7 +40,7 @@ basic_memory/mcp/server.py,sha256=L92Vit7llaKT9NlPZfxdp67C33niObmRH2QFyUhmnD0,35
|
|
|
40
40
|
basic_memory/mcp/tools/__init__.py,sha256=MHZmWw016N0qbtC3f186Jg1tPzh2g88_ZsCKJ0oyrrs,873
|
|
41
41
|
basic_memory/mcp/tools/knowledge.py,sha256=2U8YUKCizsAETHCC1mBVKMfCEef6tlc_pa2wOmA9mD4,2016
|
|
42
42
|
basic_memory/mcp/tools/memory.py,sha256=gl4MBm9l2lMOfu_xmUqjoZacWSIHOAYZiAm8z7oDuY8,5203
|
|
43
|
-
basic_memory/mcp/tools/notes.py,sha256=
|
|
43
|
+
basic_memory/mcp/tools/notes.py,sha256=ZJGMU-14_aIQvrDA-yaLbnDoFdjIgx7SzE7PWTf7c4o,7249
|
|
44
44
|
basic_memory/mcp/tools/search.py,sha256=tx6aIuB2FWmmrvzu3RHSQvszlk-zHcwrWhkLLHWjuZc,1105
|
|
45
45
|
basic_memory/mcp/tools/utils.py,sha256=icm-Xyqw3GxooGYkXqjEjoZvIGy_Z3CPw-uUYBxR_YQ,4831
|
|
46
46
|
basic_memory/models/__init__.py,sha256=Bf0xXV_ryndogvZDiVM_Wb6iV2fHUxYNGMZNWNcZi0s,307
|
|
@@ -74,8 +74,8 @@ basic_memory/sync/file_change_scanner.py,sha256=4whJej6t9sxwUp1ox93efJ0bBHSnAr6S
|
|
|
74
74
|
basic_memory/sync/sync_service.py,sha256=nAOX4N90lbpRJeq5tRR_7PYptIoWwhXMUljE7yrneF4,7087
|
|
75
75
|
basic_memory/sync/utils.py,sha256=wz1Fe7Mb_M5N9vYRQnDKGODiMGcj5MEK16KVJ3eoQ9g,1191
|
|
76
76
|
basic_memory/sync/watch_service.py,sha256=CtKBrP1imI3ZSEgJl7Ffi-JZ_oDGKrhiyGgs41h5QYI,7563
|
|
77
|
-
basic_memory-0.
|
|
78
|
-
basic_memory-0.
|
|
79
|
-
basic_memory-0.
|
|
80
|
-
basic_memory-0.
|
|
81
|
-
basic_memory-0.
|
|
77
|
+
basic_memory-0.6.0.dist-info/METADATA,sha256=GcfzSP0T_Fr8dh6xAIYPrZKBXLOTiMyQ-9vg6EJ3JkI,10849
|
|
78
|
+
basic_memory-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
79
|
+
basic_memory-0.6.0.dist-info/entry_points.txt,sha256=IDQa_VmVTzmvMrpnjhEfM0S3F--XsVGEj3MpdJfuo-Q,59
|
|
80
|
+
basic_memory-0.6.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
81
|
+
basic_memory-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|