improve validation for memory:// urls, add examples to build_context

Signed-off-by: phernandez <paul@basicmachines.co>
This commit is contained in:
phernandez
2025-06-04 00:16:33 -05:00
parent 80ec860a1c
commit c5c70cb0f4
7 changed files with 518 additions and 10 deletions
+11 -7
View File
@@ -13,7 +13,6 @@ from basic_memory.schemas.memory import (
GraphContext,
MemoryUrl,
memory_url_path,
normalize_memory_url,
)
@@ -21,12 +20,17 @@ from basic_memory.schemas.memory import (
description="""Build context from a memory:// URI to continue conversations naturally.
Use this to follow up on previous discussions or explore related topics.
Memory URL Format:
- Use paths like "folder/note" or "memory://folder/note"
- Pattern matching: "folder/*" matches all notes in folder
- Valid characters: letters, numbers, hyphens, underscores, forward slashes
- Avoid: double slashes (//), angle brackets (<>), quotes, pipes (|)
- Examples: "specs/search", "projects/basic-memory", "notes/*"
Timeframes support natural language like:
- "2 days ago"
- "last week"
- "today"
- "3 months ago"
Or standard formats like "7d", "24h"
- "2 days ago", "last week", "today", "3 months ago"
- Or standard formats like "7d", "24h"
""",
)
async def build_context(
@@ -76,7 +80,7 @@ async def build_context(
build_context("memory://specs/search", project="work-project")
"""
logger.info(f"Building context from {url}")
url = normalize_memory_url(url)
# URL is already validated and normalized by MemoryUrl type annotation
active_project = get_active_project(project)
project_url = active_project.project_url
+2 -1
View File
@@ -35,7 +35,8 @@ async def canvas(
nodes: List of node objects following JSON Canvas 1.0 spec
edges: List of edge objects following JSON Canvas 1.0 spec
title: The title of the canvas (will be saved as title.canvas)
folder: The folder where the file should be saved
folder: Folder path relative to project root where the canvas should be saved.
Use forward slashes (/) as separators. Examples: "diagrams", "projects/2025", "visual/maps"
project: Optional project name to create canvas in. If not provided, uses current active project.
Returns:
+2 -1
View File
@@ -54,7 +54,8 @@ async def write_note(
Args:
title: The title of the note
content: Markdown content for the note, can include observations and relations
folder: the folder where the file should be saved
folder: Folder path relative to project root where the file should be saved.
Use forward slashes (/) as separators. Examples: "notes", "projects/2025", "research/ml"
tags: Tags to categorize the note. Can be a list of strings, a comma-separated string, or None.
Note: If passing from external MCP clients, use a string format (e.g. "tag1,tag2,tag3")
project: Optional project name to write to. If not provided, uses current active project.
+58 -1
View File
@@ -9,8 +9,44 @@ from pydantic import BaseModel, Field, BeforeValidator, TypeAdapter
from basic_memory.schemas.search import SearchItemType
def validate_memory_url_path(path: str) -> bool:
"""Validate that a memory URL path is well-formed.
Args:
path: The path part of a memory URL (without memory:// prefix)
Returns:
True if the path is valid, False otherwise
Examples:
>>> validate_memory_url_path("specs/search")
True
>>> validate_memory_url_path("memory//test") # Double slash
False
>>> validate_memory_url_path("invalid://test") # Contains protocol
False
"""
if not path or not path.strip():
return False
# Check for invalid protocol schemes within the path first (more specific)
if "://" in path:
return False
# Check for double slashes (except at the beginning for absolute paths)
if "//" in path:
return False
# Check for invalid characters (excluding * which is used for pattern matching)
invalid_chars = {"<", ">", '"', "|", "?"}
if any(char in path for char in invalid_chars):
return False
return True
def normalize_memory_url(url: str | None) -> str:
"""Normalize a MemoryUrl string.
"""Normalize a MemoryUrl string with validation.
Args:
url: A path like "specs/search" or "memory://specs/search"
@@ -18,22 +54,43 @@ def normalize_memory_url(url: str | None) -> str:
Returns:
Normalized URL starting with memory://
Raises:
ValueError: If the URL path is malformed
Examples:
>>> normalize_memory_url("specs/search")
'memory://specs/search'
>>> normalize_memory_url("memory://specs/search")
'memory://specs/search'
>>> normalize_memory_url("memory//test")
Traceback (most recent call last):
...
ValueError: Invalid memory URL path: 'memory//test' contains double slashes
"""
if not url:
return ""
clean_path = url.removeprefix("memory://")
# Validate the extracted path
if not validate_memory_url_path(clean_path):
# Provide specific error messages for common issues
if "://" in clean_path:
raise ValueError(f"Invalid memory URL path: '{clean_path}' contains protocol scheme")
elif "//" in clean_path:
raise ValueError(f"Invalid memory URL path: '{clean_path}' contains double slashes")
elif not clean_path.strip():
raise ValueError("Memory URL path cannot be empty or whitespace")
else:
raise ValueError(f"Invalid memory URL path: '{clean_path}' contains invalid characters")
return f"memory://{clean_path}"
MemoryUrl = Annotated[
str,
BeforeValidator(str.strip), # Clean whitespace
BeforeValidator(normalize_memory_url), # Validate and normalize the URL
MinLen(1),
MaxLen(2028),
]