change /knowledge endpoints to use ids

This commit is contained in:
phernandez
2024-12-24 18:15:59 -06:00
parent bde381c9e2
commit 0bbcf636f6
21 changed files with 620 additions and 421 deletions
-1
View File
@@ -34,7 +34,6 @@ from basic_memory.schemas.request import (
from basic_memory.schemas.response import (
SQLAlchemyModel,
ObservationResponse,
ObservationsResponse,
RelationResponse,
EntityResponse,
CreateEntityResponse,
+29 -18
View File
@@ -32,25 +32,36 @@ Common Relation Types:
- 'tested_by': Test coverage
"""
import re
from typing import List, Optional, Annotated
from annotated_types import MinLen, MaxLen
from pydantic import BaseModel, BeforeValidator
# Strip whitespace
def strip_whitespace(obs: str) -> str:
return obs.strip()
def to_snake_case(name: str) -> str:
"""Convert a string to snake_case.
Examples:
BasicMemory -> basic_memory
Memory Service -> memory_service
memory-service -> memory_service
Memory_Service -> memory_service
"""
# Replace spaces and hyphens with underscores
s1 = re.sub(r"[\s\-]", "_", name)
# Insert underscore between camelCase
s2 = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1)
# Convert to lowercase
return s2.lower()
def lower_strip_whitespace(val: str) -> str:
return strip_whitespace(val.lower())
PathId = Annotated[str, BeforeValidator(lower_strip_whitespace)]
PathId = Annotated[str, BeforeValidator(to_snake_case)]
"""Unique identifier in format '{path}/{normalized_name}'."""
Observation = Annotated[str, BeforeValidator(strip_whitespace), MinLen(1), MaxLen(1000)]
Observation = Annotated[str, MinLen(1), MaxLen(1000)]
"""A single piece of information about an entity. Must be non-empty and under 1000 characters.
Best Practices:
@@ -65,7 +76,7 @@ Examples:
- "Depends on SQLAlchemy for database operations"
"""
EntityType = Annotated[str, BeforeValidator(strip_whitespace), MinLen(1), MaxLen(200)]
EntityType = Annotated[str, BeforeValidator(to_snake_case), MinLen(1), MaxLen(200)]
"""Classification of entity (e.g., 'person', 'project', 'concept').
The type serves multiple purposes:
@@ -77,7 +88,7 @@ The type serves multiple purposes:
Common types are listed in the module docstring.
"""
RelationType = Annotated[str, BeforeValidator(strip_whitespace), MinLen(1), MaxLen(200)]
RelationType = Annotated[str, BeforeValidator(to_snake_case), MinLen(1), MaxLen(200)]
"""Type of relationship between entities. Always use active voice present tense.
Guidelines:
@@ -127,8 +138,8 @@ class Relation(BaseModel):
}
"""
from_id: int
to_id: int
from_id: PathId
to_id: PathId
relation_type: RelationType
context: Optional[str] = None
@@ -147,7 +158,7 @@ class Entity(BaseModel):
1. Project Entity:
{
"name": "Basic_Memory",
"name": "BasicMemory",
"entity_type": "project",
"description": "Knowledge graph system for AI-human collaboration",
"observations": [
@@ -193,13 +204,13 @@ class Entity(BaseModel):
}
"""
id: Optional[int] = None
name: str
entity_type: EntityType
description: Optional[str] = None
observations: List[Observation] = []
@property
def file_path(self) -> str:
"""The relative file path for this entity."""
return f"{id}.md"
def path_id(self) -> PathId:
"""Get the path ID in format {type}/{snake_case_name}."""
normalized_name = to_snake_case(self.name)
return f"{self.entity_type}/{normalized_name}"
+3 -3
View File
@@ -21,7 +21,7 @@ from typing import List, Annotated
from annotated_types import MinLen
from pydantic import BaseModel
from basic_memory.schemas.base import Relation, Observation
from basic_memory.schemas.base import Relation, Observation, PathId
class DeleteEntitiesRequest(BaseModel):
@@ -56,7 +56,7 @@ class DeleteEntitiesRequest(BaseModel):
5. Create relations to replacement entities if applicable
"""
entity_ids: Annotated[List[int], MinLen(1)]
entity_ids: Annotated[List[PathId], MinLen(1)]
class DeleteRelationsRequest(BaseModel):
@@ -132,5 +132,5 @@ class DeleteObservationsRequest(BaseModel):
5. Updating implementation details
"""
entity_id: int
entity_id: PathId
deletions: Annotated[List[Observation], MinLen(1)]
+2 -2
View File
@@ -45,7 +45,7 @@ class AddObservationsRequest(BaseModel):
4. Add observations in logical groups for better history tracking
"""
entity_id: int
entity_id: PathId
context: Optional[str] = None
observations: List[Observation]
@@ -152,7 +152,7 @@ class OpenNodesRequest(BaseModel):
relations between entities that interest you.
"""
entity_ids: Annotated[List[int], MinLen(1)]
entity_ids: Annotated[List[PathId], MinLen(1)]
class CreateRelationsRequest(BaseModel):
+24 -41
View File
@@ -14,9 +14,9 @@ Key Features:
import datetime
from typing import List, Optional, Dict, Any
from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, ConfigDict, Field, AliasPath, AliasChoices
from basic_memory.schemas.base import Observation, Relation
from basic_memory.schemas.base import Observation, Relation, PathId
class SQLAlchemyModel(BaseModel):
@@ -43,36 +43,9 @@ class ObservationResponse(SQLAlchemyModel):
}
"""
id: int
content: Observation
class ObservationsResponse(SQLAlchemyModel):
"""Response schema for bulk observation operations.
Returns all added/affected observations with their IDs and
the entity they were added to.
Example Response:
{
"entity_id": "component/memory_service",
"observations": [
{
"id": 123,
"content": "Added async support"
},
{
"id": 124,
"content": "Improved error handling"
}
]
}
"""
entity_id: int
observations: List[ObservationResponse]
class RelationResponse(Relation, SQLAlchemyModel):
"""Response schema for relation operations.
@@ -81,15 +54,28 @@ class RelationResponse(Relation, SQLAlchemyModel):
Example Response:
{
"id": 45,
"from_id": "test/memory_test",
"to_id": "component/memory_service",
"relation_type": "validates",
"context": "Comprehensive test suite"
}
"""
id: int
from_id: PathId = Field(
# use the path_id from the associated Entity
# or the from_id value
validation_alias=AliasChoices(
AliasPath('from_entity', 'path_id'),
'from_id',
)
)
to_id: PathId = Field(
# use the path_id from the associated Entity
# or the to_id value
validation_alias=AliasChoices(
AliasPath('to_entity', 'path_id'),
'to_id',
)
)
class EntityResponse(SQLAlchemyModel):
@@ -103,23 +89,20 @@ class EntityResponse(SQLAlchemyModel):
Example Response:
{
"id": "component/memory_service",
"path_id": "component/memory_service",
"name": "MemoryService",
"entity_type": "component",
"description": "Core persistence service",
"observations": [
{
"id": 123,
"content": "Uses SQLite storage"
},
{
"id": 124,
"content": "Implements async operations"
}
],
"relations": [
{
"id": 45,
"from_id": "test/memory_test",
"to_id": "component/memory_service",
"relation_type": "validates",
@@ -129,7 +112,7 @@ class EntityResponse(SQLAlchemyModel):
}
"""
id: int
path_id: PathId
name: str
entity_type: str
description: Optional[str] = None
@@ -148,7 +131,7 @@ class CreateEntityResponse(SQLAlchemyModel):
{
"entities": [
{
"id": "component/search_service",
"path_id": "component/search_service",
"name": "SearchService",
"entity_type": "component",
"description": "Knowledge graph search",
@@ -161,7 +144,7 @@ class CreateEntityResponse(SQLAlchemyModel):
"relations": []
},
{
"id": "document/api_docs",
"path_id": "document/api_docs",
"name": "API_Documentation",
"entity_type": "document",
"description": "API Reference",
@@ -190,7 +173,7 @@ class SearchNodesResponse(SQLAlchemyModel):
{
"matches": [
{
"id": "component/memory_service",
"path_id": "component/memory_service",
"name": "MemoryService",
"entity_type": "component",
"description": "Core service",
@@ -219,7 +202,7 @@ class OpenNodesResponse(SQLAlchemyModel):
{
"entities": [
{
"id": "component/memory_service",
"path_id": "component/memory_service",
"name": "MemoryService",
"entity_type": "component",
"description": "Core service",