diff --git a/src/basic_memory/schemas/base.py b/src/basic_memory/schemas/base.py index 0a9dea1c..e0f35831 100644 --- a/src/basic_memory/schemas/base.py +++ b/src/basic_memory/schemas/base.py @@ -183,7 +183,6 @@ class Entity(BaseModel): description="MIME type of the content (e.g. text/markdown, image/jpeg)", examples=["text/markdown", "image/jpeg"], ) - observations: List[Observation] = [] @property def permalink(self) -> PathId: diff --git a/src/basic_memory/services/entity_service.py b/src/basic_memory/services/entity_service.py index ff927d4e..0906911c 100644 --- a/src/basic_memory/services/entity_service.py +++ b/src/basic_memory/services/entity_service.py @@ -9,10 +9,10 @@ from basic_memory.models import Entity as EntityModel, Observation, Relation from basic_memory.repository.entity_repository import EntityRepository from basic_memory.schemas import Entity as EntitySchema from basic_memory.services.exceptions import EntityNotFoundError -from . import FileService -from . import BaseService -from .link_resolver import LinkResolver -from ..markdown.entity_parser import parse +from basic_memory.services import FileService +from basic_memory.services import BaseService +from basic_memory.services.link_resolver import LinkResolver +from basic_memory.markdown.entity_parser import parse def entity_model(entity: EntitySchema): @@ -23,7 +23,6 @@ def entity_model(entity: EntitySchema): permalink=entity.permalink, file_path=entity.file_path, content_type=entity.content_type, - observations=[Observation(content=observation) for observation in entity.observations], ) return model @@ -94,12 +93,7 @@ class EntityService(BaseService[EntityModel]): ] db_entity = None - try: - # set timestamps for observations if present - for observation in model.observations: - observation.created_at = observation.created_at or datetime.now(timezone.utc) - observation.updated_at = observation.updated_at or datetime.now(timezone.utc) - + try: # Create entity in DB db_entity = await self.repository.add(model) diff --git a/tests/api/test_knowledge_router.py b/tests/api/test_knowledge_router.py index bfa31fd2..48cc90ed 100644 --- a/tests/api/test_knowledge_router.py +++ b/tests/api/test_knowledge_router.py @@ -20,7 +20,6 @@ async def create_entity(client) -> EntityResponse: data = { "title": "TestEntity", "entity_type": "test", - "observations": ["First observation", "Second observation"], } # Create an entity response = await client.post("/knowledge/entities", json={"entities": [data]}) @@ -34,8 +33,6 @@ async def create_entity(client) -> EntityResponse: entity_type = entity.get("entity_type") assert entity_type == data["entity_type"] - assert len(entity["observations"]) == 2 - create_response = EntityListResponse.model_validate(response_data) return create_response.entities[0] diff --git a/tests/api/test_search_router.py b/tests/api/test_search_router.py index d68e21bb..2e97f18a 100644 --- a/tests/api/test_search_router.py +++ b/tests/api/test_search_router.py @@ -129,7 +129,6 @@ async def test_reindex(client, search_service, entity_service, session_maker): EntitySchema( title="TestEntity1", entity_type="test", - observations=["this is a test observation"], ), ) @@ -151,7 +150,7 @@ async def test_reindex(client, search_service, entity_service, session_maker): # Verify content is searchable again search_response = await client.post("/search/", json={"text": "test"}) search_results = SearchResponse.model_validate(search_response.json()) - assert len(search_results.results) == 2 + assert len(search_results.results) == 1 diff --git a/tests/mcp/conftest.py b/tests/mcp/conftest.py index 2c0ee392..5bd00601 100644 --- a/tests/mcp/conftest.py +++ b/tests/mcp/conftest.py @@ -42,30 +42,11 @@ def test_entity_data(): "title": "Test Entity", "entity_type": "test", "summary": "", # Empty string instead of None - "observations": ["This is a test observation"], } ] } -@pytest_asyncio.fixture -def test_directory_entity_data(): - """Real data that caused failure in the tool.""" - return { - "entities": [ - { - "title": "Directory Organization", - "entity_type": "memory", - "summary": "Implemented filesystem organization by entity type", - "observations": [ - "Files are now organized by type using directories like entities/project/basic_memory", - "Entity IDs match filesystem paths for better mental model", - "Fixed path handling bugs by adding consistent get_entity_path helper", - ], - } - ] - } - @pytest_asyncio.fixture(autouse=True) async def init_search_index(search_service: SearchService): await search_service.init_search_index() diff --git a/tests/mcp/test_tool_add_observations.py b/tests/mcp/test_tool_add_observations.py index 37672f57..b8801adb 100644 --- a/tests/mcp/test_tool_add_observations.py +++ b/tests/mcp/test_tool_add_observations.py @@ -99,12 +99,12 @@ async def test_add_observations_with_context(client): @pytest.mark.asyncio -async def test_add_observations_preserves_existing(client): +async def test_add_observations(client): """Test that adding observations preserves existing ones.""" # Create entity with initial observation entity_request = CreateEntityRequest( entities=[ - Entity(title="TestEntity", entity_type="test", observations=["Initial observation"]) + Entity(title="TestEntity", entity_type="test") ] ) result = await create_entities(entity_request) @@ -119,10 +119,9 @@ async def test_add_observations_preserves_existing(client): ) updated = await add_observations(request) - # Should have both observations - assert len(updated.observations) == 2 + # Should have observation + assert len(updated.observations) == 1 contents = {obs.content for obs in updated.observations} - assert "Initial observation" in contents assert "New observation" in contents diff --git a/tests/mcp/test_tool_create_entities.py b/tests/mcp/test_tool_create_entities.py index fa42a9be..da5ca314 100644 --- a/tests/mcp/test_tool_create_entities.py +++ b/tests/mcp/test_tool_create_entities.py @@ -15,7 +15,7 @@ async def test_create_basic_entity(client): Entity( title="TestEntity", entity_type="test", - observations=["First observation"], + content="- [note] First observation", ) ] ) @@ -49,7 +49,11 @@ async def test_create_entity_with_multiple_observations(client): Entity( title="TestEntity", entity_type="test", - observations=["First observation", "Second observation", "Third observation"], + content=""" +- [note] First observation +- [note] Second observation +- [note] Third observation +""", ) ] ) @@ -72,8 +76,8 @@ async def test_create_multiple_entities(client): """Test creating multiple entities in one request.""" request = CreateEntityRequest( entities=[ - Entity(title="Entity1", entity_type="test", observations=["Observation 1"]), - Entity(title="Entity2", entity_type="test", observations=["Observation 2"]), + Entity(title="Entity1", entity_type="test", content="- [note] Observation 1"), + Entity(title="Entity2", entity_type="test", content="- [note] Observation 2"), ] ) diff --git a/tests/mcp/test_tool_get_entities.py b/tests/mcp/test_tool_get_entities.py index 6a4ad978..3b976911 100644 --- a/tests/mcp/test_tool_get_entities.py +++ b/tests/mcp/test_tool_get_entities.py @@ -3,7 +3,6 @@ import pytest from httpx import AsyncClient from mcp.server import FastMCP -from mcp.server.fastmcp import Context from mcp.types import TextContent from basic_memory.mcp.tools import get_entities @@ -23,7 +22,7 @@ async def test_open_multiple_entities(mcp: FastMCP, client: AsyncClient): Entity(title="Entity2", entity_type="test"), ] ) - result = await mcp.call_tool("create_entities",{ "request": entity_request}) + result = await mcp.call_tool("create_entities", {"request": entity_request}) assert len(result) == 1 assert isinstance(result[0], TextContent) @@ -54,7 +53,10 @@ async def test_open_nodes_with_details(client): Entity( title="DetailedEntity", entity_type="test", - observations=["First observation", "Second observation"], + content=""" +- [note] First observation +- [note] Second observation +""", ) ] ) diff --git a/tests/mcp/test_tool_get_entity.py b/tests/mcp/test_tool_get_entity.py index f9428ad0..581c5c20 100644 --- a/tests/mcp/test_tool_get_entity.py +++ b/tests/mcp/test_tool_get_entity.py @@ -18,7 +18,7 @@ async def test_get_basic_entity(client): Entity( title="TestEntity", entity_type="test", - observations=["First observation"], + content="- [note] First observation", ) ] ) diff --git a/tests/schemas/test_schemas.py b/tests/schemas/test_schemas.py index a993fb4c..70370cf9 100644 --- a/tests/schemas/test_schemas.py +++ b/tests/schemas/test_schemas.py @@ -15,28 +15,14 @@ from basic_memory.schemas import ( from basic_memory.schemas.base import to_snake_case, TimeFrame -def test_entity_in_minimal(): +def test_entity(): """Test creating EntityIn with minimal required fields.""" data = {"title": "test_entity", "entity_type": "knowledge"} entity = Entity.model_validate(data) assert entity.title == "test_entity" assert entity.entity_type == "knowledge" - assert entity.observations == [] -def test_entity_in_complete(): - """Test creating EntityIn with all fields.""" - data = { - "title": "test_entity", - "entity_type": "knowledge", - "observations": ["Test observation"], - } - entity = Entity.model_validate(data) - assert entity.title == "test_entity" - assert entity.entity_type == "knowledge" - assert len(entity.observations) == 1 - assert entity.observations[0] == "Test observation" - def test_entity_in_validation(): """Test validation errors for EntityIn.""" @@ -126,29 +112,6 @@ def test_entity_out_from_attributes(): assert len(entity.relations) == 1 -def test_optional_fields(): - """Test handling of optional fields.""" - # Create with no optional fields - entity = Entity.model_validate({"title": "test", "entity_type": "knowledge"}) - assert entity.observations == [] - - # Create with empty optional fields - entity = Entity.model_validate( - { - "title": "test", - "entity_type": "knowledge", - "observations": [], - } - ) - assert entity.observations == [] - - # Create with some optional fields - entity = Entity.model_validate( - {"title": "test", "entity_type": "knowledge", "observations": []} - ) - assert entity.observations == [] - - def test_search_nodes_input(): """Test SearchNodesInput validation.""" search = SearchNodesRequest.model_validate({"query": "test query"}) diff --git a/tests/services/test_entity_service.py b/tests/services/test_entity_service.py index 44f05cf9..588c7e48 100644 --- a/tests/services/test_entity_service.py +++ b/tests/services/test_entity_service.py @@ -19,7 +19,6 @@ async def test_create_entity(entity_service: EntityService, file_service: FileSe entity_data = EntitySchema( title="TestEntity", entity_type="test", - observations=["this is a test observation"], ) # Act @@ -32,7 +31,6 @@ async def test_create_entity(entity_service: EntityService, file_service: FileSe assert entity.file_path == entity_data.file_path assert entity.entity_type == "test" assert entity.created_at is not None - assert entity.observations[0].content == "this is a test observation" assert len(entity.relations) == 0 # Verify we can retrieve it using permalink @@ -40,7 +38,6 @@ async def test_create_entity(entity_service: EntityService, file_service: FileSe assert retrieved.title == "TestEntity" assert retrieved.entity_type == "test" assert retrieved.created_at is not None - assert retrieved.observations[0].content == "this is a test observation" # Verify file was written file_path = file_service.get_entity_path(entity) @@ -63,12 +60,10 @@ async def test_create_entities(entity_service: EntityService, file_service: File EntitySchema( title="TestEntity1", entity_type="test", - observations=["this is a test observation"], ), EntitySchema( title="TestEntity2", entity_type="test", - observations=["this is a test observation"], ), ] @@ -82,7 +77,6 @@ async def test_create_entities(entity_service: EntityService, file_service: File assert entity1.title == "TestEntity1" assert entity1.entity_type == "test" assert entity1.created_at is not None - assert entity1.observations[0].content == "this is a test observation" assert len(entity1.relations) == 0 entity2 = entities[1] @@ -90,7 +84,6 @@ async def test_create_entities(entity_service: EntityService, file_service: File assert entity2.title == "TestEntity2" assert entity2.entity_type == "test" assert entity2.created_at is not None - assert entity2.observations[0].content == "this is a test observation" # Verify we can retrieve them using permalinks retrieved1 = await entity_service.get_by_permalink(entity_data[0].permalink) @@ -110,14 +103,12 @@ async def test_get_by_permalink(entity_service: EntityService): entity1_data = EntitySchema( title="TestEntity1", entity_type="test", - observations=[], ) entity1 = await entity_service.create_entity(entity1_data) entity2_data = EntitySchema( title="TestEntity2", entity_type="test", - observations=[], ) entity2 = await entity_service.create_entity(entity2_data) @@ -143,7 +134,6 @@ async def test_get_entity_success(entity_service: EntityService): entity_data = EntitySchema( title="TestEntity", entity_type="test", - observations=[], ) await entity_service.create_entity(entity_data) @@ -160,7 +150,6 @@ async def test_delete_entity_success(entity_service: EntityService): entity_data = EntitySchema( title="TestEntity", entity_type="test", - observations=[], ) await entity_service.create_entity(entity_data) @@ -205,12 +194,10 @@ async def test_open_nodes_by_permalinks(entity_service: EntityService): entity1_data = EntitySchema( title="Entity1", entity_type="test", - observations=[], ) entity2_data = EntitySchema( title="Entity2", entity_type="test", - observations=[], ) await entity_service.create_entity(entity1_data) await entity_service.create_entity(entity2_data) @@ -236,7 +223,6 @@ async def test_open_nodes_some_not_found(entity_service: EntityService): entity_data = EntitySchema( title="Entity1", entity_type="test", - observations=[], ) await entity_service.create_entity(entity_data) @@ -254,12 +240,10 @@ async def test_delete_entities_by_permalinks(entity_service: EntityService): entity1_data = EntitySchema( title="Entity1", entity_type="test", - observations=[], ) entity2_data = EntitySchema( title="Entity2", entity_type="test", - observations=[], ) await entity_service.create_entity(entity1_data) await entity_service.create_entity(entity2_data)