mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
093dab5f03
## Summary - Add comprehensive documentation to all MCP prompt modules - Enhance search prompt with detailed contextual output formatting - Implement consistent logging and docstring patterns across prompt utilities - Fix type checking in prompt modules ## Prompts Added/Enhanced - `search.py`: New formatted output with relevance scores, excerpts, and next steps - `recent_activity.py`: Enhanced with better metadata handling and documentation - `continue_conversation.py`: Improved context management ## Resources Added/Enhanced - `ai_assistant_guide`: Resource with description to give to LLM to understand how to use the tools ## Technical improvements - Added detailed docstrings to all prompt modules explaining their purpose and usage - Enhanced the search prompt with rich contextual output that helps LLMs understand results - Created a consistent pattern for formatting output across prompts - Improved error handling in metadata extraction - Standardized import organization and naming conventions - Fixed various type checking issues across the codebase This PR is part of our ongoing effort to improve the MCP's interaction quality with LLMs, making the system more helpful and intuitive for AI assistants to navigate knowledge bases. 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: phernandez <phernandez@basicmachines.co>
274 lines
7.7 KiB
Python
274 lines
7.7 KiB
Python
"""Tests for canvas tool that exercise the full stack with SQLite."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from basic_memory.mcp.tools import canvas
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_canvas(app, test_config):
|
|
"""Test creating a new canvas file.
|
|
|
|
Should:
|
|
- Create canvas file with correct content
|
|
- Create entity in database
|
|
- Return successful status
|
|
"""
|
|
# Test data
|
|
nodes = [
|
|
{
|
|
"id": "node1",
|
|
"type": "text",
|
|
"text": "Test Node",
|
|
"x": 100,
|
|
"y": 200,
|
|
"width": 400,
|
|
"height": 300,
|
|
}
|
|
]
|
|
edges = [{"id": "edge1", "fromNode": "node1", "toNode": "node2", "label": "connects to"}]
|
|
title = "test-canvas"
|
|
folder = "visualizations"
|
|
|
|
# Execute
|
|
result = await canvas(nodes=nodes, edges=edges, title=title, folder=folder)
|
|
|
|
# Verify result message
|
|
assert result
|
|
assert "Created: visualizations/test-canvas" in result
|
|
assert "The canvas is ready to open in Obsidian" in result
|
|
|
|
# Verify file was created
|
|
file_path = Path(test_config.home) / folder / f"{title}.canvas"
|
|
assert file_path.exists()
|
|
|
|
# Verify content is correct
|
|
content = json.loads(file_path.read_text())
|
|
assert content["nodes"] == nodes
|
|
assert content["edges"] == edges
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_canvas_with_extension(app, test_config):
|
|
"""Test creating a canvas file with .canvas extension already in the title."""
|
|
# Test data
|
|
nodes = [
|
|
{
|
|
"id": "node1",
|
|
"type": "text",
|
|
"text": "Extension Test",
|
|
"x": 100,
|
|
"y": 200,
|
|
"width": 400,
|
|
"height": 300,
|
|
}
|
|
]
|
|
edges = []
|
|
title = "extension-test.canvas" # Already has extension
|
|
folder = "visualizations"
|
|
|
|
# Execute
|
|
result = await canvas(nodes=nodes, edges=edges, title=title, folder=folder)
|
|
|
|
# Verify
|
|
assert "Created: visualizations/extension-test.canvas" in result
|
|
|
|
# Verify file exists with correct name (shouldn't have double extension)
|
|
file_path = Path(test_config.home) / folder / title
|
|
assert file_path.exists()
|
|
|
|
# Verify content
|
|
content = json.loads(file_path.read_text())
|
|
assert content["nodes"] == nodes
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_existing_canvas(app, test_config):
|
|
"""Test updating an existing canvas file."""
|
|
# First create a canvas
|
|
nodes = [
|
|
{
|
|
"id": "initial",
|
|
"type": "text",
|
|
"text": "Initial content",
|
|
"x": 0,
|
|
"y": 0,
|
|
"width": 200,
|
|
"height": 100,
|
|
}
|
|
]
|
|
edges = []
|
|
title = "update-test"
|
|
folder = "visualizations"
|
|
|
|
# Create initial canvas
|
|
await canvas(nodes=nodes, edges=edges, title=title, folder=folder)
|
|
|
|
# Verify file exists
|
|
file_path = Path(test_config.home) / folder / f"{title}.canvas"
|
|
assert file_path.exists()
|
|
|
|
# Now update with new content
|
|
updated_nodes = [
|
|
{
|
|
"id": "updated",
|
|
"type": "text",
|
|
"text": "Updated content",
|
|
"x": 100,
|
|
"y": 100,
|
|
"width": 300,
|
|
"height": 200,
|
|
}
|
|
]
|
|
updated_edges = [
|
|
{"id": "new-edge", "fromNode": "updated", "toNode": "other", "label": "new connection"}
|
|
]
|
|
|
|
# Execute update
|
|
result = await canvas(nodes=updated_nodes, edges=updated_edges, title=title, folder=folder)
|
|
|
|
# Verify result indicates update
|
|
assert "Updated: visualizations/update-test.canvas" in result
|
|
|
|
# Verify content was updated
|
|
content = json.loads(file_path.read_text())
|
|
assert content["nodes"] == updated_nodes
|
|
assert content["edges"] == updated_edges
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_canvas_with_nested_folders(app, test_config):
|
|
"""Test creating a canvas in nested folders that don't exist yet."""
|
|
# Test data
|
|
nodes = [
|
|
{
|
|
"id": "test",
|
|
"type": "text",
|
|
"text": "Nested folder test",
|
|
"x": 0,
|
|
"y": 0,
|
|
"width": 200,
|
|
"height": 100,
|
|
}
|
|
]
|
|
edges = []
|
|
title = "nested-test"
|
|
folder = "visualizations/nested/folders" # Deep path
|
|
|
|
# Execute
|
|
result = await canvas(nodes=nodes, edges=edges, title=title, folder=folder)
|
|
|
|
# Verify
|
|
assert "Created: visualizations/nested/folders/nested-test.canvas" in result
|
|
|
|
# Verify folders and file were created
|
|
file_path = Path(test_config.home) / folder / f"{title}.canvas"
|
|
assert file_path.exists()
|
|
assert file_path.parent.exists()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_canvas_complex_content(app, test_config):
|
|
"""Test creating a canvas with complex content structures."""
|
|
# Test data - more complex structure with all node types
|
|
nodes = [
|
|
{
|
|
"id": "text-node",
|
|
"type": "text",
|
|
"text": "# Heading\n\nThis is a test with *markdown* formatting",
|
|
"x": 100,
|
|
"y": 100,
|
|
"width": 400,
|
|
"height": 300,
|
|
"color": "4", # Using a preset color
|
|
},
|
|
{
|
|
"id": "file-node",
|
|
"type": "file",
|
|
"file": "test/test-file.md", # Reference a file
|
|
"x": 600,
|
|
"y": 100,
|
|
"width": 400,
|
|
"height": 300,
|
|
"color": "#FF5500", # Using hex color
|
|
},
|
|
{
|
|
"id": "link-node",
|
|
"type": "link",
|
|
"url": "https://example.com",
|
|
"x": 100,
|
|
"y": 500,
|
|
"width": 400,
|
|
"height": 200,
|
|
},
|
|
{
|
|
"id": "group-node",
|
|
"type": "group",
|
|
"label": "Group Label",
|
|
"x": 600,
|
|
"y": 500,
|
|
"width": 600,
|
|
"height": 400,
|
|
},
|
|
]
|
|
|
|
edges = [
|
|
{
|
|
"id": "edge1",
|
|
"fromNode": "text-node",
|
|
"toNode": "file-node",
|
|
"label": "references",
|
|
"fromSide": "right",
|
|
"toSide": "left",
|
|
},
|
|
{
|
|
"id": "edge2",
|
|
"fromNode": "link-node",
|
|
"toNode": "group-node",
|
|
"label": "belongs to",
|
|
"color": "6",
|
|
},
|
|
]
|
|
|
|
title = "complex-test"
|
|
folder = "visualizations"
|
|
|
|
# Create a test file that we're referencing
|
|
test_file_path = Path(test_config.home) / "test/test-file.md"
|
|
test_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
test_file_path.write_text("# Test File\nThis is referenced by the canvas")
|
|
|
|
# Execute
|
|
result = await canvas(nodes=nodes, edges=edges, title=title, folder=folder)
|
|
|
|
# Verify
|
|
assert "Created: visualizations/complex-test.canvas" in result
|
|
|
|
# Verify file was created
|
|
file_path = Path(test_config.home) / folder / f"{title}.canvas"
|
|
assert file_path.exists()
|
|
|
|
# Verify content is correct with all complex structures
|
|
content = json.loads(file_path.read_text())
|
|
assert len(content["nodes"]) == 4
|
|
assert len(content["edges"]) == 2
|
|
|
|
# Verify specific content elements are preserved
|
|
assert any(node["type"] == "text" and "#" in node["text"] for node in content["nodes"])
|
|
assert any(
|
|
node["type"] == "file" and "test-file.md" in node["file"] for node in content["nodes"]
|
|
)
|
|
assert any(node["type"] == "link" and "example.com" in node["url"] for node in content["nodes"])
|
|
assert any(
|
|
node["type"] == "group" and "Group Label" == node["label"] for node in content["nodes"]
|
|
)
|
|
|
|
# Verify edge properties
|
|
assert any(
|
|
edge["fromSide"] == "right" and edge["toSide"] == "left" for edge in content["edges"]
|
|
)
|
|
assert any(edge["label"] == "belongs to" and edge["color"] == "6" for edge in content["edges"])
|