feat(memory-config): add V1 emotion and reflection engine config endpoints

Add read/update endpoints for emotion engine config (read_config_emotion, update_config_emotion)
Add read/update endpoints for reflection engine config (read_config_reflection, update_config_reflection)
Add EmotionConfigUpdateRequest and ReflectionConfigUpdateRequest schemas
Reuse emotion_config_controller and memory_reflection_controller with ownership verification
This commit is contained in:
miao
2026-04-17 17:35:19 +08:00
parent 0dd8cc5d43
commit f597139913
4 changed files with 221 additions and 21 deletions

View File

@@ -10,7 +10,11 @@ from sqlalchemy.orm import Session
from app.controllers import memory_storage_controller
from app.controllers import memory_forget_controller
from app.controllers import ontology_controller
from app.controllers import emotion_config_controller
from app.controllers import memory_reflection_controller
from app.schemas.memory_storage_schema import ForgettingConfigUpdateRequest
from app.controllers.emotion_config_controller import EmotionConfigUpdate
from app.schemas.memory_reflection_schemas import Memory_Reflection
from app.core.api_key_auth import require_api_key
from app.core.error_codes import BizCode
from app.core.exceptions import BusinessException
@@ -25,6 +29,8 @@ from app.schemas.memory_api_schema import (
ListConfigsResponse,
ConfigCreateRequest,
ConfigUpdateForgettingRequest,
EmotionConfigUpdateRequest,
ReflectionConfigUpdateRequest,
)
from app.schemas.memory_storage_schema import (
ConfigUpdate,
@@ -79,28 +85,28 @@ def _verify_config_ownership(config_id:str, workspace_id:uuid.UUID, db:Session):
code=BizCode.MEMORY_CONFIG_NOT_FOUND,
)
@router.get("/configs")
@require_api_key(scopes=["memory"])
async def list_memory_configs(
request: Request,
api_key_auth: ApiKeyAuth = None,
db: Session = Depends(get_db),
):
"""
List all memory configs for the workspace.
# @router.get("/configs")
# @require_api_key(scopes=["memory"])
# async def list_memory_configs(
# request: Request,
# api_key_auth: ApiKeyAuth = None,
# db: Session = Depends(get_db),
# ):
# """
# List all memory configs for the workspace.
Returns all available memory configurations associated with the authorized workspace.
"""
logger.info(f"List configs request - workspace_id: {api_key_auth.workspace_id}")
# Returns all available memory configurations associated with the authorized workspace.
# """
# logger.info(f"List configs request - workspace_id: {api_key_auth.workspace_id}")
memory_api_service = MemoryAPIService(db)
# memory_api_service = MemoryAPIService(db)
result = memory_api_service.list_memory_configs(
workspace_id=api_key_auth.workspace_id,
)
# result = memory_api_service.list_memory_configs(
# workspace_id=api_key_auth.workspace_id,
# )
logger.info(f"Listed {result['total']} configs for workspace: {api_key_auth.workspace_id}")
return success(data=ListConfigsResponse(**result).model_dump(), msg="Configs listed successfully")
# logger.info(f"Listed {result['total']} configs for workspace: {api_key_auth.workspace_id}")
# return success(data=ListConfigsResponse(**result).model_dump(), msg="Configs listed successfully")
@router.get("/read_all_config")
@require_api_key(scopes=["memory"])
@@ -171,6 +177,85 @@ async def read_config_extracted(
db = db,
)
@router.get("/read_config_forgetting")
@require_api_key(scopes=["memory"])
async def read_config_forgetting(
request: Request,
config_id: str = Query(..., description="config_id"),
api_key_auth: ApiKeyAuth = None,
db: Session = Depends(get_db),
):
"""
Get forgetting settings for a specific memory config.
Only configs belonging to the authorized workspace can be queried.
"""
logger.info(f"V1 read forgetting config - config_id: {config_id}, workspace: {api_key_auth.workspace_id}")
_verify_config_ownership(config_id, api_key_auth.workspace_id, db)
current_user = _get_current_user(api_key_auth, db)
result = await memory_forget_controller.read_forgetting_config(
config_id = config_id,
current_user = current_user,
db = db,
)
return jsonable_encoder(result)
@router.get("/read_config_emotion")
@require_api_key(scopes=["memory"])
async def read_config_emotion(
request: Request,
config_id: str = Query(..., description="config_id"),
api_key_auth: ApiKeyAuth = None,
db: Session = Depends(get_db),
):
"""
Get emotion engine config details for a specific config.
Only configs belonging to the authorized workspace can be queried.
"""
logger.info(f"V1 read emotion config - config_id: {config_id}, workspace: {api_key_auth.workspace_id}")
_verify_config_ownership(config_id, api_key_auth.workspace_id, db)
current_user = _get_current_user(api_key_auth, db)
return jsonable_encoder(emotion_config_controller.get_emotion_config(
config_id=config_id,
db=db,
current_user=current_user,
))
@router.get("/read_config_reflection")
@require_api_key(scopes=["memory"])
async def read_config_reflection(
request: Request,
config_id: str = Query(..., description="config_id"),
api_key_auth: ApiKeyAuth = None,
db: Session = Depends(get_db),
):
"""
Get reflection engine config details for a specific config.
Only configs belonging to the authorized workspace can be queried.
"""
logger.info(f"V1 read reflection config - config_id: {config_id}, workspace: {api_key_auth.workspace_id}")
_verify_config_ownership(config_id, api_key_auth.workspace_id, db)
current_user = _get_current_user(api_key_auth, db)
return jsonable_encoder(await memory_reflection_controller.start_reflection_configs(
config_id=config_id,
current_user=current_user,
db=db,
))
@router.post("/create_config")
@require_api_key(scopes=["memory"])
async def create_memory_config(
@@ -278,6 +363,7 @@ async def update_memory_config_extracted(
current_user = current_user,
db = db,
)
@router.put("/update_config_forgetting")
@require_api_key(scopes=["memory"])
async def update_memory_config_forgetting(
@@ -312,6 +398,66 @@ async def update_memory_config_forgetting(
)
return jsonable_encoder(result)
@router.put("/update_config_emotion")
@require_api_key(scopes=["memory"])
async def update_config_emotion(
request: Request,
api_key_auth: ApiKeyAuth = None,
db: Session = Depends(get_db),
message: str = Body(None, description="Request body"),
):
"""
Update emotion engine config (full update).
All fields except emotion_model_id are required.
Only configs belonging to the authorized workspace can be updated.
"""
body = await request.json()
payload = EmotionConfigUpdateRequest(**body)
logger.info(f"V1 update emotion config - config_id: {payload.config_id}, workspace: {api_key_auth.workspace_id}")
_verify_config_ownership(payload.config_id, api_key_auth.workspace_id, db)
current_user = _get_current_user(api_key_auth, db)
update_fields = payload.model_dump(exclude_unset=True)
mgmt_payload = EmotionConfigUpdate(**update_fields)
return jsonable_encoder(emotion_config_controller.update_emotion_config(
config=mgmt_payload,
db=db,
current_user=current_user,
))
@router.put("/update_config_reflection")
@require_api_key(scopes=["memory"])
async def update_config_reflection(
request: Request,
api_key_auth: ApiKeyAuth = None,
db: Session = Depends(get_db),
message: str = Body(None, description="Request body"),
):
"""
Update reflection engine config (full update).
All fields are required.
Only configs belonging to the authorized workspace can be updated.
"""
body = await request.json()
payload = ReflectionConfigUpdateRequest(**body)
logger.info(f"V1 update reflection config - config_id: {payload.config_id}, workspace: {api_key_auth.workspace_id}")
_verify_config_ownership(payload.config_id, api_key_auth.workspace_id, db)
current_user = _get_current_user(api_key_auth, db)
update_fields = payload.model_dump(exclude_unset=True)
mgmt_payload = Memory_Reflection(**update_fields)
return jsonable_encoder(await memory_reflection_controller.save_reflection_config(
request=mgmt_payload,
current_user=current_user,
db=db,
))
@router.delete("/delete_config")
@require_api_key(scopes=["memory"])

View File

@@ -328,7 +328,7 @@ class MemoryConfigRepository:
if not db_config:
db_logger.warning(f"记忆配置不存在: config_id={update.config_id}")
return None
#TODO部分更新没有用patch请求是在Repository层中用先查再部分更新的方式实现的后续可以考虑改成patch请求更符合RESTful设计原则
update_data = update.model_dump(exclude_unset=True)
update_data.pop("config_id", None)

View File

@@ -388,4 +388,58 @@ class ConfigUpdateForgettingRequest(BaseModel):
def validate_config_id(cls, v: str) -> str:
if not v or not v.strip():
raise ValueError("config_id is required and cannot be empty")
return v.strip()
return v.strip()
class EmotionConfigUpdateRequest(BaseModel):
"""Request schema for updating memory config emotion parameters.
Attributes:
config_id: Configuration UUID to update (required)
emotion_enabled: Whether to enable emotion extraction
emotion_model_id: Emotion analysis model ID
emotion_extract_keywords: Whether to extract emotion keywords
emotion_min_intensity: Minimum emotion intensity threshold (0.0-1.0)
emotion_enable_subject: Whether to enable subject classification for emotions
"""
config_id: str = Field(..., description="Configuration ID (UUID)")
emotion_enabled: bool = Field(..., description="Whether to enable emotion extraction")
emotion_model_id: Optional[str] = Field(None, description="Emotion analysis model ID")
emotion_extract_keywords: bool = Field(..., description="Whether to extract emotion keywords")
emotion_min_intensity: float = Field(..., ge=0.0, le=1.0, description="Minimum emotion intensity threshold")
emotion_enable_subject: bool = Field(..., description="Whether to enable subject classification for emotions")
@field_validator("config_id")
@classmethod
def validate_config_id(cls, v: str) -> str:
if not v or not v.strip():
raise ValueError("config_id is required and cannot be empty")
return v.strip()
class ReflectionConfigUpdateRequest(BaseModel):
"""Request schema for updating memory config reflection parameters.
Attributes:
config_id: Configuration UUID to update (required)
reflection_enabled: Whether to enable self-reflection
reflection_period_in_hours: Reflection iteration period in hours
reflexion_range: Reflection range (partial or all)
baseline: Baseline for reflection (TIME/FACT/TIME-FACT)
reflection_model_id: Reflection model ID
memory_verify: Whether to enable memory verification
quality_assessment: Whether to enable quality assessment
"""
config_id: str = Field(..., description="Configuration ID (UUID)")
reflection_enabled: bool = Field(..., description="Whether to enable self-reflection")
reflection_period_in_hours: str = Field(..., description="Reflection iteration period in hours")
reflexion_range: Literal["partial", "all"] = Field(..., description="Reflection range: partial/all")
baseline: Literal["TIME", "FACT", "TIME-FACT"] = Field(..., description="Baseline: TIME/FACT/TIME-FACT")
reflection_model_id: str = Field(..., description="Reflection model ID")
memory_verify: bool = Field(..., description="Whether to enable memory verification")
quality_assessment: bool = Field(..., description="Whether to enable quality assessment")
@field_validator("config_id")
@classmethod
def validate_config_id(cls, v: str) -> str:
if not v or not v.strip():
raise ValueError("config_id is required and cannot be empty")
return v.strip()

View File

@@ -291,7 +291,7 @@ class ConfigUpdateExtracted(BaseModel): # 更新记忆萃取引擎配置参数
pruning_threshold: Optional[float] = Field(
None, ge=0.0, le=0.9, description="智能语义剪枝阈值0-0.9"
)
#TODO:萃取引擎的更新的更新会带有反思引擎的参数,需判断业务是否需要,不需要可以重构
# 反思配置
enable_self_reflexion: Optional[bool] = Field(None, description="是否启用自我反思")
iteration_period: Optional[Literal["1", "3", "6", "12", "24"]] = Field(