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
6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dependencies = [
"pyjwt>=2.10.1",
"python-dotenv>=1.1.0",
"pytest-aio>=1.9.0",
"aiofiles>=24.1.0", # Optional observability (disabled by default via config)
"aiofiles>=24.1.0",
"asyncpg>=0.30.0",
"nest-asyncio>=1.6.0", # For Alembic migrations with Postgres
"pytest-asyncio>=1.2.0",
Expand All @@ -47,6 +47,7 @@ dependencies = [
"fastembed>=0.7.4",
"sqlite-vec>=0.1.6",
"openai>=1.100.2",
"logfire>=4.19.0",
]

[project.urls]
Expand All @@ -58,9 +59,6 @@ Documentation = "https://github.com/basicmachines-co/basic-memory#readme"
basic-memory = "basic_memory.cli.main:app"
bm = "basic_memory.cli.main:app"

[project.optional-dependencies]
telemetry = ["logfire>=4.19.0"]

[build-system]
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
build-backend = "hatchling.build"
Expand Down
6 changes: 3 additions & 3 deletions src/basic_memory/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
list_projects,
synchronize_projects,
)
from basic_memory import telemetry
import logfire
from basic_memory.config import init_api_logging
from basic_memory.services.exceptions import EntityAlreadyExistsError
from basic_memory.services.initialization import initialize_app
Expand All @@ -44,7 +44,7 @@ async def lifespan(app: FastAPI): # pragma: no cover
set_container(container)
app.state.container = container

with telemetry.operation(
with logfire.span(
"api.lifecycle.startup",
entrypoint="api",
mode=container.mode.name.lower(),
Expand All @@ -69,7 +69,7 @@ async def lifespan(app: FastAPI): # pragma: no cover
yield

# Shutdown - coordinator handles clean task cancellation
with telemetry.operation(
with logfire.span(
"api.lifecycle.shutdown",
entrypoint="api",
mode=container.mode.name.lower(),
Expand Down
22 changes: 11 additions & 11 deletions src/basic_memory/api/v2/routers/knowledge_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from fastapi import APIRouter, HTTPException, Response, Path
from loguru import logger

from basic_memory import telemetry
import logfire
from basic_memory.deps import (
EntityServiceV2ExternalDep,
SearchServiceV2ExternalDep,
Expand Down Expand Up @@ -74,7 +74,7 @@ async def get_graph(
Returns a flat node/edge structure optimized for rendering with graph libraries.
Only includes resolved relations (where to_id is not null).
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.get_graph",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -148,7 +148,7 @@ async def resolve_identifier(
"resolution_method": "permalink"
}
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.resolve_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -215,7 +215,7 @@ async def get_entity_by_id(
Raises:
HTTPException: 404 if entity not found
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.get_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -255,7 +255,7 @@ async def create_entity(
Returns:
Created entity with generated external_id (UUID) and file content
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.create_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -313,7 +313,7 @@ async def update_entity_by_id(
Returns:
Updated entity with file content
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.update_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -385,7 +385,7 @@ async def edit_entity_by_id(
Raises:
HTTPException: 404 if entity not found, 400 if edit fails
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.edit_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -454,7 +454,7 @@ async def delete_entity_by_id(

Note: Returns deleted=False if entity doesn't exist (idempotent)
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.delete_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -503,7 +503,7 @@ async def move_entity(
Returns:
Updated entity with new file path
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.move_entity",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -583,7 +583,7 @@ async def move_directory(
Returns:
DirectoryMoveResult with counts and details of moved files
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.move_directory",
entrypoint="api",
domain="knowledge",
Expand Down Expand Up @@ -647,7 +647,7 @@ async def delete_directory(
Returns:
DirectoryDeleteResult with counts and details of deleted files
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.delete_directory",
entrypoint="api",
domain="knowledge",
Expand Down
14 changes: 7 additions & 7 deletions src/basic_memory/api/v2/routers/memory_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from fastapi import APIRouter, Query, Path
from loguru import logger

from basic_memory import telemetry
import logfire
from basic_memory.deps import ContextServiceV2ExternalDep, EntityRepositoryV2ExternalDep
from basic_memory.schemas.base import TimeFrame, parse_timeframe
from basic_memory.schemas.memory import (
Expand Down Expand Up @@ -51,7 +51,7 @@ async def recent(
Returns:
GraphContext with recent activity and related entities
"""
with telemetry.operation(
with logfire.span(
"api.request.memory.recent_activity",
entrypoint="api",
domain="memory",
Expand All @@ -72,7 +72,7 @@ async def recent(
limit = page_size
offset = (page - 1) * page_size

with telemetry.scope(
with logfire.span(
"api.memory.recent_activity.build_context",
domain="memory",
action="recent_activity",
Expand All @@ -88,7 +88,7 @@ async def recent(
offset=offset,
max_related=max_related,
)
with telemetry.scope(
with logfire.span(
"api.memory.recent_activity.shape_response",
domain="memory",
action="recent_activity",
Expand Down Expand Up @@ -137,7 +137,7 @@ async def get_memory_context(
Returns:
GraphContext with the entity and its related context
"""
with telemetry.operation(
with logfire.span(
"api.request.memory.build_context",
entrypoint="api",
domain="memory",
Expand All @@ -154,7 +154,7 @@ async def get_memory_context(
limit = page_size
offset = (page - 1) * page_size

with telemetry.scope(
with logfire.span(
"api.memory.build_context.build_context",
domain="memory",
action="build_context",
Expand All @@ -170,7 +170,7 @@ async def get_memory_context(
offset=offset,
max_related=max_related,
)
with telemetry.scope(
with logfire.span(
"api.memory.build_context.shape_response",
domain="memory",
action="build_context",
Expand Down
32 changes: 16 additions & 16 deletions src/basic_memory/api/v2/routers/resource_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from fastapi import APIRouter, HTTPException, Response, Path
from loguru import logger

from basic_memory import telemetry
import logfire
from basic_memory.deps import (
ProjectConfigV2ExternalDep,
FileServiceV2ExternalDep,
Expand Down Expand Up @@ -56,15 +56,15 @@ async def get_resource_content(
Raises:
HTTPException: 404 if entity or file not found
"""
with telemetry.operation(
with logfire.span(
"api.request.resource.get_content",
entrypoint="api",
domain="resource",
action="get_content",
):
logger.debug(f"V2 Getting content for project {project_id}, entity_id: {entity_id}")

with telemetry.scope(
with logfire.span(
"api.resource.get_content.load_entity",
domain="resource",
action="get_content",
Expand All @@ -74,7 +74,7 @@ async def get_resource_content(
if not entity:
raise HTTPException(status_code=404, detail=f"Entity {entity_id} not found")

with telemetry.scope(
with logfire.span(
"api.resource.get_content.validate_path",
domain="resource",
action="get_content",
Expand All @@ -90,7 +90,7 @@ async def get_resource_content(
detail="Entity contains invalid file path",
)

with telemetry.scope(
with logfire.span(
"api.resource.get_content.ensure_exists",
domain="resource",
action="get_content",
Expand All @@ -102,7 +102,7 @@ async def get_resource_content(
detail=f"File not found: {entity.file_path}",
)

with telemetry.scope(
with logfire.span(
"api.resource.get_content.read_content",
domain="resource",
action="get_content",
Expand Down Expand Up @@ -139,7 +139,7 @@ async def create_resource(
Raises:
HTTPException: 400 for invalid file paths, 409 if file already exists
"""
with telemetry.operation(
with logfire.span(
"api.request.resource.create",
entrypoint="api",
domain="resource",
Expand All @@ -166,7 +166,7 @@ async def create_resource(
f"Use PUT /resource/{existing_entity.external_id} to update it.",
)

with telemetry.scope(
with logfire.span(
"api.resource.create.write_file",
domain="resource",
action="create",
Expand All @@ -175,7 +175,7 @@ async def create_resource(
await file_service.ensure_directory(PathLib(data.file_path).parent)
checksum = await file_service.write_file(data.file_path, data.content)

with telemetry.scope(
with logfire.span(
"api.resource.create.read_metadata",
domain="resource",
action="create",
Expand All @@ -197,15 +197,15 @@ async def create_resource(
created_at=file_metadata.created_at,
updated_at=file_metadata.modified_at,
)
with telemetry.scope(
with logfire.span(
"api.resource.create.upsert_entity",
domain="resource",
action="create",
phase="upsert_entity",
):
entity = await entity_repository.add(entity)

with telemetry.scope(
with logfire.span(
"api.resource.create.search_index",
domain="resource",
action="create",
Expand Down Expand Up @@ -258,7 +258,7 @@ async def update_resource(
Raises:
HTTPException: 404 if entity not found, 400 for invalid paths
"""
with telemetry.operation(
with logfire.span(
"api.request.resource.update",
entrypoint="api",
domain="resource",
Expand All @@ -282,7 +282,7 @@ async def update_resource(
"Path must be relative and stay within project boundaries.",
)

with telemetry.scope(
with logfire.span(
"api.resource.update.write_file",
domain="resource",
action="update",
Expand All @@ -297,7 +297,7 @@ async def update_resource(

checksum = await file_service.write_file(target_file_path, data.content)

with telemetry.scope(
with logfire.span(
"api.resource.update.read_metadata",
domain="resource",
action="update",
Expand All @@ -309,7 +309,7 @@ async def update_resource(
content_type = file_service.content_type(target_file_path)
note_type = "canvas" if target_file_path.endswith(".canvas") else "file"

with telemetry.scope(
with logfire.span(
"api.resource.update.update_entity",
domain="resource",
action="update",
Expand All @@ -329,7 +329,7 @@ async def update_resource(
if updated_entity is None:
raise HTTPException(status_code=404, detail=f"Entity {entity_id} not found")

with telemetry.scope(
with logfire.span(
"api.resource.update.search_index",
domain="resource",
action="update",
Expand Down
Loading
Loading