From 8db4f914d8220aa0f5cba26a67b903fc99a72431 Mon Sep 17 00:00:00 2001 From: lixinyue <2569494688@qq.com> Date: Thu, 22 Jan 2026 18:43:22 +0800 Subject: [PATCH] =?UTF-8?q?config=5Fconfig=E6=9B=BF=E6=8D=A2=E6=88=90memor?= =?UTF-8?q?y=5Fconfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/emotion_config_controller.py | 7 +- .../controllers/memory_forget_controller.py | 3 +- .../memory_reflection_controller.py | 5 +- .../controllers/memory_storage_controller.py | 5 +- .../forgetting_engine/config_utils.py | 10 +- .../forgetting_engine/forgetting_scheduler.py | 3 +- .../forgetting_engine/forgetting_strategy.py | 7 +- .../validators/memory_config_validators.py | 10 +- api/app/core/workflow/nodes/memory/config.py | 5 +- api/app/models/memory_config_model.py | 2 +- .../repositories/memory_config_repository.py | 16 ++- api/app/schemas/emotion_schema.py | 3 +- api/app/schemas/memory_agent_schema.py | 3 + api/app/schemas/memory_config_schema.py | 20 +-- api/app/schemas/memory_reflection_schemas.py | 3 +- api/app/schemas/memory_storage_schema.py | 20 +-- api/app/services/emotion_config_service.py | 6 +- api/app/services/memory_agent_service.py | 130 +++++++----------- api/app/services/memory_config_service.py | 81 ++++------- api/app/services/memory_forget_service.py | 13 +- api/app/tasks.py | 7 +- 21 files changed, 158 insertions(+), 201 deletions(-) diff --git a/api/app/controllers/emotion_config_controller.py b/api/app/controllers/emotion_config_controller.py index 76450d8a..b0015bc2 100644 --- a/api/app/controllers/emotion_config_controller.py +++ b/api/app/controllers/emotion_config_controller.py @@ -12,6 +12,7 @@ from fastapi import APIRouter, Depends, Query, HTTPException, status from pydantic import BaseModel, Field from typing import Optional from sqlalchemy.orm import Session +from uuid import UUID from app.core.response_utils import success from app.dependencies import get_current_user @@ -32,11 +33,11 @@ router = APIRouter( class EmotionConfigQuery(BaseModel): """情绪配置查询请求模型""" - config_id: int = Field(..., description="配置ID") + config_id: UUID = Field(..., description="配置ID") class EmotionConfigUpdate(BaseModel): """情绪配置更新请求模型""" - config_id: int = Field(..., description="配置ID") + config_id: UUID = Field(..., description="配置ID") emotion_enabled: bool = Field(..., description="是否启用情绪提取") emotion_model_id: Optional[str] = Field(None, description="情绪分析专用模型ID") emotion_extract_keywords: bool = Field(..., description="是否提取情绪关键词") @@ -45,7 +46,7 @@ class EmotionConfigUpdate(BaseModel): @router.get("/read_config", response_model=ApiResponse) def get_emotion_config( - config_id: int = Query(..., description="配置ID"), + config_id: UUID = Query(..., description="配置ID"), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): diff --git a/api/app/controllers/memory_forget_controller.py b/api/app/controllers/memory_forget_controller.py index ca628d0c..a71de487 100644 --- a/api/app/controllers/memory_forget_controller.py +++ b/api/app/controllers/memory_forget_controller.py @@ -11,6 +11,7 @@ """ from typing import Optional +from uuid import UUID from fastapi import APIRouter, Depends from sqlalchemy.orm import Session @@ -128,7 +129,7 @@ async def trigger_forgetting_cycle( @router.get("/read_config", response_model=ApiResponse) async def read_forgetting_config( - config_id: int, + config_id: UUID, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): diff --git a/api/app/controllers/memory_reflection_controller.py b/api/app/controllers/memory_reflection_controller.py index f17fcf7f..ccf9485f 100644 --- a/api/app/controllers/memory_reflection_controller.py +++ b/api/app/controllers/memory_reflection_controller.py @@ -1,6 +1,7 @@ import asyncio import time import uuid +from uuid import UUID from app.core.logging_config import get_api_logger from app.core.memory.storage_services.reflection_engine.self_reflexion import ( @@ -156,7 +157,7 @@ async def start_workspace_reflection( @router.get("/reflection/configs") async def start_reflection_configs( - config_id: int, + config_id: uuid.UUID, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ) -> dict: @@ -191,7 +192,7 @@ async def start_reflection_configs( @router.get("/reflection/run") async def reflection_run( - config_id: int, + config_id: UUID, language_type: str = Header(default="zh", alias="X-Language-Type"), current_user: User = Depends(get_current_user), db: Session = Depends(get_db), diff --git a/api/app/controllers/memory_storage_controller.py b/api/app/controllers/memory_storage_controller.py index f4175923..15dbc2ec 100644 --- a/api/app/controllers/memory_storage_controller.py +++ b/api/app/controllers/memory_storage_controller.py @@ -1,5 +1,6 @@ import os from typing import Optional +from uuid import UUID from app.core.error_codes import BizCode from app.core.logging_config import get_api_logger @@ -160,7 +161,7 @@ def create_config( @router.delete("/delete_config", response_model=ApiResponse) # 删除数据库中的内容(按配置名称) def delete_config( - config_id: str, + config_id: UUID, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ) -> dict: @@ -232,7 +233,7 @@ def update_config_extracted( @router.get("/read_config_extracted", response_model=ApiResponse) # 通过查询参数读取某条配置(固定路径) 没有意义的话就删除 def read_config_extracted( - config_id: str, + config_id: UUID, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), ) -> dict: diff --git a/api/app/core/memory/storage_services/forgetting_engine/config_utils.py b/api/app/core/memory/storage_services/forgetting_engine/config_utils.py index 663c89f9..25daa968 100644 --- a/api/app/core/memory/storage_services/forgetting_engine/config_utils.py +++ b/api/app/core/memory/storage_services/forgetting_engine/config_utils.py @@ -11,6 +11,7 @@ Functions: import logging from typing import Optional, Dict, Any +from uuid import UUID from sqlalchemy.orm import Session from app.repositories.memory_config_repository import MemoryConfigRepository @@ -61,7 +62,7 @@ def calculate_forgetting_rate(lambda_time: float, lambda_mem: float) -> float: def load_actr_config_from_db( db: Session, - config_id: Optional[int] = None + config_id: Optional[UUID] = None ) -> Dict[str, Any]: """ 从数据库加载 ACT-R 配置参数 @@ -150,7 +151,7 @@ def load_actr_config_from_db( def create_actr_calculator_from_config( db: Session, - config_id: Optional[int] = None + config_id: Optional[UUID] = None ) -> ACTRCalculator: """ 从数据库配置创建 ACTRCalculator 实例 @@ -168,11 +169,6 @@ def create_actr_calculator_from_config( ValueError: 如果指定的 config_id 不存在 Examples: - >>> from sqlalchemy.orm import Session - >>> db = Session() - >>> calculator = create_actr_calculator_from_config(db, config_id=1) - >>> # 使用计算器 - >>> activation = calculator.calculate_memory_activation(...) """ # 加载配置 config = load_actr_config_from_db(db, config_id) diff --git a/api/app/core/memory/storage_services/forgetting_engine/forgetting_scheduler.py b/api/app/core/memory/storage_services/forgetting_engine/forgetting_scheduler.py index e9d4c144..5a178fc2 100644 --- a/api/app/core/memory/storage_services/forgetting_engine/forgetting_scheduler.py +++ b/api/app/core/memory/storage_services/forgetting_engine/forgetting_scheduler.py @@ -16,6 +16,7 @@ Classes: import logging from typing import Dict, Any, Optional +from uuid import UUID from datetime import datetime from app.core.memory.storage_services.forgetting_engine.forgetting_strategy import ForgettingStrategy @@ -69,7 +70,7 @@ class ForgettingScheduler: end_user_id: Optional[str] = None, max_merge_batch_size: int = 100, min_days_since_access: int = 30, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, db = None ) -> Dict[str, Any]: """ diff --git a/api/app/core/memory/storage_services/forgetting_engine/forgetting_strategy.py b/api/app/core/memory/storage_services/forgetting_engine/forgetting_strategy.py index cde9e115..a8c62dd4 100644 --- a/api/app/core/memory/storage_services/forgetting_engine/forgetting_strategy.py +++ b/api/app/core/memory/storage_services/forgetting_engine/forgetting_strategy.py @@ -13,6 +13,7 @@ Classes: import logging from typing import List, Dict, Any, Optional +from uuid import UUID from datetime import datetime, timedelta from app.repositories.neo4j.neo4j_connector import Neo4jConnector @@ -176,7 +177,7 @@ class ForgettingStrategy: self, statement_node: Dict[str, Any], entity_node: Dict[str, Any], - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, db = None ) -> str: """ @@ -462,7 +463,7 @@ class ForgettingStrategy: statement_text: str, entity_name: str, entity_type: str, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, db = None ) -> str: """ @@ -527,7 +528,7 @@ class ForgettingStrategy: statement_text, entity_name, entity_type ) - async def _get_llm_client(self, db, config_id: int): + async def _get_llm_client(self, db, config_id: UUID): """ 从数据库获取 LLM 客户端 diff --git a/api/app/core/validators/memory_config_validators.py b/api/app/core/validators/memory_config_validators.py index 333572e6..ba26c5f2 100644 --- a/api/app/core/validators/memory_config_validators.py +++ b/api/app/core/validators/memory_config_validators.py @@ -26,7 +26,7 @@ logger = get_config_logger() def _parse_model_id(model_id: Union[str, UUID, None], model_type: str, - config_id: Optional[int] = None, workspace_id: Optional[UUID] = None) -> Optional[UUID]: + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None) -> Optional[UUID]: """Parse model ID from string or UUID.""" if model_id is None: return None @@ -59,7 +59,7 @@ def validate_model_exists_and_active( model_type: str, db: Session, tenant_id: Optional[UUID] = None, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None ) -> tuple[str, bool]: """Validate that a model exists and is active. @@ -166,7 +166,7 @@ def validate_and_resolve_model_id( db: Session, tenant_id: Optional[UUID] = None, required: bool = False, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None ) -> tuple[Optional[UUID], Optional[str]]: """Validate and resolve a model ID, checking existence and active status. @@ -204,7 +204,7 @@ def validate_and_resolve_model_id( def validate_embedding_model( - config_id: int, + config_id: UUID, embedding_id: Union[str, UUID, None], db: Session, tenant_id: Optional[UUID] = None, @@ -256,7 +256,7 @@ def validate_embedding_model( def validate_llm_model( - config_id: int, + config_id: UUID, llm_id: Union[str, UUID, None], db: Session, tenant_id: Optional[UUID] = None, diff --git a/api/app/core/workflow/nodes/memory/config.py b/api/app/core/workflow/nodes/memory/config.py index 987230c1..4c8c43eb 100644 --- a/api/app/core/workflow/nodes/memory/config.py +++ b/api/app/core/workflow/nodes/memory/config.py @@ -1,4 +1,5 @@ import uuid +from uuid import UUID from pydantic import Field from typing import Literal @@ -11,7 +12,7 @@ class MemoryReadNodeConfig(BaseNodeConfig): ... ) - config_id: int = Field( + config_id: UUID = Field( ... ) @@ -26,6 +27,6 @@ class MemoryWriteNodeConfig(BaseNodeConfig): ... ) - config_id: int = Field( + config_id: UUID = Field( ... ) diff --git a/api/app/models/memory_config_model.py b/api/app/models/memory_config_model.py index 55b377e6..710315db 100644 --- a/api/app/models/memory_config_model.py +++ b/api/app/models/memory_config_model.py @@ -9,7 +9,7 @@ class MemoryConfig(Base): __tablename__ = "memory_config" # 主键 - config_id = Column(Integer, primary_key=True, autoincrement=True, comment="配置ID") + config_id = Column(UUID(as_uuid=True), primary_key=True, comment="配置ID") # 基本信息 config_name = Column(String, nullable=False, comment="配置名称") diff --git a/api/app/repositories/memory_config_repository.py b/api/app/repositories/memory_config_repository.py index aca87189..12e564e2 100644 --- a/api/app/repositories/memory_config_repository.py +++ b/api/app/repositories/memory_config_repository.py @@ -9,6 +9,7 @@ Classes: """ import uuid +from uuid import UUID from typing import Dict, List, Optional, Tuple from app.core.exceptions import BusinessException from app.core.logging_config import get_config_logger, get_db_logger @@ -107,7 +108,7 @@ class MemoryConfigRepository: @staticmethod def update_reflection_config( db: Session, - config_id: int, + config_id: uuid.UUID, enable_self_reflexion: bool, iteration_period: str, reflexion_range: str, @@ -151,7 +152,7 @@ class MemoryConfigRepository: return memory_config_obj @staticmethod - def query_reflection_config_by_id(db: Session, config_id: int) -> MemoryConfig: + def query_reflection_config_by_id(db: Session, config_id: uuid.UUID) -> MemoryConfig: """构建反思配置查询语句,通过config_id查询反思配置(SQLAlchemy text() 命名参数) Args: @@ -222,6 +223,7 @@ class MemoryConfigRepository: try: db_config = MemoryConfig( + config_id=uuid.uuid4(), config_name=params.config_name, config_desc=params.config_desc, workspace_id=params.workspace_id, @@ -408,7 +410,7 @@ class MemoryConfigRepository: raise @staticmethod - def get_extracted_config(db: Session, config_id: int) -> Optional[Dict]: + def get_extracted_config(db: Session, config_id: UUID) -> Optional[Dict]: """获取萃取配置,通过主键查询某条配置 Args: @@ -457,7 +459,7 @@ class MemoryConfigRepository: raise @staticmethod - def get_forget_config(db: Session, config_id: int) -> Optional[Dict]: + def get_forget_config(db: Session, config_id: UUID) -> Optional[Dict]: """获取遗忘配置,通过主键查询某条配置 Args: @@ -489,7 +491,7 @@ class MemoryConfigRepository: raise @staticmethod - def get_by_id(db: Session, config_id: int) -> Optional[MemoryConfig]: + def get_by_id(db: Session, config_id: uuid.UUID) -> Optional[MemoryConfig]: """根据ID获取记忆配置 Args: @@ -513,7 +515,7 @@ class MemoryConfigRepository: db_logger.error(f"根据ID查询记忆配置失败: config_id={config_id} - {str(e)}") raise @staticmethod - def get_config_with_workspace(db: Session, config_id: int) -> Optional[tuple]: + def get_config_with_workspace(db: Session, config_id: uuid.UUID) -> Optional[tuple]: """Get memory config and its associated workspace information Args: @@ -664,7 +666,7 @@ class MemoryConfigRepository: raise @staticmethod - def delete(db: Session, config_id: int) -> bool: + def delete(db: Session, config_id: uuid.UUID) -> bool: """删除记忆配置 Args: diff --git a/api/app/schemas/emotion_schema.py b/api/app/schemas/emotion_schema.py index fb523887..13c802b5 100644 --- a/api/app/schemas/emotion_schema.py +++ b/api/app/schemas/emotion_schema.py @@ -1,6 +1,7 @@ """情绪分析相关的请求和响应模型""" from typing import Optional +from uuid import UUID from pydantic import BaseModel, Field class EmotionTagsRequest(BaseModel): @@ -30,7 +31,7 @@ class EmotionHealthRequest(BaseModel): class EmotionSuggestionsRequest(BaseModel): """获取个性化情绪建议请求""" end_user_id: str = Field(..., description="组ID") - config_id: Optional[int] = Field(None, description="配置ID(用于指定LLM模型)") + config_id: Optional[UUID] = Field(None, description="配置ID(用于指定LLM模型)") class EmotionGenerateSuggestionsRequest(BaseModel): diff --git a/api/app/schemas/memory_agent_schema.py b/api/app/schemas/memory_agent_schema.py index e7b1fe65..a83bd3af 100644 --- a/api/app/schemas/memory_agent_schema.py +++ b/api/app/schemas/memory_agent_schema.py @@ -15,3 +15,6 @@ class Write_UserInput(BaseModel): messages: list[dict] end_user_id: str config_id: Optional[str] = None +class End_User_Information(BaseModel): + end_user_name: str # 这是要更新的用户名 + id: str # 宿主ID,用于匹配条件 \ No newline at end of file diff --git a/api/app/schemas/memory_config_schema.py b/api/app/schemas/memory_config_schema.py index 0443dcc4..76acee5c 100644 --- a/api/app/schemas/memory_config_schema.py +++ b/api/app/schemas/memory_config_schema.py @@ -35,7 +35,7 @@ class ConfigurationError(Exception): def __init__( self, message: str, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None, context: Optional[Dict[str, Any]] = None, ): @@ -72,7 +72,7 @@ class WorkspaceNotFoundError(ConfigurationError): def __init__( self, workspace_id: UUID, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, message: Optional[str] = None, ): if message is None: @@ -89,7 +89,7 @@ class ModelNotFoundError(ConfigurationError): self, model_id: Union[str, UUID], model_type: str, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None, message: Optional[str] = None, ): @@ -112,7 +112,7 @@ class ModelInactiveError(ConfigurationError): model_id: Union[str, UUID], model_name: str, model_type: str, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None, message: Optional[str] = None, ): @@ -136,7 +136,7 @@ class InvalidConfigError(ConfigurationError): message: str, field_name: Optional[str] = None, invalid_value: Optional[Any] = None, - config_id: Optional[int] = None, + config_id: Optional[UUID] = None, workspace_id: Optional[UUID] = None, ): context = {} @@ -155,7 +155,7 @@ class InvalidConfigError(ConfigurationError): class MemoryConfigValidation(BaseModel): """Pydantic model for validating memory configuration data from database.""" - config_id: int = Field(..., gt=0, description="Configuration ID must be positive") + config_id: UUID = Field(..., description="Configuration ID (UUID)") config_name: str = Field(..., min_length=1, max_length=255) workspace_id: UUID = Field(..., description="Workspace UUID") workspace_name: str = Field(..., min_length=1, max_length=255) @@ -275,7 +275,7 @@ class ModelValidation(BaseModel): def validate_memory_config_data( - config_data: Dict[str, Any], config_id: Optional[int] = None + config_data: Dict[str, Any], config_id: Optional[UUID] = None ) -> MemoryConfigValidation: """Validate memory configuration data using Pydantic model.""" try: @@ -302,7 +302,7 @@ def validate_memory_config_data( def validate_workspace_data( - workspace_data: Dict[str, Any], config_id: Optional[int] = None + workspace_data: Dict[str, Any], config_id: Optional[UUID] = None ) -> WorkspaceValidation: """Validate workspace data using Pydantic model.""" try: @@ -331,7 +331,7 @@ def validate_workspace_data( def validate_model_data( - model_data: Dict[str, Any], config_id: Optional[int] = None + model_data: Dict[str, Any], config_id: Optional[UUID] = None ) -> ModelValidation: """Validate model data using Pydantic model.""" try: @@ -364,7 +364,7 @@ def validate_model_data( class MemoryConfig: """Immutable memory configuration loaded from database.""" - config_id: int + config_id: UUID config_name: str workspace_id: UUID workspace_name: str diff --git a/api/app/schemas/memory_reflection_schemas.py b/api/app/schemas/memory_reflection_schemas.py index 860f1ef1..df841fb1 100644 --- a/api/app/schemas/memory_reflection_schemas.py +++ b/api/app/schemas/memory_reflection_schemas.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, Field from typing import Optional +from uuid import UUID from enum import Enum @@ -9,7 +10,7 @@ class OptimizationStrategy(str, Enum): ACCURACY_FIRST = "accuracy_first" BALANCED = "balanced" class Memory_Reflection(BaseModel): - config_id: Optional[int] = None + config_id: Optional[UUID] = None reflection_enabled: bool reflection_period_in_hours: str reflexion_range: Optional[str] = "partial" diff --git a/api/app/schemas/memory_storage_schema.py b/api/app/schemas/memory_storage_schema.py index 80b62b8a..e5a4cde6 100644 --- a/api/app/schemas/memory_storage_schema.py +++ b/api/app/schemas/memory_storage_schema.py @@ -159,7 +159,7 @@ class ReflexionResultSchema(BaseModel): # Composite key identifying a config row class ConfigKey(BaseModel): # 配置参数键模型 model_config = ConfigDict(populate_by_name=True, extra="forbid") - config_id: int = Field("config_id", description="配置唯一标识(字符串)") + config_id: uuid.UUID = Field("config_id", description="配置唯一标识(UUID)") user_id: str = Field("user_id", description="用户标识(字符串)") apply_id: str = Field("apply_id", description="应用或场景标识(字符串)") @@ -250,17 +250,17 @@ class ConfigParamsCreate(BaseModel): # 创建配置参数模型(仅 body, class ConfigParamsDelete(BaseModel): # 删除配置参数模型(请求体) model_config = ConfigDict(populate_by_name=True, extra="forbid") # config_name: str = Field("配置名称", description="配置名称(字符串)") - config_id: int = Field("配置ID", description="配置ID(字符串)") + config_id: uuid.UUID = Field("配置ID", description="配置ID(UUID)") class ConfigUpdate(BaseModel): # 更新记忆萃取引擎配置参数时使用的模型 - config_id: Optional[int] = None + config_id: Optional[uuid.UUID] = None config_name: str = Field("配置名称", description="配置名称(字符串)") config_desc: str = Field("配置描述", description="配置描述(字符串)") class ConfigUpdateExtracted(BaseModel): # 更新记忆萃取引擎配置参数时使用的模型 - config_id: Optional[int] = None + config_id: Optional[uuid.UUID] = None llm_id: Optional[str] = Field(None, description="LLM模型配置ID") embedding_id: Optional[str] = Field(None, description="嵌入模型配置ID") rerank_id: Optional[str] = Field(None, description="重排序模型配置ID") @@ -327,14 +327,14 @@ class ConfigUpdateExtracted(BaseModel): # 更新记忆萃取引擎配置参数 class ConfigUpdateForget(BaseModel): # 更新遗忘引擎配置参数时使用的模型 # 遗忘引擎配置参数更新模型 - config_id: Optional[int] = None + config_id: Optional[uuid.UUID] = None lambda_time: Optional[float] = Field(0.5, ge=0.0, le=1.0, description="最低保持度,0-1 小数;默认 0.5") lambda_mem: Optional[float] = Field(0.5, ge=0.0, le=1.0, description="遗忘率,0-1 小数;默认 0.5") offset: Optional[float] = Field(0.0, ge=0.0, le=1.0, description="偏移度,0-1 小数;默认 0.0") class ConfigPilotRun(BaseModel): # 试运行触发请求模型 - config_id: int = Field(..., description="配置ID(唯一)") + config_id: uuid.UUID = Field(..., description="配置ID(唯一)") dialogue_text: str = Field(..., description="前端传入的对话文本,格式如 '用户: ...\nAI: ...' 可多行,试运行必填") model_config = ConfigDict(populate_by_name=True, extra="forbid") @@ -342,7 +342,7 @@ class ConfigPilotRun(BaseModel): # 试运行触发请求模型 class ConfigFilter(BaseModel): # 查询配置参数时使用的模型 model_config = ConfigDict(populate_by_name=True, extra="forbid") - config_id: Optional[int] = None + config_id: Optional[uuid.UUID] = None user_id: Optional[str] = None apply_id: Optional[str] = None @@ -418,7 +418,7 @@ class ForgettingConfigResponse(BaseModel): """遗忘引擎配置响应模型""" model_config = ConfigDict(populate_by_name=True, extra="forbid") - config_id: int = Field(..., description="配置ID") + config_id: uuid.UUID = Field(..., description="配置ID") decay_constant: float = Field(..., description="衰减常数 d") lambda_time: float = Field(..., description="时间衰减参数") lambda_mem: float = Field(..., description="记忆衰减参数") @@ -436,7 +436,7 @@ class ForgettingConfigUpdateRequest(BaseModel): """遗忘引擎配置更新请求模型""" model_config = ConfigDict(populate_by_name=True, extra="forbid") - config_id: int = Field(..., description="配置ID") + config_id: uuid.UUID = Field(..., description="配置ID") decay_constant: Optional[float] = Field(None, ge=0.0, le=1.0, description="衰减常数 d") lambda_time: Optional[float] = Field(None, ge=0.0, le=1.0, description="时间衰减参数") lambda_mem: Optional[float] = Field(None, ge=0.0, le=1.0, description="记忆衰减参数") @@ -511,7 +511,7 @@ class ForgettingCurveRequest(BaseModel): importance_score: float = Field(0.5, ge=0.0, le=1.0, description="重要性分数(0-1)") days: int = Field(60, ge=1, le=365, description="模拟天数(默认60天)") - config_id: Optional[int] = Field(None, description="配置ID(可选,如果为None则使用默认配置)") + config_id: Optional[uuid.UUID] = Field(None, description="配置ID(可选,如果为None则使用默认配置)") class ForgettingCurveResponse(BaseModel): diff --git a/api/app/services/emotion_config_service.py b/api/app/services/emotion_config_service.py index f8b4d22a..9880d4e1 100644 --- a/api/app/services/emotion_config_service.py +++ b/api/app/services/emotion_config_service.py @@ -8,6 +8,8 @@ Classes: """ from typing import Dict, Any +from uuid import UUID + from sqlalchemy.orm import Session from app.models.memory_config_model import MemoryConfig @@ -37,7 +39,7 @@ class EmotionConfigService: self.db = db logger.info("情绪配置服务初始化完成") - def get_emotion_config(self, config_id: int) -> Dict[str, Any]: + def get_emotion_config(self, config_id: UUID) -> Dict[str, Any]: """获取情绪引擎配置 查询指定配置ID的情绪相关配置字段。 @@ -144,7 +146,7 @@ class EmotionConfigService: def update_emotion_config( self, - config_id: int, + config_id: UUID, config_data: Dict[str, Any] ) -> Dict[str, Any]: """更新情绪引擎配置 diff --git a/api/app/services/memory_agent_service.py b/api/app/services/memory_agent_service.py index f8861258..e4ab7087 100644 --- a/api/app/services/memory_agent_service.py +++ b/api/app/services/memory_agent_service.py @@ -9,6 +9,7 @@ import os import re import time import uuid +from uuid import UUID from typing import Any, AsyncGenerator, Dict, List, Optional import redis @@ -266,7 +267,7 @@ class MemoryAgentService: logger.info("Log streaming completed, cleaning up resources") # LogStreamer uses context manager for file handling, so cleanup is automatic - async def write_memory(self, end_user_id: str, messages: str, config_id: Optional[str], db: Session, storage_type: str, user_rag_memory_id: str) -> str: + async def write_memory(self, end_user_id: str, messages: list[dict], config_id: Optional[uuid.UUID], db: Session, storage_type: str, user_rag_memory_id: str) -> str: """ Process write operation with config_id @@ -319,85 +320,52 @@ class MemoryAgentService: raise ValueError(error_msg) - async with make_write_graph() as graph: - config = {"configurable": {"thread_id": end_user_id}} - # Convert structured messages to LangChain messages - langchain_messages = [] - for msg in messages: - if msg['role'] == 'user': - langchain_messages.append(HumanMessage(content=msg['content'])) - elif msg['role'] == 'assistant': - langchain_messages.append(AIMessage(content=msg['content'])) + try: + if storage_type == "rag": + # For RAG storage, convert messages to single string + message_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages]) + result = await write_rag(end_user_id, message_text, user_rag_memory_id) + return result + else: + async with make_write_graph() as graph: + config = {"configurable": {"thread_id": end_user_id}} + # Convert structured messages to LangChain messages + langchain_messages = [] + for msg in messages: + if msg['role'] == 'user': + langchain_messages.append(HumanMessage(content=msg['content'])) + elif msg['role'] == 'assistant': + langchain_messages.append(AIMessage(content=msg['content'])) - # 初始状态 - 包含所有必要字段 - initial_state = { - "messages": langchain_messages, - "end_user_id": end_user_id, - "memory_config": memory_config - } + # 初始状态 - 包含所有必要字段 + initial_state = { + "messages": langchain_messages, + "end_user_id": end_user_id, + "memory_config": memory_config + } - # 获取节点更新信息 - async for update_event in graph.astream( - initial_state, - stream_mode="updates", - config=config - ): - for node_name, node_data in update_event.items(): - if 'save_neo4j' == node_name: - massages = node_data - print(massages) - massagesstatus = massages.get('write_result')['status'] - contents = massages.get('write_result') - # Convert messages back to string for logging - message_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages]) - return self.writer_messages_deal(massagesstatus, start_time, end_user_id, config_id, message_text, contents) - - # try: - # if storage_type == "rag": - # # For RAG storage, convert messages to single string - # message_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages]) - # result = await write_rag(end_user_id, message_text, user_rag_memory_id) - # return result - # else: - # async with make_write_graph() as graph: - # config = {"configurable": {"thread_id": end_user_id}} - # # Convert structured messages to LangChain messages - # langchain_messages = [] - # for msg in messages: - # if msg['role'] == 'user': - # langchain_messages.append(HumanMessage(content=msg['content'])) - # elif msg['role'] == 'assistant': - # langchain_messages.append(AIMessage(content=msg['content'])) - # - # # 初始状态 - 包含所有必要字段 - # initial_state = { - # "messages": langchain_messages, - # "end_user_id": end_user_id, - # "memory_config": memory_config - # } - # - # # 获取节点更新信息 - # async for update_event in graph.astream( - # initial_state, - # stream_mode="updates", - # config=config - # ): - # for node_name, node_data in update_event.items(): - # if 'save_neo4j' == node_name: - # massages = node_data - # massagesstatus = massages.get('write_result')['status'] - # contents = massages.get('write_result') - # # Convert messages back to string for logging - # message_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages]) - # return self.writer_messages_deal(massagesstatus, start_time, end_user_id, config_id, message_text, contents) - # except Exception as e: - # # Ensure proper error handling and logging - # error_msg = f"Write operation failed: {str(e)}" - # logger.error(error_msg) - # if audit_logger: - # duration = time.time() - start_time - # audit_logger.log_operation(operation="WRITE", config_id=config_id, end_user_id=end_user_id, success=False, duration=duration, error=error_msg) - # raise ValueError(error_msg) + # 获取节点更新信息 + async for update_event in graph.astream( + initial_state, + stream_mode="updates", + config=config + ): + for node_name, node_data in update_event.items(): + if 'save_neo4j' == node_name: + massages = node_data + massagesstatus = massages.get('write_result')['status'] + contents = massages.get('write_result') + # Convert messages back to string for logging + message_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages]) + return self.writer_messages_deal(massagesstatus, start_time, end_user_id, config_id, message_text, contents) + except Exception as e: + # Ensure proper error handling and logging + error_msg = f"Write operation failed: {str(e)}" + logger.error(error_msg) + if audit_logger: + duration = time.time() - start_time + audit_logger.log_operation(operation="WRITE", config_id=config_id, end_user_id=end_user_id, success=False, duration=duration, error=error_msg) + raise ValueError(error_msg) @@ -408,7 +376,7 @@ class MemoryAgentService: message: str, history: List[Dict], search_switch: str, - config_id: Optional[str], + config_id: Optional[UUID], db: Session, storage_type: str, user_rag_memory_id: str) -> Dict: @@ -685,7 +653,7 @@ class MemoryAgentService: logger.info(f"Validation successful: Structured message list, count: {len(user_input.messages)}") return user_input.messages - async def classify_message_type(self, message: str, config_id: int, db: Session) -> Dict: + async def classify_message_type(self, message: str, config_id: UUID, db: Session) -> Dict: """ Determine the type of user message (read or write) Updated to eliminate global variables in favor of explicit parameters. @@ -716,7 +684,7 @@ class MemoryAgentService: retrieve_info: str, history: List[Dict], query: str, - config_id: str, + config_id: UUID, db: Session ) -> str: """ diff --git a/api/app/services/memory_config_service.py b/api/app/services/memory_config_service.py index 9afba797..d7f7c8a6 100644 --- a/api/app/services/memory_config_service.py +++ b/api/app/services/memory_config_service.py @@ -23,53 +23,12 @@ from app.schemas.memory_config_schema import ( ModelNotFoundError, ) from sqlalchemy.orm import Session +from uuid import UUID logger = get_logger(__name__) config_logger = get_config_logger() -def _validate_config_id(config_id): - """Validate configuration ID format.""" - if config_id is None: - raise InvalidConfigError( - "Configuration ID cannot be None", - field_name="config_id", - invalid_value=config_id, - ) - - if isinstance(config_id, int): - if config_id <= 0: - raise InvalidConfigError( - f"Configuration ID must be positive: {config_id}", - field_name="config_id", - invalid_value=config_id, - ) - return config_id - - if isinstance(config_id, str): - try: - parsed_id = int(config_id.strip()) - if parsed_id <= 0: - raise InvalidConfigError( - f"Configuration ID must be positive: {parsed_id}", - field_name="config_id", - invalid_value=config_id, - ) - return parsed_id - except ValueError: - raise InvalidConfigError( - f"Invalid configuration ID format: '{config_id}'", - field_name="config_id", - invalid_value=config_id, - ) - - raise InvalidConfigError( - f"Invalid type for configuration ID: expected int or str, got {type(config_id).__name__}", - field_name="config_id", - invalid_value=config_id, - ) - - class MemoryConfigService: """ Centralized service for memory configuration loading and validation. @@ -93,14 +52,14 @@ class MemoryConfigService: def load_memory_config( self, - config_id: int, + config_id: UUID, service_name: str = "MemoryConfigService", ) -> MemoryConfig: """ Load memory configuration from database by config_id. Args: - config_id: Configuration ID from database + config_id: Configuration ID (UUID) from database service_name: Name of the calling service (for logging purposes) Returns: @@ -116,18 +75,34 @@ class MemoryConfigService: extra={ "operation": "load_memory_config", "service": service_name, - "config_id": config_id, + "config_id": str(config_id), }, ) logger.info(f"Loading memory configuration from database: config_id={config_id}") try: - validated_config_id = _validate_config_id(config_id) + # Validate config_id is UUID + if not isinstance(config_id, UUID): + if isinstance(config_id, str): + try: + config_id = UUID(config_id) + except ValueError: + raise InvalidConfigError( + f"Invalid UUID format for config_id: {config_id}", + field_name="config_id", + invalid_value=config_id, + ) + else: + raise InvalidConfigError( + f"config_id must be UUID or valid UUID string, got {type(config_id).__name__}", + field_name="config_id", + invalid_value=config_id, + ) # Step 1: Get config and workspace db_query_start = time.time() - result = MemoryConfigRepository.get_config_with_workspace(self.db, validated_config_id) + result = MemoryConfigRepository.get_config_with_workspace(self.db, config_id) db_query_time = time.time() - db_query_start logger.info(f"[PERF] Config+Workspace query: {db_query_time:.4f}s") if not result: @@ -136,14 +111,14 @@ class MemoryConfigService: "Configuration not found in database", extra={ "operation": "load_memory_config", - "config_id": validated_config_id, + "config_id": str(config_id), "load_result": "not_found", "elapsed_ms": elapsed_ms, "service": service_name, }, ) raise ConfigurationError( - f"Configuration {validated_config_id} not found in database" + f"Configuration {config_id} not found in database" ) memory_config, workspace = result @@ -151,7 +126,7 @@ class MemoryConfigService: # Step 2: Validate embedding model (returns both UUID and name) embed_start = time.time() embedding_uuid, embedding_name = validate_embedding_model( - validated_config_id, + config_id, memory_config.embedding_id, self.db, workspace.tenant_id, @@ -168,7 +143,7 @@ class MemoryConfigService: self.db, workspace.tenant_id, required=True, - config_id=validated_config_id, + config_id=config_id, workspace_id=workspace.id, ) llm_time = time.time() - llm_start @@ -185,7 +160,7 @@ class MemoryConfigService: self.db, workspace.tenant_id, required=False, - config_id=validated_config_id, + config_id=config_id, workspace_id=workspace.id, ) rerank_time = time.time() - rerank_start @@ -243,7 +218,7 @@ class MemoryConfigService: extra={ "operation": "load_memory_config", "service": service_name, - "config_id": validated_config_id, + "config_id": str(config_id), "config_name": config.config_name, "workspace_id": str(config.workspace_id), "load_result": "success", diff --git a/api/app/services/memory_forget_service.py b/api/app/services/memory_forget_service.py index 204f5df1..e1030b24 100644 --- a/api/app/services/memory_forget_service.py +++ b/api/app/services/memory_forget_service.py @@ -12,6 +12,7 @@ from typing import Optional, Dict, Any, Tuple from datetime import datetime, timezone +from uuid import UUID from sqlalchemy.orm import Session @@ -87,7 +88,7 @@ class MemoryForgetService: async def _get_forgetting_components( self, db: Session, - config_id: Optional[int] = None + config_id: Optional[UUID] = None ) -> Tuple[ACTRCalculator, ForgettingStrategy, ForgettingScheduler, Dict[str, Any]]: """ 获取遗忘引擎组件(计算器、策略、调度器) @@ -294,7 +295,7 @@ class MemoryForgetService: end_user_id: str, max_merge_batch_size: Optional[int] = None, min_days_since_access: Optional[int] = None, - config_id: Optional[int] = None + config_id: Optional[UUID] = None ) -> Dict[str, Any]: """ 手动触发遗忘周期 @@ -389,7 +390,7 @@ class MemoryForgetService: def read_forgetting_config( self, db: Session, - config_id: int + config_id: UUID ) -> Dict[str, Any]: """ 获取遗忘引擎配置 @@ -416,7 +417,7 @@ class MemoryForgetService: def update_forgetting_config( self, db: Session, - config_id: int, + config_id: UUID, update_fields: Dict[str, Any] ) -> Dict[str, Any]: """ @@ -466,7 +467,7 @@ class MemoryForgetService: self, db: Session, end_user_id: Optional[str] = None, - config_id: Optional[int] = None + config_id: Optional[UUID] = None ) -> Dict[str, Any]: """ 获取遗忘引擎统计信息 @@ -677,7 +678,7 @@ class MemoryForgetService: db: Session, importance_score: float, days: int, - config_id: Optional[int] = None + config_id: Optional[UUID] = None ) -> Dict[str, Any]: """ 获取遗忘曲线数据 diff --git a/api/app/tasks.py b/api/app/tasks.py index f4b5f78f..3ef2653a 100644 --- a/api/app/tasks.py +++ b/api/app/tasks.py @@ -4,6 +4,7 @@ import os import re import time import uuid +from uuid import UUID from datetime import datetime, timezone from math import ceil from typing import Any, Dict, List, Optional @@ -382,7 +383,7 @@ def build_graphrag_for_kb(kb_id: uuid.UUID): @celery_app.task(name="app.core.memory.agent.read_message", bind=True) -def read_message_task(self, end_user_id: str, message: str, history: List[Dict[str, Any]], search_switch: str, config_id: str,storage_type:str,user_rag_memory_id:str) -> Dict[str, Any]: +def read_message_task(self, end_user_id: str, message: str, history: List[Dict[str, Any]], search_switch: str, config_id: uuid.UUID, storage_type:str, user_rag_memory_id:str) -> Dict[str, Any]: """Celery task to process a read message via MemoryAgentService. @@ -472,7 +473,7 @@ def read_message_task(self, end_user_id: str, message: str, history: List[Dict[s @celery_app.task(name="app.core.memory.agent.write_message", bind=True) -def write_message_task(self, end_user_id: str, message: str, config_id: str,storage_type:str,user_rag_memory_id:str) -> Dict[str, Any]: +def write_message_task(self, end_user_id: str, message: str, config_id: uuid.UUID, storage_type:str, user_rag_memory_id:str) -> Dict[str, Any]: """Celery task to process a write message via MemoryAgentService. Args: @@ -1084,7 +1085,7 @@ def workspace_reflection_task(self) -> Dict[str, Any]: @celery_app.task(name="app.tasks.run_forgetting_cycle_task", bind=True) -def run_forgetting_cycle_task(self, config_id: Optional[int] = None) -> Dict[str, Any]: +def run_forgetting_cycle_task(self, config_id: Optional[uuid.UUID] = None) -> Dict[str, Any]: """定时任务:运行遗忘周期 定期执行遗忘周期,识别并融合低激活值的知识节点。