Skip to content

Commit 93cc637

Browse files
author
phernandez
committed
chore: formatting
2 parents a573f78 + 37a01b8 commit 93cc637

40 files changed

Lines changed: 1322 additions & 437 deletions

Makefile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,13 @@ installer-win:
4242
update-deps:
4343
uv lock f--upgrade
4444

45-
check: lint format type-check test
45+
check: lint format type-check test
46+
47+
48+
# Target for generating Alembic migrations with a message from command line
49+
migration:
50+
@if [ -z "$(m)" ]; then \
51+
echo "Usage: make migration m=\"Your migration message\""; \
52+
exit 1; \
53+
fi; \
54+
cd src/basic_memory/alembic && alembic revision --autogenerate -m "$(m)"

src/basic_memory/alembic/env.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Alembic environment configuration."""
22

3+
import os
34
from logging.config import fileConfig
45

56
from sqlalchemy import engine_from_config
@@ -8,6 +9,10 @@
89
from alembic import context
910

1011
from basic_memory.models import Base
12+
13+
# set config.env to "test" for pytest to prevent logging to file in utils.setup_logging()
14+
os.environ["BASIC_MEMORY_ENV"] = "test"
15+
1116
from basic_memory.config import config as app_config
1217

1318
# this is the Alembic Config object, which provides
@@ -18,7 +23,7 @@
1823
sqlalchemy_url = f"sqlite:///{app_config.database_path}"
1924
config.set_main_option("sqlalchemy.url", sqlalchemy_url)
2025

21-
#print(f"Using SQLAlchemy URL: {sqlalchemy_url}")
26+
# print(f"Using SQLAlchemy URL: {sqlalchemy_url}")
2227

2328
# Interpret the config file for Python logging.
2429
if config.config_file_name is not None:
@@ -29,6 +34,14 @@
2934
target_metadata = Base.metadata
3035

3136

37+
# Add this function to tell Alembic what to include/exclude
38+
def include_object(object, name, type_, reflected, compare_to):
39+
# Ignore SQLite FTS tables
40+
if type_ == "table" and name.startswith("search_index"):
41+
return False
42+
return True
43+
44+
3245
def run_migrations_offline() -> None:
3346
"""Run migrations in 'offline' mode.
3447
@@ -46,6 +59,8 @@ def run_migrations_offline() -> None:
4659
target_metadata=target_metadata,
4760
literal_binds=True,
4861
dialect_opts={"paramstyle": "named"},
62+
include_object=include_object,
63+
render_as_batch=True,
4964
)
5065

5166
with context.begin_transaction():
@@ -65,7 +80,12 @@ def run_migrations_online() -> None:
6580
)
6681

6782
with connectable.connect() as connection:
68-
context.configure(connection=connection, target_metadata=target_metadata)
83+
context.configure(
84+
connection=connection,
85+
target_metadata=target_metadata,
86+
include_object=include_object,
87+
render_as_batch=True,
88+
)
6989

7090
with context.begin_transaction():
7191
context.run_migrations()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""remove required from entity.permalink
2+
3+
Revision ID: 502b60eaa905
4+
Revises: b3c3938bacdb
5+
Create Date: 2025-02-24 13:33:09.790951
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
from alembic import op
12+
import sqlalchemy as sa
13+
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "502b60eaa905"
17+
down_revision: Union[str, None] = "b3c3938bacdb"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
# ### commands auto generated by Alembic - please adjust! ###
24+
with op.batch_alter_table("entity", schema=None) as batch_op:
25+
batch_op.alter_column("permalink", existing_type=sa.VARCHAR(), nullable=True)
26+
batch_op.drop_index("ix_entity_permalink")
27+
batch_op.create_index(batch_op.f("ix_entity_permalink"), ["permalink"], unique=False)
28+
batch_op.drop_constraint("uix_entity_permalink", type_="unique")
29+
batch_op.create_index(
30+
"uix_entity_permalink",
31+
["permalink"],
32+
unique=True,
33+
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
34+
)
35+
36+
# ### end Alembic commands ###
37+
38+
39+
def downgrade() -> None:
40+
# ### commands auto generated by Alembic - please adjust! ###
41+
with op.batch_alter_table("entity", schema=None) as batch_op:
42+
batch_op.drop_index(
43+
"uix_entity_permalink",
44+
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
45+
)
46+
batch_op.create_unique_constraint("uix_entity_permalink", ["permalink"])
47+
batch_op.drop_index(batch_op.f("ix_entity_permalink"))
48+
batch_op.create_index("ix_entity_permalink", ["permalink"], unique=1)
49+
batch_op.alter_column("permalink", existing_type=sa.VARCHAR(), nullable=False)
50+
51+
# ### end Alembic commands ###

src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,40 @@
55
Create Date: 2025-02-22 14:59:30.668466
66
77
"""
8+
89
from typing import Sequence, Union
910

1011
from alembic import op
11-
import sqlalchemy as sa
12-
from alembic.context import get_context
1312

1413

1514
# revision identifiers, used by Alembic.
16-
revision: str = 'b3c3938bacdb'
17-
down_revision: Union[str, None] = '3dae7c7b1564'
15+
revision: str = "b3c3938bacdb"
16+
down_revision: Union[str, None] = "3dae7c7b1564"
1817
branch_labels: Union[str, Sequence[str], None] = None
1918
depends_on: Union[str, Sequence[str], None] = None
2019

2120

2221
def upgrade() -> None:
2322
# SQLite doesn't support constraint changes through ALTER
2423
# Need to recreate table with desired constraints
25-
with op.batch_alter_table('relation') as batch_op:
24+
with op.batch_alter_table("relation") as batch_op:
2625
# Drop existing unique constraint
27-
batch_op.drop_constraint('uix_relation', type_='unique')
28-
26+
batch_op.drop_constraint("uix_relation", type_="unique")
27+
2928
# Add new constraints
3029
batch_op.create_unique_constraint(
31-
'uix_relation_from_id_to_id',
32-
['from_id', 'to_id', 'relation_type']
30+
"uix_relation_from_id_to_id", ["from_id", "to_id", "relation_type"]
3331
)
3432
batch_op.create_unique_constraint(
35-
'uix_relation_from_id_to_name',
36-
['from_id', 'to_name', 'relation_type']
33+
"uix_relation_from_id_to_name", ["from_id", "to_name", "relation_type"]
3734
)
3835

3936

4037
def downgrade() -> None:
41-
with op.batch_alter_table('relation') as batch_op:
38+
with op.batch_alter_table("relation") as batch_op:
4239
# Drop new constraints
43-
batch_op.drop_constraint('uix_relation_from_id_to_name', type_='unique')
44-
batch_op.drop_constraint('uix_relation_from_id_to_id', type_='unique')
45-
40+
batch_op.drop_constraint("uix_relation_from_id_to_name", type_="unique")
41+
batch_op.drop_constraint("uix_relation_from_id_to_id", type_="unique")
42+
4643
# Restore original constraint
47-
batch_op.create_unique_constraint(
48-
'uix_relation',
49-
['from_id', 'to_id', 'relation_type']
50-
)
44+
batch_op.create_unique_constraint("uix_relation", ["from_id", "to_id", "relation_type"])

src/basic_memory/api/routers/knowledge_router.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ async def delete_entity(
133133
return DeleteEntitiesResponse(deleted=False)
134134

135135
# Delete the entity
136-
deleted = await entity_service.delete_entity(entity.permalink)
136+
deleted = await entity_service.delete_entity(entity.permalink or entity.id)
137137

138138
# Remove from search index
139139
background_tasks.add_task(search_service.delete_by_permalink, entity.permalink)

src/basic_memory/api/routers/memory_router.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,19 @@ async def to_summary(item: SearchIndexRow | ContextResultRow):
4141
case SearchItemType.OBSERVATION:
4242
assert item.category is not None
4343
assert item.content is not None
44+
assert item.permalink is not None
4445

4546
return ObservationSummary(
4647
category=item.category, content=item.content, permalink=item.permalink
4748
)
4849
case SearchItemType.RELATION:
4950
assert item.from_id is not None
51+
assert item.permalink is not None
5052
from_entity = await entity_repository.find_by_id(item.from_id)
5153
assert from_entity is not None
54+
assert from_entity.permalink is not None
5255

5356
to_entity = await entity_repository.find_by_id(item.to_id) if item.to_id else None
54-
5557
return RelationSummary(
5658
permalink=item.permalink,
5759
relation_type=item.type,
@@ -104,9 +106,11 @@ async def recent(
104106
context = await context_service.build_context(
105107
types=types, depth=depth, since=since, limit=limit, offset=offset, max_related=max_related
106108
)
107-
return await to_graph_context(
109+
recent_context = await to_graph_context(
108110
context, entity_repository=entity_repository, page=page, page_size=page_size
109111
)
112+
logger.debug(f"Recent context: {recent_context.model_dump_json()}")
113+
return recent_context
110114

111115

112116
# get_memory_context needs to be declared last so other paths can match

src/basic_memory/cli/commands/mcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ def mcp(): # pragma: no cover
1717
home_dir = config.home
1818
logger.info(f"Starting Basic Memory MCP server {basic_memory.__version__}")
1919
logger.info(f"Home directory: {home_dir}")
20-
mcp_server.run()
20+
mcp_server.run()

src/basic_memory/cli/commands/status.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,4 @@ def status(
141141
except Exception as e:
142142
logger.exception(f"Error checking status: {e}")
143143
typer.echo(f"Error checking status: {e}", err=True)
144-
raise typer.Exit(code=1) # pragma: no cover
144+
raise typer.Exit(code=1) # pragma: no cover

src/basic_memory/cli/commands/sync.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,10 @@ async def run_sync(verbose: bool = False, watch: bool = False, console_status: b
160160
file_service=sync_service.entity_service.file_service,
161161
config=config,
162162
)
163-
await watch_service.handle_changes(config.home)
164-
await watch_service.run(console_status=console_status) # pragma: no cover
163+
# full sync
164+
await sync_service.sync(config.home)
165+
# watch changes
166+
await watch_service.run() # pragma: no cover
165167
else:
166168
# one time sync
167169
knowledge_changes = await sync_service.sync(config.home)
@@ -186,18 +188,15 @@ def sync(
186188
"-w",
187189
help="Start watching for changes after sync.",
188190
),
189-
console_status: bool = typer.Option(
190-
False, "--console-status", "-c", help="Show live console status"
191-
),
192191
) -> None:
193192
"""Sync knowledge files with the database."""
194193
try:
195194
# Run sync
196-
asyncio.run(run_sync(verbose=verbose, watch=watch, console_status=console_status))
195+
asyncio.run(run_sync(verbose=verbose, watch=watch))
197196

198197
except Exception as e: # pragma: no cover
199198
if not isinstance(e, typer.Exit):
200199
logger.exception("Sync failed")
201200
typer.echo(f"Error during sync: {e}", err=True)
202201
raise typer.Exit(1)
203-
raise
202+
raise

src/basic_memory/cli/commands/tools.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from basic_memory.cli.app import app
1010
from basic_memory.mcp.tools import build_context as mcp_build_context
1111
from basic_memory.mcp.tools import get_entity as mcp_get_entity
12-
from basic_memory.mcp.tools import read_resource as mcp_read_resource
1312
from basic_memory.mcp.tools import read_note as mcp_read_note
1413
from basic_memory.mcp.tools import recent_activity as mcp_recent_activity
1514
from basic_memory.mcp.tools import search as mcp_search
@@ -156,14 +155,3 @@ def get_entity(identifier: str):
156155
typer.echo(f"Error during get_entity: {e}", err=True)
157156
raise typer.Exit(1)
158157
raise
159-
160-
@tool_app.command()
161-
def read_resource(identifier: str):
162-
try:
163-
entity = asyncio.run(read_resource(identifier=identifier))
164-
rprint(entity.model_dump_json(indent=2))
165-
except Exception as e: # pragma: no cover
166-
if not isinstance(e, typer.Exit):
167-
typer.echo(f"Error during get_entity: {e}", err=True)
168-
raise typer.Exit(1)
169-
raise

0 commit comments

Comments
 (0)