diff --git a/api/app/controllers/service/memory_config_api_controller.py b/api/app/controllers/service/memory_config_api_controller.py index 02ffed08..1e61e0af 100644 --- a/api/app/controllers/service/memory_config_api_controller.py +++ b/api/app/controllers/service/memory_config_api_controller.py @@ -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"]) diff --git a/api/app/repositories/memory_config_repository.py b/api/app/repositories/memory_config_repository.py index 3139b851..072be1e2 100644 --- a/api/app/repositories/memory_config_repository.py +++ b/api/app/repositories/memory_config_repository.py @@ -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) diff --git a/api/app/schemas/memory_api_schema.py b/api/app/schemas/memory_api_schema.py index 63ec3354..4cc548f3 100644 --- a/api/app/schemas/memory_api_schema.py +++ b/api/app/schemas/memory_api_schema.py @@ -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() \ No newline at end of file + 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() diff --git a/api/app/schemas/memory_storage_schema.py b/api/app/schemas/memory_storage_schema.py index bfcf6337..24dddd80 100644 --- a/api/app/schemas/memory_storage_schema.py +++ b/api/app/schemas/memory_storage_schema.py @@ -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(