add get_entity_types endpoint

This commit is contained in:
phernandez
2024-12-30 13:06:41 -06:00
parent 65dac90128
commit bcc5f8a165
9 changed files with 118 additions and 4 deletions
+2
View File
@@ -5,6 +5,7 @@ from loguru import logger
from .routers import documents
from .routers import knowledge
from .routers import discovery
# Initialize FastAPI app
app = FastAPI(
@@ -14,6 +15,7 @@ app = FastAPI(
# Include routers
app.include_router(knowledge.router)
app.include_router(documents.router)
app.include_router(discovery.router)
# Add startup event
+2 -1
View File
@@ -2,5 +2,6 @@
from . import knowledge_router as knowledge
from . import documents_router as documents
from . import discovery_router as discovery
__all__ = ["knowledge", "documents"]
__all__ = ["knowledge", "documents", "discovery"]
@@ -0,0 +1,17 @@
"""Router for knowledge discovery and analytics operations."""
from fastapi import APIRouter
from loguru import logger
from basic_memory.deps import EntityServiceDep
from basic_memory.schemas import EntityTypeList, ObservationCategoryList
router = APIRouter(prefix="/discovery", tags=["discovery"])
@router.get("/entity-types", response_model=EntityTypeList)
async def get_entity_types(entity_service: EntityServiceDep) -> EntityTypeList:
"""Get list of all unique entity types in the system."""
logger.debug("Getting all entity types")
types = await entity_service.get_entity_types()
return EntityTypeList(types=types)
@@ -43,7 +43,7 @@ class EntityRepository(Repository[Entity]):
"""Get list of distinct entity types."""
query = select(Entity.entity_type).distinct()
result = await self.execute_query(query)
result = await self.execute_query(query, use_query_options=False)
return list(result.scalars().all())
async def search(self, query_str: str) -> List[Entity]:
+2 -2
View File
@@ -246,10 +246,10 @@ class Repository[T: Base]:
logger.debug(f"Counted {count} {self.Model.__name__} records")
return count
async def execute_query(self, query: Executable) -> Result[Any]:
async def execute_query(self, query: Executable, use_query_options:bool = True) -> Result[Any]:
"""Execute a query asynchronously."""
query = query.options(*self.get_load_options())
query = query.options(*self.get_load_options()) if use_query_options else query
logger.debug(f"Executing query: {query}")
async with db.scoped_session(self.session_maker) as session:
+9
View File
@@ -41,6 +41,12 @@ from basic_memory.schemas.response import (
DeleteEntitiesResponse,
)
# Discovery and analytics models
from basic_memory.schemas.discovery import (
EntityTypeList,
ObservationCategoryList,
)
# For convenient imports, export all models
__all__ = [
# Base
@@ -67,4 +73,7 @@ __all__ = [
"DeleteEntitiesRequest",
"DeleteRelationsRequest",
"DeleteObservationsRequest",
# Discovery and Analytics
"EntityTypeList",
"ObservationCategoryList",
]
+14
View File
@@ -0,0 +1,14 @@
"""Schemas for knowledge discovery and analytics endpoints."""
from typing import List
from pydantic import BaseModel
class EntityTypeList(BaseModel):
"""List of unique entity types in the system."""
types: List[str]
class ObservationCategoryList(BaseModel):
"""List of unique observation categories in the system."""
categories: List[str]
@@ -67,6 +67,11 @@ class EntityService(BaseService[EntityRepository]):
async def get_all(self) -> Sequence[EntityModel]:
"""Get all entities."""
return await self.repository.find_all()
async def get_entity_types(self) -> List[str]:
"""Get list of all distinct entity types in the system."""
logger.debug("Getting all distinct entity types")
return await self.repository.get_entity_types()
async def delete_entity(self, path_id: str) -> bool:
"""Delete entity from database."""
+66
View File
@@ -0,0 +1,66 @@
"""Tests for discovery router endpoints."""
import pytest
import pytest_asyncio
from httpx import AsyncClient
from basic_memory.models.knowledge import Entity
from basic_memory.repository.entity_repository import EntityRepository
from basic_memory.schemas import EntityTypeList
pytestmark = pytest.mark.asyncio
@pytest_asyncio.fixture
async def test_entities(entity_repository: EntityRepository) -> list[Entity]:
"""Create test entities with different types."""
entities = [
Entity(
name="Memory Service",
entity_type="technical_component",
description="Core memory service",
path_id="component/memory_service",
file_path="component/memory_service.md",
),
Entity(
name="File Format",
entity_type="specification",
description="File format spec",
path_id="spec/file_format",
file_path="spec/file_format.md",
),
Entity(
name="Technical Decision",
entity_type="decision",
description="Architecture decision",
path_id="decision/tech_choice",
file_path="decision/tech_choice.md",
),
]
created = await entity_repository.add_all(entities)
return created
@pytest.mark.asyncio
async def test_get_entity_types(client: AsyncClient, test_entities):
"""Test getting list of entity types."""
# Get types
response = await client.get("/discovery/entity-types")
assert response.status_code == 200
# Parse response
data = EntityTypeList.model_validate(response.json())
# Should have types from test data
assert len(data.types) > 0
assert "technical_component" in data.types
assert "specification" in data.types
assert "decision" in data.types
# Types should all be strings
assert isinstance(data.types, list)
assert all(isinstance(t, str) for t in data.types)
# Types should be unique
assert len(data.types) == len(set(data.types))