mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
change /knowledge endpoints to use ids
This commit is contained in:
@@ -34,7 +34,6 @@ from basic_memory.schemas.request import (
|
||||
from basic_memory.schemas.response import (
|
||||
SQLAlchemyModel,
|
||||
ObservationResponse,
|
||||
ObservationsResponse,
|
||||
RelationResponse,
|
||||
EntityResponse,
|
||||
CreateEntityResponse,
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user