Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 0 additions & 162 deletions src/basic_memory/mcp/formatting.py

This file was deleted.

43 changes: 0 additions & 43 deletions src/basic_memory/mcp/tools/move_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,49 +148,6 @@ def _format_cross_project_error_response(
""").strip()


def _format_potential_cross_project_guidance(
identifier: str, destination_path: str, current_project: str, available_projects: list[str]
) -> str:
"""Format guidance for potentially cross-project moves."""
other_projects = ", ".join(available_projects[:3]) # Show first 3 projects # pragma: no cover
if len(available_projects) > 3: # pragma: no cover
other_projects += f" (and {len(available_projects) - 3} others)" # pragma: no cover

return ( # pragma: no cover
dedent(f"""
# Move Failed - Check Project Context

Cannot move '{identifier}' to '{destination_path}' within the current project '{current_project}'.

## If you intended to move within the current project:
The destination path should be relative to the project root:
```
move_note("{identifier}", "folder/filename.md")
```

## If you intended to move to a different project:
Cross-project moves require switching projects first. Available projects: {other_projects}

### To move to another project:
```
# 1. Read the content
read_note("{identifier}")

# 2. Create note in target project
write_note("Title", "content", "folder", project="target-project-name")

# 3. Delete original if desired
delete_note("{identifier}", project="{current_project}")
```

### To see all projects:
```
list_memory_projects()
```
""").strip()
)


def _format_move_error_response(error_message: str, identifier: str, destination_path: str) -> str:
"""Format helpful error responses for move failures that guide users to successful moves."""

Expand Down
8 changes: 6 additions & 2 deletions src/basic_memory/mcp/tools/recent_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,12 @@ async def recent_activity(
return _extract_recent_rows(activity_data)

# Format project-specific mode output
return _format_project_output(active_project.name, activity_data, timeframe, type, page)
return _format_project_output(
active_project.name,
activity_data,
timeframe,
page=page,
)


async def _get_project_activity(
Expand Down Expand Up @@ -480,7 +485,6 @@ def _format_project_output(
project_name: str,
activity_data: GraphContext,
timeframe: str,
type_filter: Union[str, List[str]],
page: int = 1,
) -> str:
"""Format project-specific mode output as human-readable text."""
Expand Down
58 changes: 0 additions & 58 deletions src/basic_memory/repository/search_repository_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
VectorChunkRecord,
build_entity_fingerprint,
build_vector_chunk_records,
compose_row_source_text,
split_text_into_chunks,
)
from basic_memory.repository.semantic_errors import (
Expand Down Expand Up @@ -424,15 +423,6 @@ def _assert_semantic_available(self) -> None:
"and set semantic_search_enabled=true."
)

def _compose_row_source_text(self, row: SemanticSourceRow) -> str:
"""Build the text blob that will be chunked and embedded for one search_index row.

For entity rows we use title, permalink, and content_snippet (the actual
human-readable content). content_stems is an FTS-optimised variant that
includes word-boundary expansions and would dilute embedding quality.
"""
return compose_row_source_text(row)

def _build_chunk_records(self, rows: Iterable[SemanticSourceRow]) -> list[VectorChunkRecord]:
chunk_build = build_vector_chunk_records(rows)
if chunk_build.duplicate_chunk_keys:
Expand Down Expand Up @@ -584,28 +574,6 @@ async def _prepare_entity_vector_jobs_window(
entity_ids,
)

async def _prepare_entity_vector_jobs(
self,
entity_id: int,
) -> _PreparedEntityVectorSync:
"""Prepare chunk mutations and embedding jobs for one entity."""
return await semantic_vector_sync.prepare_entity_vector_jobs(self, entity_id)

async def _prepare_entity_vector_jobs_prefetched(
self,
*,
entity_id: int,
source_rows: list[Any],
existing_rows: list[VectorChunkState],
) -> _PreparedEntityVectorSync:
"""Prepare one entity using prefetched window rows."""
return await semantic_vector_sync.prepare_entity_vector_jobs_prefetched(
self,
entity_id=entity_id,
source_rows=source_rows,
existing_rows=existing_rows,
)

async def _upsert_scheduled_chunk_records(
self,
session: AsyncSession,
Expand Down Expand Up @@ -1011,32 +979,6 @@ def _log_vector_summary() -> None:
_log_vector_summary()
return ranked_rows[offset : offset + limit]

async def _fetch_entity_rows_by_ids(self, entity_ids: list[int]) -> dict[int, SearchIndexRow]:
"""Fetch entity-type search_index rows by their entity_id values."""
placeholders = ",".join(f":id_{idx}" for idx in range(len(entity_ids)))
params: dict[str, Any] = {
**{f"id_{idx}": eid for idx, eid in enumerate(entity_ids)},
"project_id": self.project_id,
"item_type": SearchItemType.ENTITY.value,
}
sql = f"""
SELECT
project_id, id, title, permalink, file_path, type, metadata,
from_id, to_id, relation_type, entity_id, content_snippet,
category, created_at, updated_at, 0 as score
FROM search_index
WHERE project_id = :project_id
AND type = :item_type
AND entity_id IN ({placeholders})
"""
result: dict[int, SearchIndexRow] = {}
async with db.scoped_session(self.session_maker) as session:
row_result = await session.execute(text(sql), params)
for row in row_result.fetchall():
search_row = SearchIndexRow.from_mapping(row._asdict())
result[row.entity_id] = search_row
return result

async def _fetch_search_index_rows_by_ids(
self, row_ids: list[int]
) -> dict[SearchIndexKey, SearchIndexRow]:
Expand Down
6 changes: 1 addition & 5 deletions tests/mcp/test_tool_recent_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def test_recent_activity_format_project_output_no_results():
)

out = recent_activity_module._format_project_output(
project_name="proj", activity_data=empty, timeframe="7d", type_filter="", page=1
project_name="proj", activity_data=empty, timeframe="7d", page=1
)
assert "No recent activity found" in out

Expand Down Expand Up @@ -321,7 +321,6 @@ def test_recent_activity_format_project_output_renders_all_entities_and_relation
project_name="proj",
activity_data=activity,
timeframe="7d",
type_filter=["entity", "relation"],
page=1,
)

Expand Down Expand Up @@ -367,7 +366,6 @@ def test_recent_activity_format_project_output_includes_observation_truncation()
project_name="proj",
activity_data=activity,
timeframe="7d",
type_filter="observation",
page=1,
)
assert "Recent Observations" in out
Expand Down Expand Up @@ -568,7 +566,6 @@ def test_format_project_output_has_more_pagination_guidance():
project_name="proj",
activity_data=activity,
timeframe="7d",
type_filter="entity",
page=1,
)
assert "Use page=2 to see more" in out
Expand Down Expand Up @@ -606,7 +603,6 @@ def test_format_project_output_no_more_pages():
project_name="proj",
activity_data=activity,
timeframe="7d",
type_filter="entity",
page=1,
)
assert "1 items found." in out
Expand Down
Loading