feat(memory-api): add end user management and enhance memory API endpoints
- Add end_user_controller with unauthenticated endpoint for creating end users - Implement get_or_create_end_user logic to handle duplicate end users by other_id - Register end_user_controller router in main controller initialization - Add list_memory_configs endpoint to retrieve all workspace memory configurations - Update MemoryWriteRequest and MemoryReadRequest to make config_id required field - Refactor memory API endpoints to parse request body directly instead of using Body parameter - Add CreateEndUserRequest and CreateEndUserResponse schemas for end user creation - Add ListConfigsResponse schema for configs listing endpoint - Remove unused config_id and llm_model_id parameters from Neo4j write operation - Update .gitignore to exclude redbear-mem-metrics and pitch-deck directories
This commit is contained in:
@@ -84,43 +84,65 @@ class MemoryAPIService:
|
||||
|
||||
if not app:
|
||||
logger.warning(f"App not found for end_user: {end_user_id}")
|
||||
raise ResourceNotFoundException(
|
||||
resource_type="App",
|
||||
resource_id=str(end_user.app_id)
|
||||
)
|
||||
|
||||
if app.workspace_id != workspace_id:
|
||||
logger.warning(
|
||||
f"End user {end_user_id} belongs to workspace {app.workspace_id}, "
|
||||
f"not authorized workspace {workspace_id}"
|
||||
)
|
||||
raise BusinessException(
|
||||
message="End user does not belong to authorized workspace",
|
||||
code=BizCode.FORBIDDEN
|
||||
)
|
||||
# raise ResourceNotFoundException(
|
||||
# resource_type="App",
|
||||
# resource_id=str(end_user.app_id)
|
||||
# )
|
||||
# temporally allow any workspace to access
|
||||
# if end_user.workspace_id != workspace_id:
|
||||
# print(f"[DEBUG] end_user.workspace_id={end_user.workspace_id}, api_key.workspace_id={workspace_id}")
|
||||
# logger.warning(
|
||||
# f"End user {end_user_id} belongs to workspace {end_user.workspace_id}, "
|
||||
# f"not authorized workspace {workspace_id}"
|
||||
# )
|
||||
# raise BusinessException(
|
||||
# message=f"End user does not belong to authorized workspace. end_user.workspace_id={end_user.workspace_id}, api_key.workspace_id={workspace_id}",
|
||||
# code=BizCode.FORBIDDEN
|
||||
# )
|
||||
|
||||
logger.info(f"End user {end_user_id} validated successfully")
|
||||
return end_user
|
||||
|
||||
|
||||
def _update_end_user_config(self, end_user_id: str, config_id: str) -> None:
|
||||
"""Update the end user's memory_config_id.
|
||||
|
||||
Silently updates the config association. Logs warnings on failure
|
||||
but does not raise, so it won't block the main read/write operation.
|
||||
|
||||
Args:
|
||||
end_user_id: End user identifier
|
||||
config_id: Memory configuration ID to assign
|
||||
"""
|
||||
try:
|
||||
config_uuid = uuid.UUID(config_id)
|
||||
from app.repositories.end_user_repository import EndUserRepository
|
||||
end_user_repo = EndUserRepository(self.db)
|
||||
end_user_repo.update_memory_config_id(
|
||||
end_user_id=uuid.UUID(end_user_id),
|
||||
memory_config_id=config_uuid,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to update memory_config_id for end_user {end_user_id}: {e}")
|
||||
|
||||
async def write_memory(
|
||||
self,
|
||||
workspace_id: uuid.UUID,
|
||||
end_user_id: str,
|
||||
message: str,
|
||||
config_id: Optional[str] = None,
|
||||
config_id: str,
|
||||
storage_type: str = "neo4j",
|
||||
user_rag_memory_id: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Write memory with validation.
|
||||
|
||||
Validates end_user exists and belongs to workspace, then delegates
|
||||
to MemoryAgentService.write_memory.
|
||||
Validates end_user exists and belongs to workspace, updates the end user's
|
||||
memory_config_id, then delegates to MemoryAgentService.write_memory.
|
||||
|
||||
Args:
|
||||
workspace_id: Workspace ID for resource validation
|
||||
end_user_id: End user identifier (used as end_user_id)
|
||||
message: Message content to store
|
||||
config_id: Optional memory configuration ID
|
||||
config_id: Memory configuration ID (required)
|
||||
storage_type: Storage backend (neo4j or rag)
|
||||
user_rag_memory_id: Optional RAG memory ID
|
||||
|
||||
@@ -136,7 +158,8 @@ class MemoryAPIService:
|
||||
# Validate end_user exists and belongs to workspace
|
||||
self.validate_end_user(end_user_id, workspace_id)
|
||||
|
||||
# Use end_user_id as end_user_id for memory operations
|
||||
# Update end user's memory_config_id
|
||||
self._update_end_user_config(end_user_id, config_id)
|
||||
|
||||
try:
|
||||
# Delegate to MemoryAgentService
|
||||
@@ -188,21 +211,21 @@ class MemoryAPIService:
|
||||
end_user_id: str,
|
||||
message: str,
|
||||
search_switch: str = "0",
|
||||
config_id: Optional[str] = None,
|
||||
config_id: str = "",
|
||||
storage_type: str = "neo4j",
|
||||
user_rag_memory_id: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Read memory with validation.
|
||||
|
||||
Validates end_user exists and belongs to workspace, then delegates
|
||||
to MemoryAgentService.read_memory.
|
||||
Validates end_user exists and belongs to workspace, updates the end user's
|
||||
memory_config_id, then delegates to MemoryAgentService.read_memory.
|
||||
|
||||
Args:
|
||||
workspace_id: Workspace ID for resource validation
|
||||
end_user_id: End user identifier (used as end_user_id)
|
||||
message: Query message
|
||||
search_switch: Search mode (0=deep search with verification, 1=deep search, 2=fast search)
|
||||
config_id: Optional memory configuration ID
|
||||
config_id: Memory configuration ID (required)
|
||||
storage_type: Storage backend (neo4j or rag)
|
||||
user_rag_memory_id: Optional RAG memory ID
|
||||
|
||||
@@ -218,7 +241,8 @@ class MemoryAPIService:
|
||||
# Validate end_user exists and belongs to workspace
|
||||
self.validate_end_user(end_user_id, workspace_id)
|
||||
|
||||
# Use end_user_id as end_user_id for memory operations
|
||||
# Update end user's memory_config_id
|
||||
self._update_end_user_config(end_user_id, config_id)
|
||||
|
||||
|
||||
try:
|
||||
@@ -256,3 +280,50 @@ class MemoryAPIService:
|
||||
message=f"Memory read failed: {str(e)}",
|
||||
code=BizCode.MEMORY_READ_FAILED
|
||||
)
|
||||
|
||||
def list_memory_configs(
|
||||
self,
|
||||
workspace_id: uuid.UUID,
|
||||
) -> Dict[str, Any]:
|
||||
"""List all memory configs for a workspace.
|
||||
|
||||
Args:
|
||||
workspace_id: Workspace ID from API key authorization
|
||||
|
||||
Returns:
|
||||
Dict with configs list and total count
|
||||
|
||||
Raises:
|
||||
BusinessException: If listing fails
|
||||
"""
|
||||
logger.info(f"Listing memory configs for workspace: {workspace_id}")
|
||||
|
||||
try:
|
||||
from app.repositories.memory_config_repository import MemoryConfigRepository
|
||||
|
||||
results = MemoryConfigRepository.get_all(self.db, workspace_id=workspace_id)
|
||||
|
||||
configs = []
|
||||
for config, scene_name in results:
|
||||
configs.append({
|
||||
"config_id": str(config.config_id),
|
||||
"config_name": config.config_name,
|
||||
"config_desc": config.config_desc,
|
||||
"is_default": config.is_default or False,
|
||||
"scene_name": scene_name,
|
||||
"created_at": config.created_at.isoformat() if config.created_at else None,
|
||||
"updated_at": config.updated_at.isoformat() if config.updated_at else None,
|
||||
})
|
||||
|
||||
logger.info(f"Found {len(configs)} memory configs for workspace {workspace_id}")
|
||||
return {
|
||||
"configs": configs,
|
||||
"total": len(configs),
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list memory configs for workspace {workspace_id}: {e}")
|
||||
raise BusinessException(
|
||||
message=f"Failed to list memory configs: {str(e)}",
|
||||
code=BizCode.MEMORY_READ_FAILED
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user