From 4c34cb55b6ab12c6eb6bc0de0b5ad102db59a55b Mon Sep 17 00:00:00 2001 From: zhaoying Date: Thu, 29 Jan 2026 17:50:57 +0800 Subject: [PATCH 1/5] feat(web): model ui update --- .../views/ModelManagement/components/ModelImplement/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/views/ModelManagement/components/ModelImplement/index.tsx b/web/src/views/ModelManagement/components/ModelImplement/index.tsx index 779d6541..2720009c 100644 --- a/web/src/views/ModelManagement/components/ModelImplement/index.tsx +++ b/web/src/views/ModelManagement/components/ModelImplement/index.tsx @@ -74,13 +74,13 @@ const ModelImplement: FC = ({ type, value, onChange }) => { return (
-
{[item.model_name, item.api_key].join(' / ')}
- +
{item.model_name}
handleDelete(item)} >
+
{item.api_key}
{t(`modelNew.${item.provider}`)}
) From 3459a73705e37b0d8bf3ad930bd9fe8f19af49c0 Mon Sep 17 00:00:00 2001 From: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:57:27 +0800 Subject: [PATCH 2/5] Add/develop memory (#243) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 遗漏的历史映射 * 遗漏的历史映射 --- api/app/core/agent/langchain_agent.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/app/core/agent/langchain_agent.py b/api/app/core/agent/langchain_agent.py index ddacb094..eb58c905 100644 --- a/api/app/core/agent/langchain_agent.py +++ b/api/app/core/agent/langchain_agent.py @@ -28,6 +28,8 @@ from langchain.agents import create_agent from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage from langchain_core.tools import BaseTool +from app.utils.config_utils import resolve_config_id + logger = get_business_logger() @@ -196,6 +198,9 @@ class LangChainAgent: 2. 如果只有 user_message:创建单条用户消息 [user](用于历史记忆场景) 3. 每条消息会被转换为独立的 Chunk,保留 speaker 字段 """ + + db = next(get_db()) + actual_config_id=resolve_config_id(actual_config_id, db) if storage_type == "rag": # RAG 模式:组合消息为字符串格式(保持原有逻辑) combined_message = f"user: {user_message}\nassistant: {ai_message}" From 2b9638e7d34f1e5dda5d012532cc6b6de958cc44 Mon Sep 17 00:00:00 2001 From: Timebomb2018 <18868801967@163.com> Date: Thu, 29 Jan 2026 18:06:32 +0800 Subject: [PATCH 3/5] fix(model): bug fix --- api/app/controllers/model_controller.py | 11 +++++++---- api/app/services/model_service.py | 16 ++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/api/app/controllers/model_controller.py b/api/app/controllers/model_controller.py index d76f0b1d..83753744 100644 --- a/api/app/controllers/model_controller.py +++ b/api/app/controllers/model_controller.py @@ -31,7 +31,8 @@ def get_model_types(): @router.get("/provider", response_model=ApiResponse) def get_model_providers(): - return success(msg="获取模型提供商成功", data=list(ModelProvider)) + providers = [p for p in ModelProvider if p != ModelProvider.COMPOSITE] + return success(msg="获取模型提供商成功", data=providers) @router.get("/strategy", response_model=ApiResponse) def get_model_strategies(): @@ -151,7 +152,7 @@ def get_model_plaza_list( type: Optional[ModelType] = Query(None, description="模型类型"), provider: Optional[ModelProvider] = Query(None, description="供应商"), is_official: Optional[bool] = Query(None, description="是否官方模型"), - is_deprecated: Optional[bool] = Query(False, description="是否弃用"), + is_deprecated: Optional[bool] = Query(None, description="是否弃用"), search: Optional[str] = Query(None, description="搜索关键词"), db: Session = Depends(get_db), current_user: User = Depends(get_current_user) @@ -467,11 +468,13 @@ async def create_model_api_key_by_provider( priority=api_key_data.priority, model_config_ids=model_config_ids ) - created_keys = await ModelApiKeyService.create_api_key_by_provider(db=db, data=create_data) + created_keys, failed_models = await ModelApiKeyService.create_api_key_by_provider(db=db, data=create_data) api_logger.info(f"API Key创建成功: 关联{len(created_keys)}个模型") # result_list = [model_schema.ModelApiKey.model_validate(key) for key in created_keys] - return success(data=f"成功为 {len(created_keys)} 个模型创建API Key", msg=f"成功为 {len(created_keys)} 个模型创建API Key") + result = "API Key已存在" if len(created_keys) == 0 and len(failed_models) == 0 else \ + f"成功为 {len(created_keys)} 个模型创建API Key, 失败模型列表{failed_models}" + return success(data=result, msg=f"成功为 {len(created_keys)} 个模型创建API Key") except Exception as e: api_logger.error(f"创建API Key失败: {str(e)}") raise diff --git a/api/app/services/model_service.py b/api/app/services/model_service.py index ceb9cd70..904821c1 100644 --- a/api/app/services/model_service.py +++ b/api/app/services/model_service.py @@ -457,9 +457,11 @@ class ModelApiKeyService: return ModelApiKeyRepository.get_by_model_config(db, model_config_id, is_active) @staticmethod - async def create_api_key_by_provider(db: Session, data: model_schema.ModelApiKeyCreateByProvider) -> List[ModelApiKey]: + async def create_api_key_by_provider(db: Session, data: model_schema.ModelApiKeyCreateByProvider) -> tuple[ + list[Any], list[Any]]: """根据provider为多个ModelConfig创建API Key""" created_keys = [] + failed_models = [] # 记录验证失败的模型 for model_config_id in data.model_config_ids: model_config = ModelConfigRepository.get_by_id(db, model_config_id) @@ -505,10 +507,12 @@ class ModelApiKeyService: test_message="Hello" ) if not validation_result["valid"]: - raise BusinessException( - f"模型配置验证失败: {validation_result['error']}", - BizCode.INVALID_PARAMETER - ) + # 记录验证失败的模型,但不抛出异常 + failed_models.append({ + "model_name": model_name, + "error": validation_result["error"] + }) + continue # 创建API Key api_key_data = ModelApiKeyCreate( @@ -530,7 +534,7 @@ class ModelApiKeyService: for key in created_keys: db.refresh(key) - return created_keys + return created_keys, failed_models @staticmethod async def create_api_key(db: Session, api_key_data: ModelApiKeyCreate) -> ModelApiKey: From 59c5a3973a87f68fdb066a7540cc2d93ad22bf01 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Thu, 29 Jan 2026 19:04:57 +0800 Subject: [PATCH 4/5] feat(web): model ui update --- web/src/i18n/en.ts | 1 + web/src/i18n/zh.ts | 1 + web/src/views/ModelManagement/Square.tsx | 2 +- web/src/views/ModelManagement/components/ModelSquareDetail.tsx | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 0962c4a6..c76e61bf 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -558,6 +558,7 @@ export const en = { item: 'item', apiKeyNum: ' API Keys', official: 'Official', + deprecated: 'Deprecated', llm: 'LLM', chat: 'Chat', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 70899093..ec6f6950 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -1124,6 +1124,7 @@ export const zh = { item: '个', apiKeyNum: '个 API Key', official: '官方', + deprecated: '已弃用', llm: 'LLM', chat: 'Chat', diff --git a/web/src/views/ModelManagement/Square.tsx b/web/src/views/ModelManagement/Square.tsx index f52d158e..8eb67eef 100644 --- a/web/src/views/ModelManagement/Square.tsx +++ b/web/src/views/ModelManagement/Square.tsx @@ -80,7 +80,7 @@ const ModelSquare = forwardRef handleEdit(item)}>{t('modelNew.edit')}} {item.is_added ? - : + : } diff --git a/web/src/views/ModelManagement/components/ModelSquareDetail.tsx b/web/src/views/ModelManagement/components/ModelSquareDetail.tsx index 50b5b64f..9e39f44a 100644 --- a/web/src/views/ModelManagement/components/ModelSquareDetail.tsx +++ b/web/src/views/ModelManagement/components/ModelSquareDetail.tsx @@ -89,7 +89,7 @@ const ModelSquareDetail = forwardRef handleEdit(item)}>{t('modelNew.edit')}} {item.is_added ? - : + : } From ee50b25d06d3f41550eacc37d3983d229126ad24 Mon Sep 17 00:00:00 2001 From: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:27:02 +0800 Subject: [PATCH 5/5] Add/develop memory (#247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 * 遗漏的历史映射 --- .../controllers/memory_forget_controller.py | 8 +- api/app/core/agent/langchain_agent.py | 80 ++++++++++--------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/api/app/controllers/memory_forget_controller.py b/api/app/controllers/memory_forget_controller.py index ea55ea26..2b5ef72f 100644 --- a/api/app/controllers/memory_forget_controller.py +++ b/api/app/controllers/memory_forget_controller.py @@ -84,10 +84,8 @@ async def trigger_forgetting_cycle( connected_config = get_end_user_connected_config(end_user_id, db) config_id = connected_config.get("memory_config_id") - config_id = resolve_config_id(int(config_id), db) + config_id = resolve_config_id((config_id), db) - - if config_id is None: api_logger.warning(f"终端用户 {end_user_id} 未关联记忆配置") return fail(BizCode.INVALID_PARAMETER, f"终端用户 {end_user_id} 未关联记忆配置", "memory_config_id is None") @@ -199,7 +197,7 @@ async def update_forgetting_config( ApiResponse: 包含更新结果的响应 """ workspace_id = current_user.current_workspace_id - payload.config_id=resolve_config_id(int(payload.config_id), db) + payload.config_id=resolve_config_id((payload.config_id), db) # 检查用户是否已选择工作空间 @@ -330,7 +328,7 @@ async def get_forgetting_curve( ApiResponse: 包含遗忘曲线数据的响应 """ workspace_id = current_user.current_workspace_id - request.config_id = resolve_config_id(int(request.config_id), db) + request.config_id = resolve_config_id((request.config_id), db) # 检查用户是否已选择工作空间 if workspace_id is None: api_logger.warning(f"用户 {current_user.username} 尝试获取遗忘曲线但未选择工作空间") diff --git a/api/app/core/agent/langchain_agent.py b/api/app/core/agent/langchain_agent.py index eb58c905..a34c781f 100644 --- a/api/app/core/agent/langchain_agent.py +++ b/api/app/core/agent/langchain_agent.py @@ -177,7 +177,6 @@ class LangChainAgent: # messagss_list.append(f'用户:{query}。AI回复:{aimessages}') # retrieved_content.append({query: aimessages}) # return messagss_list,retrieved_content - async def write(self, storage_type, end_user_id, user_message, ai_message, user_rag_memory_id, actual_end_user_id, actual_config_id): """ 写入记忆(支持结构化消息) @@ -200,49 +199,52 @@ class LangChainAgent: """ db = next(get_db()) - actual_config_id=resolve_config_id(actual_config_id, db) - if storage_type == "rag": - # RAG 模式:组合消息为字符串格式(保持原有逻辑) - combined_message = f"user: {user_message}\nassistant: {ai_message}" - await write_rag(end_user_id, combined_message, user_rag_memory_id) - logger.info(f'RAG_Agent:{end_user_id};{user_rag_memory_id}') - else: - # Neo4j 模式:使用结构化消息列表 - structured_messages = [] + try: + actual_config_id=resolve_config_id(actual_config_id, db) - # 始终添加用户消息(如果不为空) - if user_message: - structured_messages.append({"role": "user", "content": user_message}) + if storage_type == "rag": + # RAG 模式:组合消息为字符串格式(保持原有逻辑) + combined_message = f"user: {user_message}\nassistant: {ai_message}" + await write_rag(end_user_id, combined_message, user_rag_memory_id) + logger.info(f'RAG_Agent:{end_user_id};{user_rag_memory_id}') + else: + # Neo4j 模式:使用结构化消息列表 + structured_messages = [] - # 只有当 AI 回复不为空时才添加 assistant 消息 - if ai_message: - structured_messages.append({"role": "assistant", "content": ai_message}) + # 始终添加用户消息(如果不为空) + if user_message: + structured_messages.append({"role": "user", "content": user_message}) - # 如果没有消息,直接返回 - if not structured_messages: - logger.warning(f"No messages to write for user {actual_end_user_id}") - return + # 只有当 AI 回复不为空时才添加 assistant 消息 + if ai_message: + structured_messages.append({"role": "assistant", "content": ai_message}) - # 调用 Celery 任务,传递结构化消息列表 - # 数据流: - # 1. structured_messages 传递给 write_message_task - # 2. write_message_task 调用 memory_agent_service.write_memory - # 3. write_memory 调用 write_tools.write,传递 messages 参数 - # 4. write_tools.write 调用 get_chunked_dialogs,传递 messages 参数 - # 5. get_chunked_dialogs 为每条消息创建独立的 Chunk,设置 speaker 字段 - # 6. 每个 Chunk 保存到 Neo4j,包含 speaker 字段 - logger.info(f"[WRITE] Submitting Celery task - user={actual_end_user_id}, messages={len(structured_messages)}, config={actual_config_id}") - write_id = write_message_task.delay( - actual_end_user_id, # end_user_id: 用户ID - structured_messages, # message: 结构化消息列表 [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}] - actual_config_id, # config_id: 配置ID - storage_type, # storage_type: "neo4j" - user_rag_memory_id # user_rag_memory_id: RAG记忆ID(Neo4j模式下不使用) - ) - logger.info(f"[WRITE] Celery task submitted - task_id={write_id}") - write_status = get_task_memory_write_result(str(write_id)) - logger.info(f'[WRITE] Task result - user={actual_end_user_id}, status={write_status}') + # 如果没有消息,直接返回 + if not structured_messages: + logger.warning(f"No messages to write for user {actual_end_user_id}") + return + # 调用 Celery 任务,传递结构化消息列表 + # 数据流: + # 1. structured_messages 传递给 write_message_task + # 2. write_message_task 调用 memory_agent_service.write_memory + # 3. write_memory 调用 write_tools.write,传递 messages 参数 + # 4. write_tools.write 调用 get_chunked_dialogs,传递 messages 参数 + # 5. get_chunked_dialogs 为每条消息创建独立的 Chunk,设置 speaker 字段 + # 6. 每个 Chunk 保存到 Neo4j,包含 speaker 字段 + logger.info(f"[WRITE] Submitting Celery task - user={actual_end_user_id}, messages={len(structured_messages)}, config={actual_config_id}") + write_id = write_message_task.delay( + actual_end_user_id, # end_user_id: 用户ID + structured_messages, # message: 结构化消息列表 [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}] + actual_config_id, # config_id: 配置ID + storage_type, # storage_type: "neo4j" + user_rag_memory_id # user_rag_memory_id: RAG记忆ID(Neo4j模式下不使用) + ) + logger.info(f"[WRITE] Celery task submitted - task_id={write_id}") + write_status = get_task_memory_write_result(str(write_id)) + logger.info(f'[WRITE] Task result - user={actual_end_user_id}, status={write_status}') + finally: + db.close() async def chat( self, message: str,