From 255fb07615943e5b26b4ce19592a90d0fd36f38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BF=8A=E7=94=B7?= Date: Wed, 14 Jan 2026 11:27:23 +0800 Subject: [PATCH 01/32] fix(custome tool): get the name of the custom tool schema --- api/app/core/tools/custom/schema_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/core/tools/custom/schema_parser.py b/api/app/core/tools/custom/schema_parser.py index a22e2cfa..11e38b30 100644 --- a/api/app/core/tools/custom/schema_parser.py +++ b/api/app/core/tools/custom/schema_parser.py @@ -223,7 +223,7 @@ class OpenAPISchemaParser: operations[operation_id] = { "method": method.upper(), "path": path, - "summary": operation.get("summary", ""), + "summary": operation_id, "description": operation.get("description", ""), "parameters": self._extract_parameters(operation), "request_body": self._extract_request_body(operation), From 2e3e0f4ce98a5514ac10023d5cc2498e091c1015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BF=8A=E7=94=B7?= Date: Wed, 14 Jan 2026 11:41:45 +0800 Subject: [PATCH 02/32] fix(custome tool): get the name of the custom tool schema --- api/app/core/tools/custom/schema_parser.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/api/app/core/tools/custom/schema_parser.py b/api/app/core/tools/custom/schema_parser.py index 11e38b30..ea261dba 100644 --- a/api/app/core/tools/custom/schema_parser.py +++ b/api/app/core/tools/custom/schema_parser.py @@ -10,9 +10,6 @@ from app.core.logging_config import get_business_logger logger = get_business_logger() -# 为了兼容性,创建别名 -# SchemaParser = OpenAPISchemaParser = None - class OpenAPISchemaParser: """OpenAPI Schema解析器 - 解析OpenAPI 3.0规范""" @@ -213,7 +210,9 @@ class OpenAPISchemaParser: if not isinstance(operation, dict): continue - + + summary = operation.get("summary", "") + # 生成操作ID operation_id = operation.get("operationId") if not operation_id: @@ -223,7 +222,7 @@ class OpenAPISchemaParser: operations[operation_id] = { "method": method.upper(), "path": path, - "summary": operation_id, + "summary": summary if summary else operation_id, "description": operation.get("description", ""), "parameters": self._extract_parameters(operation), "request_body": self._extract_request_body(operation), From 93ff64f1304d0fd4556b20f13127684e11ee5f55 Mon Sep 17 00:00:00 2001 From: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:36:26 +0800 Subject: [PATCH 03/32] Fix/memory bug fix (#111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 --- api/app/schemas/memory_episodic_schema.py | 42 +++++++++++++++++++ .../memory_entity_relationship_service.py | 10 +++-- api/app/services/user_memory_service.py | 15 +++++-- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/api/app/schemas/memory_episodic_schema.py b/api/app/schemas/memory_episodic_schema.py index 7b3f3d2d..832bf34b 100644 --- a/api/app/schemas/memory_episodic_schema.py +++ b/api/app/schemas/memory_episodic_schema.py @@ -1,9 +1,51 @@ """ 情景记忆的请求和响应模型 """ +from abc import ABC from pydantic import BaseModel, Field from typing import Optional +type_mapping = { + "Person": "人物实体节点", + "Organization": "组织实体节点", + "ORG": "组织实体节点", + "Location": "地点实体节点", + "LOC": "地点实体节点", + "Event": "事件实体节点", + "Concept": "概念实体节点", + "Time": "时间实体节点", + "Position": "职位实体节点", + "WorkRole": "职业实体节点", + "System": "系统实体节点", + "Policy": "政策实体节点", + "HistoricalPeriod": "历史时期实体节点", + "HistoricalState": "历史国家实体节点", + "HistoricalEvent": "历史事件实体节点", + "EconomicFactor": "经济因素实体节点", + "Condition": "条件实体节点", + "Numeric": "数值实体节点" + } +class EmotionType(ABC): + JOY_TYPE = "joy" + SURPRISE_TYPE = "surprise" + SANDROWNESS_TYPE = "sadness" + FEAR_TYPE = "fear" + ANGET_TYPE="anger" + NEUTRAL_TYPE="neutral" + EMOTION_MAPPING={ + "joy":"愉快", + "surprise":"惊喜", + "sadness":"悲伤", + "fear":"恐惧", + "anger":"生气", + "neutral":"中性" + } +class EmotionSubject(ABC): + SUBJECT_MAPPING={ + "self":"自己", + "other":"别人", + "object":"事物对象" + } class EpisodicMemoryOverviewRequest(BaseModel): """情景记忆总览查询请求""" diff --git a/api/app/services/memory_entity_relationship_service.py b/api/app/services/memory_entity_relationship_service.py index f650217d..ca97fb39 100644 --- a/api/app/services/memory_entity_relationship_service.py +++ b/api/app/services/memory_entity_relationship_service.py @@ -15,6 +15,8 @@ from neo4j.time import DateTime as Neo4jDateTime import json from datetime import datetime +from app.schemas.memory_episodic_schema import EmotionType + logger = logging.getLogger(__name__) class MemoryEntityService: @@ -123,7 +125,7 @@ class MemoryEntityService: extracted_entity_list = self._deduplicate_dict_list(extracted_entity_list) # 合并所有数据并处理相同text的合并 - all_timeline_data = memory_summary_list + statement_list + extracted_entity_list + all_timeline_data = memory_summary_list + statement_list all_timeline_data = self._merge_same_text_items(all_timeline_data) result = { @@ -496,11 +498,11 @@ class MemoryEmotion: length_data.append(emotion_intensity) if emotion_type is not None and emotion_intensity is not None and formatted_created_at is not None: # 使用(emotion_type, created_at)作为分组键 - if emotion_type in {"joy", "surprise"}: + if emotion_type in {EmotionType.JOY_TYPE, EmotionType.SURPRISE_TYPE}: emotion_type='positive' - elif emotion_type in {"sadness", "fear", "anger"}: + elif emotion_type in {EmotionType.SANDROWNESS_TYPE, EmotionType.FEAR_TYPE, EmotionType.ANGET_TYPE}: emotion_type='negative' - elif emotion_type=='neutral': + elif emotion_type==EmotionType.NEUTRAL_TYPE: emotion_type='neutral' group_key = (emotion_type, formatted_created_at) # 累加emotion_intensity diff --git a/api/app/services/user_memory_service.py b/api/app/services/user_memory_service.py index 59bbc211..5011e83e 100644 --- a/api/app/services/user_memory_service.py +++ b/api/app/services/user_memory_service.py @@ -15,6 +15,8 @@ from app.core.memory.utils.llm.llm_utils import MemoryClientFactory from app.db import get_db_context from app.repositories.end_user_repository import EndUserRepository from app.repositories.neo4j.neo4j_connector import Neo4jConnector +from app.schemas.memory_episodic_schema import type_mapping, EmotionType, EmotionSubject + from app.services.memory_base_service import MemoryBaseService from app.services.memory_config_service import MemoryConfigService from pydantic import BaseModel, Field @@ -1332,7 +1334,7 @@ async def analytics_graph_data( db: Session, end_user_id: str, node_types: Optional[List[str]] = None, - limit: int = 100, + limit: int = 130, depth: int = 1, center_node_id: Optional[str] = None ) -> Dict[str, Any]: @@ -1416,12 +1418,14 @@ async def analytics_graph_data( elementId(n) as id, labels(n)[0] as label, properties(n) as properties + LIMIT $limit """ node_params = { "group_id": end_user_id, - # "limit": limit + "limit": limit } + # 执行节点查询 node_results = await _neo4j_connector.execute_query(node_query, **node_params) @@ -1576,10 +1580,15 @@ async def _extract_node_properties(label: str, properties: Dict[str, Any],node_ for field in allowed_fields: if field in properties: value = properties[field] + if str(field) == 'entity_type': + value=type_mapping.get(value,'') + if str(field)=="emotion_type": + value=EmotionType.EMOTION_MAPPING.get(value) + if str(field)=="emotion_subject": + value=EmotionSubject.SUBJECT_MAPPING.get(value) # 清理 Neo4j 特殊类型 filtered_props[field] = _clean_neo4j_value(value) filtered_props['associative_memory']=[i['rel_count'] for i in node_results][0] - print(filtered_props) return filtered_props From 271d6b5d8d110c5a8a150b6c9e51cf189641ef3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=90=E5=8A=9B=E9=BD=90?= <162269739+lanceyq@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:52:11 +0800 Subject: [PATCH 04/32] Refactor/memory statistics (#112) * [refactor]Reconstructing forgotten, emotional, situational, and explicit memory statistics * [refactor]Reconstructing forgotten, emotional, situational, and explicit memory statistics * [changes]Improve the code based on AI review * [changes]Statistics on work, perception, short-term, and implicit memory * [changes]Statistics on work, perception, short-term, and implicit memory * [changes]Replace the invisible memory calculation method * [changes]Statistics on work, perception, short-term, and implicit memory * [refactor]Reconstructing forgotten, emotional, situational, and explicit memory statistics * [changes]Statistics on work, perception, short-term, and implicit memory * [changes]Replace the invisible memory calculation method --- api/app/services/user_memory_service.py | 131 +++++++++++++++--------- 1 file changed, 84 insertions(+), 47 deletions(-) diff --git a/api/app/services/user_memory_service.py b/api/app/services/user_memory_service.py index 5011e83e..8f25f477 100644 --- a/api/app/services/user_memory_service.py +++ b/api/app/services/user_memory_service.py @@ -13,12 +13,14 @@ from typing import Any, Dict, List, Optional, Tuple from app.core.logging_config import get_logger from app.core.memory.utils.llm.llm_utils import MemoryClientFactory from app.db import get_db_context +from app.repositories.conversation_repository import ConversationRepository from app.repositories.end_user_repository import EndUserRepository from app.repositories.neo4j.neo4j_connector import Neo4jConnector -from app.schemas.memory_episodic_schema import type_mapping, EmotionType, EmotionSubject - +from app.services.implicit_memory_service import ImplicitMemoryService from app.services.memory_base_service import MemoryBaseService from app.services.memory_config_service import MemoryConfigService +from app.services.memory_perceptual_service import MemoryPerceptualService +from app.services.memory_short_service import ShortService from pydantic import BaseModel, Field from sqlalchemy.orm import Session @@ -1198,18 +1200,17 @@ async def analytics_memory_types( end_user_id: Optional[str] = None ) -> List[Dict[str, Any]]: """ - 统计9种记忆类型的数量和百分比 + 统计8种记忆类型的数量和百分比 计算规则: - 1. 感知记忆 (PERCEPTUAL_MEMORY) = statement + entity - 2. 工作记忆 (WORKING_MEMORY) = chunk + entity - 3. 短期记忆 (SHORT_TERM_MEMORY) = chunk - 4. 长期记忆 (LONG_TERM_MEMORY) = entity - 5. 显性记忆 (EXPLICIT_MEMORY) = 情景记忆 + 语义记忆(通过 MemoryBaseService.get_explicit_memory_count 获取) - 6. 隐性记忆 (IMPLICIT_MEMORY) = 1/3 * entity - 7. 情绪记忆 (EMOTIONAL_MEMORY) = 情绪标签统计总数(通过 MemoryBaseService.get_emotional_memory_count 获取) - 8. 情景记忆 (EPISODIC_MEMORY) = memory_summary(通过 MemoryBaseService.get_episodic_memory_count 获取) - 9. 遗忘记忆 (FORGET_MEMORY) = 激活值低于阈值的节点数(通过 MemoryBaseService.get_forget_memory_count 获取) + 1. 感知记忆 (PERCEPTUAL_MEMORY) = 通过 MemoryPerceptualService.get_memory_count 获取的 total_count + 2. 工作记忆 (WORKING_MEMORY) = 会话数量(通过 ConversationRepository.get_conversation_by_user_id 获取) + 3. 短期记忆 (SHORT_TERM_MEMORY) = /short_term 接口返回的问答对数量 + 4. 显性记忆 (EXPLICIT_MEMORY) = 情景记忆 + 语义记忆(通过 MemoryBaseService.get_explicit_memory_count 获取) + 5. 隐性记忆 (IMPLICIT_MEMORY) = Statement 节点数量的三分之一 + 6. 情绪记忆 (EMOTIONAL_MEMORY) = 情绪标签统计总数(通过 MemoryBaseService.get_emotional_memory_count 获取) + 7. 情景记忆 (EPISODIC_MEMORY) = memory_summary(通过 MemoryBaseService.get_episodic_memory_count 获取) + 8. 遗忘记忆 (FORGET_MEMORY) = 激活值低于阈值的节点数(通过 MemoryBaseService.get_forget_memory_count 获取) Args: db: 数据库会话 @@ -1229,7 +1230,6 @@ async def analytics_memory_types( - PERCEPTUAL_MEMORY: 感知记忆 - WORKING_MEMORY: 工作记忆 - SHORT_TERM_MEMORY: 短期记忆 - - LONG_TERM_MEMORY: 长期记忆 - EXPLICIT_MEMORY: 显性记忆 - IMPLICIT_MEMORY: 隐性记忆 - EMOTIONAL_MEMORY: 情绪记忆 @@ -1239,40 +1239,78 @@ async def analytics_memory_types( # 初始化基础服务 base_service = MemoryBaseService() - # 定义需要查询的基础节点类型 - node_types = { - "Statement": "Statement", - "Entity": "ExtractedEntity", - "Chunk": "Chunk" - } + # 初始化感知记忆服务 + perceptual_service = MemoryPerceptualService(db) - # 存储每种节点类型的计数 - node_counts = {} + # 获取感知记忆数量 + if end_user_id: + perceptual_stats = perceptual_service.get_memory_count(uuid.UUID(end_user_id)) + perceptual_count = perceptual_stats.get("total", 0) + else: + perceptual_count = 0 - # 查询每种节点类型的数量 - for key, node_type in node_types.items(): - if end_user_id: - query = f""" - MATCH (n:{node_type}) + # 获取工作记忆数量(基于会话数量) + work_count = 0 + if end_user_id: + try: + conversation_repo = ConversationRepository(db) + conversations = conversation_repo.get_conversation_by_user_id( + user_id=uuid.UUID(end_user_id), + limit=100, # 获取更多会话以准确统计 + is_activate=True + ) + work_count = len(conversations) + logger.debug(f"工作记忆数量(会话数): {work_count} (end_user_id={end_user_id})") + except Exception as e: + logger.warning(f"获取会话数量失败,工作记忆数量设为0: {str(e)}") + work_count = 0 + + # 获取隐性记忆数量(基于 Statement 节点数量的三分之一) + implicit_count = 0 + if end_user_id: + try: + # 查询 Statement 节点数量 + query = """ + MATCH (n:Statement) WHERE n.group_id = $group_id RETURN count(n) as count """ result = await _neo4j_connector.execute_query(query, group_id=end_user_id) - else: - query = f""" - MATCH (n:{node_type}) - RETURN count(n) as count - """ - result = await _neo4j_connector.execute_query(query) - - # 提取计数结果 - count = result[0]["count"] if result and len(result) > 0 else 0 - node_counts[key] = count + statement_count = result[0]["count"] if result and len(result) > 0 else 0 + # 取三分之一作为隐性记忆数量 + implicit_count = round(statement_count / 3) + logger.debug(f"隐性记忆数量(Statement数量的1/3): {implicit_count} (Statement总数={statement_count}, end_user_id={end_user_id})") + except Exception as e: + logger.warning(f"获取Statement数量失败,隐性记忆数量设为0: {str(e)}") + implicit_count = 0 - # 获取各节点类型的数量 - statement_count = node_counts.get("Statement", 0) - entity_count = node_counts.get("Entity", 0) - chunk_count = node_counts.get("Chunk", 0) + # 原有的基于行为习惯的统计方式(已注释) + # implicit_count = 0 + # if end_user_id: + # try: + # implicit_service = ImplicitMemoryService(db, end_user_id) + # behavior_habits = await implicit_service.get_behavior_habits( + # user_id=end_user_id + # ) + # implicit_count = len(behavior_habits) + # logger.debug(f"隐性记忆数量(行为习惯数): {implicit_count} (end_user_id={end_user_id})") + # except Exception as e: + # logger.warning(f"获取行为习惯数量失败,隐性记忆数量设为0: {str(e)}") + # implicit_count = 0 + + # 获取短期记忆数量(基于 /short_term 接口返回的问答对数量) + short_term_count = 0 + if end_user_id: + try: + short_term_service = ShortService(end_user_id) + short_term_data = short_term_service.get_short_databasets() + # 统计 short_term 数组的长度 + if short_term_data: + short_term_count = len(short_term_data) + logger.debug(f"短期记忆数量(问答对数): {short_term_count} (end_user_id={end_user_id})") + except Exception as e: + logger.warning(f"获取短期记忆数量失败,短期记忆数量设为0: {str(e)}") + short_term_count = 0 # 获取用户的遗忘阈值配置 forgetting_threshold = 0.3 # 默认值 @@ -1298,17 +1336,16 @@ async def analytics_memory_types( # 使用 MemoryBaseService 的共享方法获取特殊记忆类型的数量 episodic_count = await base_service.get_episodic_memory_count(end_user_id) explicit_count = await base_service.get_explicit_memory_count(end_user_id) - emotion_count = await base_service.get_emotional_memory_count(end_user_id, statement_count) + emotion_count = await base_service.get_emotional_memory_count(end_user_id, perceptual_count) forget_count = await base_service.get_forget_memory_count(end_user_id, forgetting_threshold) - # 按规则计算9种记忆类型的数量(使用英文枚举作为key) + # 按规则计算8种记忆类型的数量(使用英文枚举作为key) memory_counts = { - "PERCEPTUAL_MEMORY": statement_count + entity_count, # 感知记忆 - "WORKING_MEMORY": chunk_count + entity_count, # 工作记忆 - "SHORT_TERM_MEMORY": chunk_count, # 短期记忆 - "LONG_TERM_MEMORY": entity_count, # 长期记忆 + "PERCEPTUAL_MEMORY": perceptual_count, # 感知记忆 + "WORKING_MEMORY": work_count, # 工作记忆(基于会话数量) + "SHORT_TERM_MEMORY": short_term_count, # 短期记忆(基于问答对数量) "EXPLICIT_MEMORY": explicit_count, # 显性记忆(情景记忆 + 语义记忆) - "IMPLICIT_MEMORY": entity_count // 3, # 隐性记忆 (1/3 entity) + "IMPLICIT_MEMORY": implicit_count, # 隐性记忆(Statement数量的1/3) "EMOTIONAL_MEMORY": emotion_count, # 情绪记忆(使用情绪标签统计) "EPISODIC_MEMORY": episodic_count, # 情景记忆 "FORGET_MEMORY": forget_count # 遗忘记忆(激活值低于阈值) From 6452733c4eb0ea2c6cdcb6b21ea470ae48cb2183 Mon Sep 17 00:00:00 2001 From: Ke Sun Date: Wed, 14 Jan 2026 15:58:24 +0800 Subject: [PATCH 05/32] fix(memory): simplify summary tool by removing LLM processing - Remove template_service extraction and template rendering logic - Remove LLM client initialization from MemoryClientFactory - Remove structured response call to LLM with RetrieveSummaryResponse model - Replace LLM-based answer generation with direct retrieval information - Simplify response to use raw retrieved info or default fallback message - Update logging to reflect non-LLM quick answer approach - Reduce unnecessary dependencies and improve performance by eliminating LLM call overhead --- .../agent/mcp_server/tools/summary_tools.py | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/api/app/core/memory/agent/mcp_server/tools/summary_tools.py b/api/app/core/memory/agent/mcp_server/tools/summary_tools.py index 6d5012f1..0f306572 100644 --- a/api/app/core/memory/agent/mcp_server/tools/summary_tools.py +++ b/api/app/core/memory/agent/mcp_server/tools/summary_tools.py @@ -425,15 +425,9 @@ async def Input_Summary( try: # Extract services from context - template_service = get_context_resource(ctx, "template_service") session_service = get_context_resource(ctx, "session_service") search_service = get_context_resource(ctx, "search_service") - # Get LLM client from memory_config - with get_db_context() as db: - factory = MemoryClientFactory(db) - llm_client = factory.get_llm_client_from_config(memory_config) - # Resolve session ID sessionid = Resolve_username(usermessages) or "" sessionid = sessionid.replace('call_id_', '') @@ -539,31 +533,11 @@ async def Input_Summary( ) retrieve_info, question, raw_results = "", query, [] + # Return retrieved information directly without LLM processing + # Use the raw retrieved info as the answer + aimessages = retrieve_info if retrieve_info else "信息不足,无法回答" - # Render template - system_prompt = await template_service.render_template( - template_name='Retrieve_Summary_prompt.jinja2', - operation_name='input_summary', - query=query, - history=history, - retrieve_info=retrieve_info - ) - - # Call LLM with structured response - try: - structured = await llm_client.response_structured( - messages=[{"role": "system", "content": system_prompt}], - response_model=RetrieveSummaryResponse - ) - aimessages = structured.data.query_answer or "信息不足,无法回答" - except Exception as e: - logger.error( - f"Input_Summary: response_structured failed, using default answer: {e}", - exc_info=True - ) - aimessages = "信息不足,无法回答" - - logger.info(f"Quick answer summary: {storage_type}--{user_rag_memory_id}--{aimessages}") + logger.info(f"Quick answer (no LLM): {storage_type}--{user_rag_memory_id}--{aimessages[:500]}...") # Emit intermediate output for frontend return { From 567624c323d57430e22657aa048eb83f4c573815 Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Wed, 14 Jan 2026 16:35:46 +0800 Subject: [PATCH 06/32] feat(workflow): add session context memory support to LLM nodes --- api/app/controllers/workflow_controller.py | 85 ++++++----- api/app/core/config.py | 37 ++--- api/app/core/workflow/executor.py | 3 +- api/app/core/workflow/nodes/base_node.py | 10 +- api/app/core/workflow/nodes/end/node.py | 52 ++++++- api/app/core/workflow/nodes/llm/config.py | 26 +++- api/app/core/workflow/nodes/llm/node.py | 19 ++- api/app/schemas/app_schema.py | 2 + api/app/services/app_chat_service.py | 161 ++++----------------- api/app/services/workflow_service.py | 79 ++++++++-- api/pyproject.toml | 3 +- 11 files changed, 249 insertions(+), 228 deletions(-) diff --git a/api/app/controllers/workflow_controller.py b/api/app/controllers/workflow_controller.py index 429aa67e..c6d9ddab 100644 --- a/api/app/controllers/workflow_controller.py +++ b/api/app/controllers/workflow_controller.py @@ -39,11 +39,11 @@ router = APIRouter(prefix="/apps", tags=["workflow"]) @router.post("/{app_id}/workflow") @cur_workspace_access_guard() async def create_workflow_config( - app_id: Annotated[uuid.UUID, Path(description="应用 ID")], - config: WorkflowConfigCreate, - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)] + app_id: Annotated[uuid.UUID, Path(description="应用 ID")], + config: WorkflowConfigCreate, + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)] ): """创建工作流配置 @@ -96,6 +96,7 @@ async def create_workflow_config( msg=f"创建工作流配置失败: {str(e)}" ) + # # @router.get("/{app_id}/workflow") # async def get_workflow_config( @@ -199,10 +200,10 @@ async def create_workflow_config( @router.delete("/{app_id}/workflow") async def delete_workflow_config( - app_id: Annotated[uuid.UUID, Path(description="应用 ID")], - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)] + app_id: Annotated[uuid.UUID, Path(description="应用 ID")], + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)] ): """删除工作流配置 @@ -243,11 +244,11 @@ async def delete_workflow_config( @router.post("/{app_id}/workflow/validate") async def validate_workflow_config( - app_id: Annotated[uuid.UUID, Path(description="应用 ID")], - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)], - for_publish: Annotated[bool, Query(description="是否为发布验证")] = False + app_id: Annotated[uuid.UUID, Path(description="应用 ID")], + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)], + for_publish: Annotated[bool, Query(description="是否为发布验证")] = False ): """验证工作流配置 @@ -312,12 +313,12 @@ async def validate_workflow_config( @router.get("/{app_id}/workflow/executions") async def get_workflow_executions( - app_id: Annotated[uuid.UUID, Path(description="应用 ID")], - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)], - limit: Annotated[int, Query(ge=1, le=100)] = 50, - offset: Annotated[int, Query(ge=0)] = 0 + app_id: Annotated[uuid.UUID, Path(description="应用 ID")], + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)], + limit: Annotated[int, Query(ge=1, le=100)] = 50, + offset: Annotated[int, Query(ge=0)] = 0 ): """获取工作流执行记录列表 @@ -365,10 +366,10 @@ async def get_workflow_executions( @router.get("/workflow/executions/{execution_id}") async def get_workflow_execution( - execution_id: Annotated[str, Path(description="执行 ID")], - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)] + execution_id: Annotated[str, Path(description="执行 ID")], + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)] ): """获取工作流执行详情 @@ -417,16 +418,14 @@ async def get_workflow_execution( ) - # ==================== 工作流执行 ==================== - @router.post("/{app_id}/workflow/run") async def run_workflow( - app_id: Annotated[uuid.UUID, Path(description="应用 ID")], - request: WorkflowExecutionRequest, - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)] + app_id: Annotated[uuid.UUID, Path(description="应用 ID")], + request: WorkflowExecutionRequest, + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)] ): """执行工作流 @@ -487,22 +486,22 @@ async def run_workflow( """ try: async for event in await service.run_workflow( - app_id=app_id, - input_data=input_data, - triggered_by=current_user.id, - conversation_id=uuid.UUID(request.conversation_id) if request.conversation_id else None, - stream=True + app_id=app_id, + input_data=input_data, + triggered_by=current_user.id, + conversation_id=uuid.UUID(request.conversation_id) if request.conversation_id else None, + stream=True ): # 提取事件类型和数据 event_type = event.get("event", "message") event_data = event.get("data", {}) - + # 转换为标准 SSE 格式(字符串) # event: # data: sse_message = f"event: {event_type}\ndata: {json.dumps(event_data)}\n\n" yield sse_message - + except Exception as e: logger.error(f"流式执行异常: {e}", exc_info=True) # 发送错误事件 @@ -554,10 +553,10 @@ async def run_workflow( @router.post("/workflow/executions/{execution_id}/cancel") async def cancel_workflow_execution( - execution_id: Annotated[str, Path(description="执行 ID")], - db: Annotated[Session, Depends(get_db)], - current_user: Annotated[User, Depends(get_current_user)], - service: Annotated[WorkflowService, Depends(get_workflow_service)] + execution_id: Annotated[str, Path(description="执行 ID")], + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(get_current_user)], + service: Annotated[WorkflowService, Depends(get_workflow_service)] ): """取消工作流执行 @@ -602,7 +601,7 @@ async def cancel_workflow_execution( except BusinessException as e: logger.warning(f"取消工作流执行失败: {e.message}") - return fail(code=e.error_code, msg=e.message) + return fail(code=e.code, msg=e.message) except Exception as e: logger.error(f"取消工作流执行异常: {e}", exc_info=True) return fail( diff --git a/api/app/core/config.py b/api/app/core/config.py index 573c4283..ff7bf2e1 100644 --- a/api/app/core/config.py +++ b/api/app/core/config.py @@ -7,17 +7,18 @@ from dotenv import load_dotenv load_dotenv() + class Settings: ENABLE_SINGLE_WORKSPACE: bool = os.getenv("ENABLE_SINGLE_WORKSPACE", "true").lower() == "true" # API Keys Configuration OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") DASHSCOPE_API_KEY: str = os.getenv("DASHSCOPE_API_KEY", "") - + # Neo4j Configuration (记忆系统数据库) NEO4J_URI: str = os.getenv("NEO4J_URI", "bolt://1.94.111.67:7687") NEO4J_USERNAME: str = os.getenv("NEO4J_USERNAME", "neo4j") NEO4J_PASSWORD: str = os.getenv("NEO4J_PASSWORD", "") - + # Database configuration (Postgres) DB_HOST: str = os.getenv("DB_HOST", "127.0.0.1") DB_PORT: int = int(os.getenv("DB_PORT", "5432")) @@ -37,7 +38,7 @@ class Settings: REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379")) REDIS_DB: int = int(os.getenv("REDIS_DB", "1")) REDIS_PASSWORD: str = os.getenv("REDIS_PASSWORD", "") - + # ElasticSearch configuration ELASTICSEARCH_HOST: str = os.getenv("ELASTICSEARCH_HOST", "https://127.0.0.1") ELASTICSEARCH_PORT: int = int(os.getenv("ELASTICSEARCH_PORT", "9200")) @@ -48,7 +49,7 @@ class Settings: ELASTICSEARCH_REQUEST_TIMEOUT: int = int(os.getenv("ELASTICSEARCH_REQUEST_TIMEOUT", "100000")) ELASTICSEARCH_RETRY_ON_TIMEOUT: bool = os.getenv("ELASTICSEARCH_RETRY_ON_TIMEOUT", "True").lower() == "true" ELASTICSEARCH_MAX_RETRIES: int = int(os.getenv("ELASTICSEARCH_MAX_RETRIES", "10")) - + # Xinference configuration XINFERENCE_URL: str = os.getenv("XINFERENCE_URL", "http://127.0.0.1") @@ -57,17 +58,17 @@ class Settings: LANGCHAIN_TRACING: bool = os.getenv("LANGCHAIN_TRACING", "false").lower() == "true" LANGCHAIN_API_KEY: str = os.getenv("LANGCHAIN_API_KEY", "") LANGCHAIN_ENDPOINT: str = os.getenv("LANGCHAIN_ENDPOINT", "") - + # LLM Request Configuration LLM_TIMEOUT: float = float(os.getenv("LLM_TIMEOUT", "120.0")) LLM_MAX_RETRIES: int = int(os.getenv("LLM_MAX_RETRIES", "2")) - + # JWT Token Configuration SECRET_KEY: str = os.getenv("SECRET_KEY", "a_default_secret_key_that_is_long_and_random") ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30")) REFRESH_TOKEN_EXPIRE_DAYS: int = int(os.getenv("REFRESH_TOKEN_EXPIRE_DAYS", "7")) - + # Single Sign-On configuration ENABLE_SINGLE_SESSION: bool = os.getenv("ENABLE_SINGLE_SESSION", "false").lower() == "true" @@ -86,19 +87,19 @@ class Settings: LANGFUSE_PUBLIC_KEY: str = os.getenv("LANGFUSE_PUBLIC_KEY", "") LANGFUSE_SECRET_KEY: str = os.getenv("LANGFUSE_SECRET_KEY", "") LANGFUSE_HOST: str = os.getenv("LANGFUSE_HOST", "") - + # Server Configuration SERVER_IP: str = os.getenv("SERVER_IP", "127.0.0.1") # ======================================================================== # Internal Configuration (not in .env, used by application code) # ======================================================================== - + # Superuser settings (internal defaults) FIRST_SUPERUSER_EMAIL: str = os.getenv("FIRST_SUPERUSER_EMAIL", "admin@example.com") FIRST_SUPERUSER_USERNAME: str = os.getenv("FIRST_SUPERUSER_USERNAME", "admin") FIRST_SUPERUSER_PASSWORD: str = os.getenv("FIRST_SUPERUSER_PASSWORD", "admin_password") - + # Generic File Upload (internal) GENERIC_FILE_PATH: str = os.getenv("GENERIC_FILE_PATH", "/uploads") ENABLE_FILE_COMPRESSION: bool = os.getenv("ENABLE_FILE_COMPRESSION", "false").lower() == "true" @@ -123,7 +124,7 @@ class Settings: LOG_BACKUP_COUNT: int = int(os.getenv("LOG_BACKUP_COUNT", "5")) LOG_TO_CONSOLE: bool = os.getenv("LOG_TO_CONSOLE", "true").lower() == "true" LOG_TO_FILE: bool = os.getenv("LOG_TO_FILE", "true").lower() == "true" - + # Sensitive Data Filtering ENABLE_SENSITIVE_DATA_FILTER: bool = os.getenv("ENABLE_SENSITIVE_DATA_FILTER", "true").lower() == "true" @@ -142,7 +143,6 @@ class Settings: LOG_STREAM_BUFFER_SIZE: int = int(os.getenv("LOG_STREAM_BUFFER_SIZE", "8192")) # 8KB LOG_FILE_MAX_SIZE_MB: int = int(os.getenv("LOG_FILE_MAX_SIZE_MB", "10")) # 10MB - # Celery configuration (internal) CELERY_BROKER: int = int(os.getenv("CELERY_BROKER", "1")) CELERY_BACKEND: int = int(os.getenv("CELERY_BACKEND", "2")) @@ -150,15 +150,15 @@ class Settings: HEALTH_CHECK_SECONDS: float = float(os.getenv("HEALTH_CHECK_SECONDS", "600")) MEMORY_INCREMENT_INTERVAL_HOURS: float = float(os.getenv("MEMORY_INCREMENT_INTERVAL_HOURS", "24")) DEFAULT_WORKSPACE_ID: Optional[str] = os.getenv("DEFAULT_WORKSPACE_ID", None) - REFLECTION_INTERVAL_TIME:Optional[str] = int(os.getenv("REFLECTION_INTERVAL_TIME", 30)) - + REFLECTION_INTERVAL_TIME: Optional[str] = int(os.getenv("REFLECTION_INTERVAL_TIME", 30)) + # Memory Cache Regeneration Configuration MEMORY_CACHE_REGENERATION_HOURS: int = int(os.getenv("MEMORY_CACHE_REGENERATION_HOURS", "24")) # Memory Module Configuration (internal) MEMORY_OUTPUT_DIR: str = os.getenv("MEMORY_OUTPUT_DIR", "logs/memory-output") MEMORY_CONFIG_DIR: str = os.getenv("MEMORY_CONFIG_DIR", "app/core/memory") - + # Tool Management Configuration TOOL_CONFIG_DIR: str = os.getenv("TOOL_CONFIG_DIR", "app/core/tools") TOOL_EXECUTION_TIMEOUT: int = int(os.getenv("TOOL_EXECUTION_TIMEOUT", "60")) @@ -167,7 +167,10 @@ class Settings: # official environment system version SYSTEM_VERSION: str = os.getenv("SYSTEM_VERSION", "v0.2.0") - + + # workflow config + WORKFLOW_NODE_TIMEOUT: int = os.getenv("WORKFLOW_NODE_TIMEOUT", 600) + def get_memory_output_path(self, filename: str = "") -> str: """ Get the full path for memory module output files. @@ -182,7 +185,7 @@ class Settings: if filename: return str(base_path / filename) return str(base_path) - + def ensure_memory_output_dir(self) -> None: """ Ensure the memory output directory exists. diff --git a/api/app/core/workflow/executor.py b/api/app/core/workflow/executor.py index 67689935..c048f447 100644 --- a/api/app/core/workflow/executor.py +++ b/api/app/core/workflow/executor.py @@ -74,6 +74,7 @@ class WorkflowExecutor: 初始化的工作流状态 """ user_message = input_data.get("message") or "" + conversation_messages = input_data.get("conv_messages") or [] # 会话变量处理:从配置文件获取变量定义列表,转换为字典(name -> default value) config_variables_list = self.workflow_config.get("variables") or [] @@ -114,7 +115,7 @@ class WorkflowExecutor: } return { - "messages": [('user', user_message)], + "messages": conversation_messages, "variables": variables, "node_outputs": {}, "runtime_vars": {}, # 运行时节点变量(简化版,供快速访问) diff --git a/api/app/core/workflow/nodes/base_node.py b/api/app/core/workflow/nodes/base_node.py index e3bf36c9..72fd0bb5 100644 --- a/api/app/core/workflow/nodes/base_node.py +++ b/api/app/core/workflow/nodes/base_node.py @@ -7,13 +7,13 @@ import asyncio import logging from abc import ABC, abstractmethod -from operator import add from typing import Any -from langchain_core.messages import AnyMessage, AIMessage +from langchain_core.messages import AIMessage from langgraph.config import get_stream_writer from typing_extensions import TypedDict, Annotated +from app.core.config import settings from app.core.workflow.variable_pool import VariablePool logger = logging.getLogger(__name__) @@ -25,7 +25,7 @@ class WorkflowState(TypedDict): The state object passed between nodes in a workflow, containing messages, variables, node outputs, etc. """ # List of messages (append mode) - messages: Annotated[list[tuple[str, str]], add] + messages: list[dict[str, str]] # Set of loop node IDs, used for assigning values in loop nodes cycle_nodes: list @@ -154,7 +154,7 @@ class BaseNode(ABC): Returns: 超时时间 """ - return 60 + return settings.WORKFLOW_NODE_TIMEOUT # return self.error_handling.get("timeout", 60) async def run(self, state: WorkflowState) -> dict[str, Any]: @@ -203,6 +203,7 @@ class BaseNode(ABC): # 返回包装后的输出和运行时变量 return { **wrapped_output, + "messages": state["messages"], "variables": state["variables"], "runtime_vars": { self.node_id: runtime_var @@ -356,6 +357,7 @@ class BaseNode(ABC): # Build complete state update (including node_outputs, runtime_vars, and final streaming buffer) state_update = { **final_output, + "messages": state["messages"], "variables": state["variables"], "runtime_vars": { self.node_id: runtime_var diff --git a/api/app/core/workflow/nodes/end/node.py b/api/app/core/workflow/nodes/end/node.py index 6195afbd..0cbd9e8e 100644 --- a/api/app/core/workflow/nodes/end/node.py +++ b/api/app/core/workflow/nodes/end/node.py @@ -6,7 +6,6 @@ End 节点实现 import logging import re -import asyncio from app.core.workflow.nodes.base_node import BaseNode, WorkflowState from app.core.workflow.nodes.enums import NodeType @@ -38,7 +37,23 @@ class EndNode(BaseNode): # 如果配置了输出模板,使用模板渲染;否则使用默认输出 if output_template: output = self._render_template(output_template, state, strict=False) + state['messages'].extend([ + { + "role": "user", + "content": self.get_variable("sys.message", state) + }, + { + "role": "assistant", + "content": output + } + ]) else: + state['messages'].extend([ + { + "role": "user", + "content": self.get_variable("sys.message", state) + }, + ]) output = "工作流已完成" # 统计信息(用于日志) @@ -166,6 +181,12 @@ class EndNode(BaseNode): "chunk_index": 1, "is_suffix": False }) + state['messages'].extend([ + { + "role": "user", + "content": self.get_variable("sys.message", state) + } + ]) yield {"__final__": True, "result": output} return @@ -176,7 +197,6 @@ class EndNode(BaseNode): source_node_id = edge.get("source") # Check if the source node is an LLM node for node in self.workflow_config.get("nodes", []): - print("="*50) logger.info(f"节点 {self.node_id} 的类型 {node.get("type")}") if node.get("id") == source_node_id and node.get("type") == NodeType.LLM: direct_upstream_llm_nodes.append(source_node_id) @@ -216,12 +236,24 @@ class EndNode(BaseNode): }) logger.info(f"节点 {self.node_id} 已通过 writer 发送完整内容") + state['messages'].extend([ + { + "role": "user", + "content": self.get_variable("sys.message", state) + }, + { + "role": "assistant", + "content": output + } + ]) + # yield completion marker yield {"__final__": True, "result": output} return # Has reference to direct upstream LLM node, only output the part after that reference (suffix) - logger.info(f"节点 {self.node_id} 检测到直接上游 LLM 节点引用,只输出后缀部分(从索引 {upstream_llm_ref_index + 1} 开始)") + logger.info( + f"节点 {self.node_id} 检测到直接上游 LLM 节点引用,只输出后缀部分(从索引 {upstream_llm_ref_index + 1} 开始)") # Collect suffix parts suffix_parts = [] @@ -258,6 +290,17 @@ class EndNode(BaseNode): # 构建完整输出(用于返回,包含前缀 + 动态内容 + 后缀) full_output = self._render_template(output_template, state, strict=False) + state['messages'].extend([ + { + "role": "user", + "content": self.get_variable("sys.message", state) + }, + { + "role": "assistant", + "content": full_output + } + ]) + logger.info(f"[后缀调试] 节点 {self.node_id} 后缀部分数量: {len(suffix_parts)}") logger.info(f"[后缀调试] 后缀内容: '{suffix}'") logger.info(f"[后缀调试] 后缀长度: {len(suffix)}") @@ -280,7 +323,8 @@ class EndNode(BaseNode): }) logger.info(f"节点 {self.node_id} 已通过 writer 发送后缀,full_content 长度: {len(full_output)}") else: - logger.warning(f"[后缀调试] 节点 {self.node_id} 后缀为空,不发送!upstream_llm_ref_index={upstream_llm_ref_index}, parts数量={len(parts)}") + logger.warning(f"[后缀调试] 节点 {self.node_id} 后缀为空,不发送!" + f"upstream_llm_ref_index={upstream_llm_ref_index}, parts数量={len(parts)}") # 统计信息 node_outputs = state.get("node_outputs", {}) diff --git a/api/app/core/workflow/nodes/llm/config.py b/api/app/core/workflow/nodes/llm/config.py index 8498fc38..f65d5879 100644 --- a/api/app/core/workflow/nodes/llm/config.py +++ b/api/app/core/workflow/nodes/llm/config.py @@ -11,12 +11,12 @@ class MessageConfig(BaseModel): """消息配置""" role: str = Field( - ..., + default='user', description="消息角色:system, user, assistant" ) content: str = Field( - ..., + default="", description="消息内容,支持模板变量,如:{{ sys.message }}" ) @@ -30,6 +30,23 @@ class MessageConfig(BaseModel): return v.lower() +class MemoryWindowSetting(BaseModel): + enable: bool = Field( + default=False, + description="启用记忆" + ) + + enable_window: bool = Field( + default=False, + description="启用记忆窗口" + ) + + window_size: int = Field( + default=20, + description="记忆窗口大小" + ) + + class LLMNodeConfig(BaseNodeConfig): """LLM 节点配置 @@ -48,6 +65,11 @@ class LLMNodeConfig(BaseNodeConfig): description="上下文" ) + memory: MemoryWindowSetting = Field( + ..., + description="对话上下文窗口" + ) + # 简单模式 prompt: str | None = Field( default=None, diff --git a/api/app/core/workflow/nodes/llm/node.py b/api/app/core/workflow/nodes/llm/node.py index bfa1b99f..b9ba3d7b 100644 --- a/api/app/core/workflow/nodes/llm/node.py +++ b/api/app/core/workflow/nodes/llm/node.py @@ -85,28 +85,31 @@ class LLMNode(BaseNode): """ # 1. 处理消息格式(优先使用 messages) - messages_config = self.config.get("messages") + messages_config = self.typed_config.messages if messages_config: # 使用 LangChain 消息格式 messages = [] for msg_config in messages_config: - role = msg_config.get("role", "user").lower() - content_template = msg_config.get("content", "") + role = msg_config.role.lower() + content_template = msg_config.content content_template = self._render_context(content_template, state) content = self._render_template(content_template, state) # 根据角色创建对应的消息对象 if role == "system": - messages.append(SystemMessage(content=content)) + messages.append({"role": "system", "content": content}) elif role in ["user", "human"]: - messages.append(HumanMessage(content=content)) + messages.append({"role": "user", "content": content}) elif role in ["ai", "assistant"]: - messages.append(AIMessage(content=content)) + messages.append({"role": "user", "content": content}) else: logger.warning(f"未知的消息角色: {role},默认使用 user") - messages.append(HumanMessage(content=content)) + messages.append({"role": "user", "content": content}) + if self.typed_config.memory.enable: + # if self.typed_config.memory.enable_window: + messages = messages[:-1] + state["messages"][-self.typed_config.memory.window_size:] + messages[-1:] prompt_or_messages = messages else: # 使用简单的 prompt 格式(向后兼容) @@ -189,7 +192,7 @@ class LLMNode(BaseNode): return { "prompt": prompt_or_messages if isinstance(prompt_or_messages, str) else None, "messages": [ - {"role": msg.__class__.__name__.replace("Message", "").lower(), "content": msg.content} + {"role": msg.get("role"), "content": msg.get("content", "")} for msg in prompt_or_messages ] if isinstance(prompt_or_messages, list) else None, "config": { diff --git a/api/app/schemas/app_schema.py b/api/app/schemas/app_schema.py index 3c00e5a0..35d2e424 100644 --- a/api/app/schemas/app_schema.py +++ b/api/app/schemas/app_schema.py @@ -41,6 +41,7 @@ class ToolConfig(BaseModel): tool_id: Optional[str] = Field(default=None, description="工具ID") operation: Optional[str] = Field(default=None, description="工具特定配置") + class ToolOldConfig(BaseModel): """工具配置""" enabled: bool = Field(default=False, description="是否启用该工具") @@ -348,6 +349,7 @@ class AppChatRequest(BaseModel): variables: Optional[Dict[str, Any]] = Field(default=None, description="自定义变量参数值") stream: bool = Field(default=False, description="是否流式返回") + class DraftRunRequest(BaseModel): """试运行请求""" message: str = Field(..., description="用户消息") diff --git a/api/app/services/app_chat_service.py b/api/app/services/app_chat_service.py index 56400c92..0065c64b 100644 --- a/api/app/services/app_chat_service.py +++ b/api/app/services/app_chat_service.py @@ -14,6 +14,7 @@ from app.core.exceptions import BusinessException from app.core.logging_config import get_business_logger from app.db import get_db, get_db_context from app.models import MultiAgentConfig, AgentConfig, WorkflowConfig +from app.schemas import DraftRunRequest from app.services.tool_service import ToolService from app.repositories.tool_repository import ToolRepository from app.db import get_db @@ -59,7 +60,7 @@ class AppChatService: # 获取模型配置ID model_config_id = config.default_model_config_id - api_key_obj = ModelApiKeyService.get_a_api_key(self.db ,model_config_id) + api_key_obj = ModelApiKeyService.get_a_api_key(self.db, model_config_id) # 处理系统提示词(支持变量替换) system_prompt = config.system_prompt if variables: @@ -210,7 +211,7 @@ class AppChatService: # 获取模型配置ID model_config_id = config.default_model_config_id - api_key_obj = ModelApiKeyService.get_a_api_key(self.db ,model_config_id) + api_key_obj = ModelApiKeyService.get_a_api_key(self.db, model_config_id) # 处理系统提示词(支持变量替换) system_prompt = config.system_prompt if variables: @@ -511,7 +512,6 @@ class AppChatService: } ) - except (GeneratorExit, asyncio.CancelledError): # 生成器被关闭或任务被取消,正常退出 logger.debug("多 Agent 流式聊天被中断") @@ -537,83 +537,19 @@ class AppChatService: ) -> Dict[str, Any]: """聊天(非流式)""" workflow_service = WorkflowService(self.db) - - input_data = {"message":message, "variables": variables, - "conversation_id": str(conversation_id)} - inconfig = workflow_service.get_workflow_config(app_id) - - # 2. 创建执行记录 - execution = workflow_service.create_execution( - workflow_config_id=inconfig.id, - app_id=app_id, - trigger_type="manual", - triggered_by=None, - conversation_id=conversation_id, - input_data=input_data + payload = DraftRunRequest( + message=message, + variables=variables, + conversation_id=str(conversation_id), + stream=True, + user_id=user_id + ) + return await workflow_service.run( + app_id=app_id, + payload=payload, + config=config, + workspace_id=workspace_id, ) - - # 3. 构建工作流配置字典 - workflow_config_dict = { - "nodes": config.nodes, - "edges": config.edges, - "variables": config.variables, - "execution_config": config.execution_config - } - - # 4. 获取工作空间 ID(从 app 获取) - - # 5. 执行工作流 - from app.core.workflow.executor import execute_workflow - - try: - # 更新状态为运行中 - workflow_service.update_execution_status(execution.execution_id, "running") - - result = await execute_workflow( - workflow_config=workflow_config_dict, - input_data=input_data, - execution_id=execution.execution_id, - workspace_id=str(workspace_id), - user_id=user_id - ) - - # 更新执行结果 - if result.get("status") == "completed": - workflow_service.update_execution_status( - execution.execution_id, - "completed", - output_data=result.get("node_outputs", {}) - ) - else: - workflow_service.update_execution_status( - execution.execution_id, - "failed", - error_message=result.get("error") - ) - - # 返回增强的响应结构 - return { - "execution_id": execution.execution_id, - "status": result.get("status"), - "output": result.get("output"), # 最终输出(字符串) - "output_data": result.get("node_outputs", {}), # 所有节点输出(详细数据) - "conversation_id": result.get("conversation_id"), # 所有节点输出(详细数据)payload., # 会话 ID - "error_message": result.get("error"), - "elapsed_time": result.get("elapsed_time"), - "token_usage": result.get("token_usage") - } - - except Exception as e: - logger.error(f"工作流执行失败: execution_id={execution.execution_id}, error={e}", exc_info=True) - workflow_service.update_execution_status( - execution.execution_id, - "failed", - error_message=str(e) - ) - raise BusinessException( - code=BizCode.INTERNAL_ERROR, - message=f"工作流执行失败: {str(e)}" - ) async def workflow_chat_stream( self, @@ -632,62 +568,21 @@ class AppChatService: ) -> AsyncGenerator[str, None]: """聊天(流式)""" workflow_service = WorkflowService(self.db) - input_data = {"message": message, "variables": variables, - "conversation_id": str(conversation_id)} - inconfig = workflow_service.get_workflow_config(app_id) - # 2. 创建执行记录 - execution = workflow_service.create_execution( - workflow_config_id=inconfig.id, - app_id=app_id, - trigger_type="manual", - triggered_by=None, - conversation_id=conversation_id, - input_data=input_data + payload = DraftRunRequest( + message=message, + variables=variables, + conversation_id=str(conversation_id), + stream=True, + user_id=user_id ) + async for event in workflow_service.run_stream( + app_id=app_id, + payload=payload, + config=config, + workspace_id=workspace_id, + ): + yield event - # 3. 构建工作流配置字典 - workflow_config_dict = { - "nodes": config.nodes, - "edges": config.edges, - "variables": config.variables, - "execution_config": config.execution_config - } - - # 4. 获取工作空间 ID(从 app 获取) - - # 5. 流式执行工作流 - - try: - # 更新状态为运行中 - workflow_service.update_execution_status(execution.execution_id, "running") - - - # 调用流式执行(executor 会发送 workflow_start 和 workflow_end 事件) - async for event in workflow_service._run_workflow_stream( - workflow_config=workflow_config_dict, - input_data=input_data, - execution_id=execution.execution_id, - workspace_id=str(workspace_id), - user_id=user_id - ): - # 直接转发 executor 的事件(已经是正确的格式) - yield event - - except Exception as e: - logger.error(f"工作流流式执行失败: execution_id={execution.execution_id}, error={e}", exc_info=True) - workflow_service.update_execution_status( - execution.execution_id, - "failed", - error_message=str(e) - ) - # 发送错误事件 - yield { - "event": "error", - "data": { - "execution_id": execution.execution_id, - "error": str(e) - } - } # ==================== 依赖注入函数 ==================== diff --git a/api/app/services/workflow_service.py b/api/app/services/workflow_service.py index 7d3c784f..f9988352 100644 --- a/api/app/services/workflow_service.py +++ b/api/app/services/workflow_service.py @@ -2,12 +2,11 @@ 工作流服务层 """ import datetime -import json import logging import uuid -import datetime from typing import Any, Annotated, AsyncGenerator +from deprecated import deprecated from fastapi import Depends from sqlalchemy.orm import Session @@ -16,15 +15,16 @@ from app.core.exceptions import BusinessException from app.core.workflow.validator import validate_workflow_config from app.db import get_db, get_db_context from app.models.workflow_model import WorkflowConfig, WorkflowExecution +from app.repositories.conversation_repository import MessageRepository +from app.models.conversation_model import Message from app.repositories.end_user_repository import EndUserRepository -from app.services.multi_agent_service import convert_uuids_to_str from app.repositories.workflow_repository import ( WorkflowConfigRepository, WorkflowExecutionRepository, WorkflowNodeExecutionRepository ) from app.schemas import DraftRunRequest -from app.utils.sse_utils import format_sse_message +from app.services.multi_agent_service import convert_uuids_to_str logger = logging.getLogger(__name__) @@ -37,6 +37,7 @@ class WorkflowService: self.config_repo = WorkflowConfigRepository(db) self.execution_repo = WorkflowExecutionRepository(db) self.node_execution_repo = WorkflowNodeExecutionRepository(db) + self.message_repo = MessageRepository(db) # ==================== 配置管理 ==================== @@ -418,14 +419,13 @@ class WorkflowService: """运行工作流 Args: + workspace_id: + config: + payload: app_id: 应用 ID - input_data: 输入数据(包含 message 和 variables) - triggered_by: 触发用户 ID - conversation_id: 会话 ID(可选) - stream: 是否流式返回 Returns: - 执行结果(非流式)或生成器(流式) + 执行结果(非流式) Raises: BusinessException: 配置不存在或执行失败时抛出 @@ -438,7 +438,8 @@ class WorkflowService: code=BizCode.CONFIG_MISSING, message=f"工作流配置不存在: app_id={app_id}" ) - input_data = {"message": payload.message, "variables": payload.variables, "conversation_id": payload.conversation_id} + input_data = {"message": payload.message, "variables": payload.variables, + "conversation_id": payload.conversation_id} # 转换 user_id 为 UUID triggered_by_uuid = None @@ -461,7 +462,7 @@ class WorkflowService: workflow_config_id=config.id, app_id=app_id, trigger_type="manual", - triggered_by=triggered_by_uuid, + triggered_by=None, conversation_id=conversation_id_uuid, input_data=input_data ) @@ -500,8 +501,11 @@ class WorkflowService: variables = last_state.get("variables", {}) conv_vars = variables.get("conv", {}) input_data["conv"] = conv_vars + input_data["conv_messages"] = last_state.get("messages") or [] break + init_message_length = len(input_data.get("conv_messages", [])) + result = await execute_workflow( workflow_config=workflow_config_dict, input_data=input_data, @@ -517,6 +521,17 @@ class WorkflowService: "completed", output_data=result ) + final_messages = result.get("messages", [])[init_message_length:] + for message in final_messages: + message_obj = Message( + conversation_id=conversation_id_uuid, + role=message["role"], + content=message["content"], + ) + self.message_repo.add_message(message_obj) + self.db.commit() + logger.info(f"Workflow Run Success, " + f"execution_id: {execution.execution_id}, message count: {len(final_messages)}") else: self.update_execution_status( execution.execution_id, @@ -529,6 +544,7 @@ class WorkflowService: "execution_id": execution.execution_id, "status": result.get("status"), "variables": result.get("variables"), + "messages": result.get("messages"), "output": result.get("output"), # 最终输出(字符串) "output_data": result.get("node_outputs", {}), # 所有节点输出(详细数据) "conversation_id": result.get("conversation_id"), # 所有节点输出(详细数据)payload., # 会话 ID @@ -559,6 +575,7 @@ class WorkflowService: """运行工作流(流式) Args: + workspace_id: app_id: 应用 ID payload: 请求对象(包含 message, variables, conversation_id 等) config: 存储类型(可选) @@ -601,7 +618,7 @@ class WorkflowService: workflow_config_id=config.id, app_id=app_id, trigger_type="manual", - triggered_by=triggered_by_uuid, + triggered_by=None, conversation_id=conversation_id_uuid, input_data=input_data ) @@ -638,17 +655,46 @@ class WorkflowService: variables = last_state.get("variables", {}) conv_vars = variables.get("conv", {}) input_data["conv"] = conv_vars + input_data["conv_messages"] = last_state.get("messages") or [] break + init_message_length = len(input_data.get("conv_messages", [])) + from app.core.workflow.executor import execute_workflow_stream - # 调用流式执行(executor 会发送 workflow_start 和 workflow_end 事件) - async for event in self._run_workflow_stream( + async for event in execute_workflow_stream( workflow_config=workflow_config_dict, input_data=input_data, execution_id=execution.execution_id, workspace_id=str(workspace_id), user_id=end_user_id ): - # 直接转发 executor 的事件(已经是正确的格式) + if event.get("event") == "workflow_end": + + status = event.get("data", {}).get("status") + if status == "completed": + self.update_execution_status( + execution.execution_id, + "completed", + output_data=event.get("data") + ) + final_messages = event.get("data", {}).get("messages", [])[init_message_length:] + for message in final_messages: + message_obj = Message( + conversation_id=conversation_id_uuid, + role=message["role"], + content=message["content"], + ) + self.message_repo.add_message(message_obj) + self.db.commit() + logger.info(f"Workflow Run Success, " + f"execution_id: {execution.execution_id}, message count: {len(final_messages)}") + elif status == "failed": + self.update_execution_status( + execution.execution_id, + "failed", + output_data=event.get("data") + ) + else: + logger.error(f"unexpect workflow run status, status: {status}") yield event except Exception as e: @@ -667,6 +713,8 @@ class WorkflowService: } } + @deprecated(reason="This method is deprecated. " + "Please use WorkflowService.run / run_stream instead.") async def run_workflow( self, app_id: uuid.UUID, @@ -819,6 +867,7 @@ class WorkflowService: return clean_value(event) + @deprecated(reason="This method is deprecated. Please use WorkflowService.run_stream instead.") async def _run_workflow_stream( self, workflow_config: dict[str, Any], diff --git a/api/pyproject.toml b/api/pyproject.toml index 2dcc706d..6da684de 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -136,7 +136,8 @@ dependencies = [ "markdown-to-json==2.1.1", "valkey==6.0.2", "python-calamine>=0.4.0", - "xlrd==2.0.2" + "xlrd==2.0.2", + "deprecated>=1.3.1", ] [tool.pytest.ini_options] From 830e9dd6f9821179a20d315da4090ae50fe074b6 Mon Sep 17 00:00:00 2001 From: yujiangping Date: Wed, 14 Jan 2026 16:42:32 +0800 Subject: [PATCH 07/32] style(chat): improve chat layout spacing and empty state styling - Add top margin (rb:mt-2) to bottom label in ChatContent for better spacing - Wrap Chat component in centered container with max-width (760px) and top padding - Replace AnalysisEmptyIcon with BgImg in empty state for improved visual consistency - Add size prop [320,180] to Empty component for proper empty state dimensions - Adjust contentClassName spacing for better layout alignment - Improves overall chat interface visual hierarchy and spacing consistency --- web/src/components/Chat/ChatContent.tsx | 2 +- web/src/views/Conversation/index.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web/src/components/Chat/ChatContent.tsx b/web/src/components/Chat/ChatContent.tsx index 11ccb5c3..ecc2e4e9 100644 --- a/web/src/components/Chat/ChatContent.tsx +++ b/web/src/components/Chat/ChatContent.tsx @@ -68,7 +68,7 @@ const ChatContent: FC = ({ {/* 底部标签(如时间戳、用户名等) */} {labelPosition === 'bottom' && -
+
{labelFormat(item)}
} diff --git a/web/src/views/Conversation/index.tsx b/web/src/views/Conversation/index.tsx index d791bf2d..7fa1ada1 100644 --- a/web/src/views/Conversation/index.tsx +++ b/web/src/views/Conversation/index.tsx @@ -254,9 +254,10 @@ const Conversation: FC = () => {
+
} - contentClassName="rb:h-[calc(100%-152px)]" + empty={} + contentClassName="rb:h-[calc(100%-152px)] " data={chatList} streamLoading={streamLoading} loading={loading} @@ -285,6 +286,7 @@ const Conversation: FC = () => { +
) From b71232539933efc3d4c8980901398c892c2dfd6f Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Wed, 14 Jan 2026 16:46:09 +0800 Subject: [PATCH 08/32] fix(workflow): fix env timeout configuration and LLM node message role mismatch --- api/app/core/config.py | 2 +- api/app/core/workflow/nodes/llm/node.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/app/core/config.py b/api/app/core/config.py index ff7bf2e1..5f4f91c4 100644 --- a/api/app/core/config.py +++ b/api/app/core/config.py @@ -169,7 +169,7 @@ class Settings: SYSTEM_VERSION: str = os.getenv("SYSTEM_VERSION", "v0.2.0") # workflow config - WORKFLOW_NODE_TIMEOUT: int = os.getenv("WORKFLOW_NODE_TIMEOUT", 600) + WORKFLOW_NODE_TIMEOUT: int = int(os.getenv("WORKFLOW_NODE_TIMEOUT", 600)) def get_memory_output_path(self, filename: str = "") -> str: """ diff --git a/api/app/core/workflow/nodes/llm/node.py b/api/app/core/workflow/nodes/llm/node.py index b9ba3d7b..e25bd35d 100644 --- a/api/app/core/workflow/nodes/llm/node.py +++ b/api/app/core/workflow/nodes/llm/node.py @@ -102,7 +102,7 @@ class LLMNode(BaseNode): elif role in ["user", "human"]: messages.append({"role": "user", "content": content}) elif role in ["ai", "assistant"]: - messages.append({"role": "user", "content": content}) + messages.append({"role": "assistant", "content": content}) else: logger.warning(f"未知的消息角色: {role},默认使用 user") messages.append({"role": "user", "content": content}) From 5904ac80dbf056a1c85327af74b5405cb19eb53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BF=8A=E7=94=B7?= Date: Wed, 14 Jan 2026 17:01:09 +0800 Subject: [PATCH 09/32] fix(mcp tool): 1. add identification for the SSE protocol tools; 2. When using the agent call tool to handle parameters, there was an error caused by the enumeration --- api/app/core/tools/langchain_adapter.py | 4 +- api/app/core/tools/mcp/client.py | 357 ++++++++++++++---------- 2 files changed, 217 insertions(+), 144 deletions(-) diff --git a/api/app/core/tools/langchain_adapter.py b/api/app/core/tools/langchain_adapter.py index ea5fdb96..51415732 100644 --- a/api/app/core/tools/langchain_adapter.py +++ b/api/app/core/tools/langchain_adapter.py @@ -232,7 +232,7 @@ class LangchainAdapter: # 添加验证约束 if param.enum: # 枚举值约束 - field_kwargs["regex"] = f"^({'|'.join(map(str, param.enum))})$" + field_kwargs["pattern"] = f"^({'|'.join(map(str, param.enum))})$" if param.minimum is not None: field_kwargs["ge"] = param.minimum @@ -241,7 +241,7 @@ class LangchainAdapter: field_kwargs["le"] = param.maximum if param.pattern: - field_kwargs["regex"] = param.pattern + field_kwargs["pattern"] = param.pattern fields[param.name] = Field(**field_kwargs) annotations[param.name] = python_type diff --git a/api/app/core/tools/mcp/client.py b/api/app/core/tools/mcp/client.py index 2901b7ca..e513a147 100644 --- a/api/app/core/tools/mcp/client.py +++ b/api/app/core/tools/mcp/client.py @@ -27,20 +27,22 @@ class SimpleMCPClient: # 确定连接类型 self.is_websocket = server_url.startswith(("ws://", "wss://")) + self.is_sse = "/sse" in server_url.lower() # 连接状态 self._websocket = None self._session = None self._request_id = 0 self._pending_requests = {} + self._server_capabilities = {} + self._endpoint_url = None # SSE endpoint URL + self._sse_task = None async def __aenter__(self): - """异步上下文管理器入口""" await self.connect() return self async def __aexit__(self, exc_type, exc_val, exc_tb): - """异步上下文管理器出口""" await self.disconnect() async def connect(self): @@ -57,47 +59,157 @@ class SimpleMCPClient: async def disconnect(self): """断开连接""" try: + if self._sse_task: + self._sse_task.cancel() if self._websocket: await self._websocket.close() self._websocket = None - if self._session: await self._session.close() self._session = None - except Exception as e: logger.error(f"断开连接失败: {e}") async def _connect_websocket(self): """WebSocket 连接""" headers = self._build_headers() - self._websocket = await websockets.connect( self.server_url, extra_headers=headers, timeout=self.timeout ) - - # 启动消息处理 asyncio.create_task(self._handle_websocket_messages()) - - # 发送初始化消息 await self._send_initialize() async def _connect_http(self): """HTTP 连接""" headers = self._build_headers() timeout = aiohttp.ClientTimeout(total=self.timeout) + self._session = aiohttp.ClientSession(headers=headers, timeout=timeout) - self._session = aiohttp.ClientSession( - headers=headers, - timeout=timeout - ) - - # 对于 ModelScope MCP 服务,需要先发送初始化请求 - if "modelscope.net" in self.server_url: + if self.is_sse: + await self._initialize_sse_session() + elif "modelscope.net" in self.server_url: await self._initialize_modelscope_session() + async def _initialize_sse_session(self): + """初始化 SSE MCP 会话 - 参考 Dify 实现""" + try: + # 建立 SSE 连接 + response = await self._session.get( + self.server_url, + headers={"Accept": "text/event-stream"} + ) + + if response.status != 200: + error_text = await response.text() + raise MCPConnectionError(f"SSE 连接失败 {response.status}: {error_text}") + + # 启动 SSE 读取任务 + self._sse_task = asyncio.create_task(self._read_sse_stream(response)) + + # 等待获取 endpoint URL + for _ in range(10): + if self._endpoint_url: + break + await asyncio.sleep(1) + + if not self._endpoint_url: + raise MCPConnectionError("未能获取 endpoint URL") + + # 发送 initialize 请求到 endpoint + init_request = { + "jsonrpc": "2.0", + "id": self._get_request_id(), + "method": "initialize", + "params": { + "protocolVersion": "2024-11-05", + "capabilities": {"tools": {}}, + "clientInfo": {"name": "MemoryBear", "version": "1.0.0"} + } + } + + init_response = await self._send_sse_request(init_request) + if "error" in init_response: + raise MCPConnectionError(f"初始化失败: {init_response['error']}") + + result = init_response.get("result", {}) + self._server_capabilities = result.get("capabilities", {}) + + # 发送 initialized 通知 + await self._send_sse_notification({"jsonrpc": "2.0", "method": "notifications/initialized"}) + + except aiohttp.ClientError as e: + raise MCPConnectionError(f"初始化连接失败: {e}") + + async def _read_sse_stream(self, response): + """读取 SSE 流""" + try: + async for line in response.content: + line = line.decode('utf-8').strip() + + if line.startswith('event:'): + continue + + if line.startswith('data:'): + data = line[5:].strip() # 去除 'data:' 后的空格 + if not data or data == '[DONE]': + continue + + try: + # 处理 endpoint 事件(相对路径或绝对路径) + if not self._endpoint_url: + # 如果是相对路径,拼接成完整 URL + if data.startswith('/'): + from urllib.parse import urlparse, urlunparse + parsed = urlparse(self.server_url) + self._endpoint_url = f"{parsed.scheme}://{parsed.netloc}{data}" + else: + self._endpoint_url = data + logger.info(f"获取到 endpoint URL: {self._endpoint_url}") + continue + + # 处理 message 事件 + message = json.loads(data) + request_id = message.get("id") + if request_id and request_id in self._pending_requests: + future = self._pending_requests.pop(request_id) + if not future.done(): + future.set_result(message) + except json.JSONDecodeError: + continue + except Exception as e: + logger.error(f"SSE 流读取错误: {e}") + + async def _send_sse_request(self, request: Dict[str, Any]) -> Dict[str, Any]: + """通过 SSE endpoint 发送请求""" + if not self._endpoint_url: + raise MCPConnectionError("endpoint URL 未初始化") + + request_id = request["id"] + future = asyncio.Future() + self._pending_requests[request_id] = future + + try: + async with self._session.post(self._endpoint_url, json=request) as response: + if response.status != 200: + error_text = await response.text() + raise MCPConnectionError(f"请求失败 {response.status}: {error_text}") + + return await asyncio.wait_for(future, timeout=self.timeout) + except asyncio.TimeoutError: + self._pending_requests.pop(request_id, None) + raise MCPConnectionError("请求超时") + + async def _send_sse_notification(self, notification: Dict[str, Any]): + """发送通知(无需响应)""" + if not self._endpoint_url: + raise MCPConnectionError("endpoint URL 未初始化") + + async with self._session.post(self._endpoint_url, json=notification) as response: + if response.status != 200: + logger.warning(f"通知发送失败: {response.status}") + async def _initialize_modelscope_session(self): """初始化 ModelScope MCP 会话""" init_request = { @@ -107,18 +219,12 @@ class SimpleMCPClient: "params": { "protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, - "clientInfo": { - "name": "MemoryBear", - "version": "1.0.0" - } + "clientInfo": {"name": "MemoryBear", "version": "1.0.0"} } } try: - async with self._session.post( - self.server_url, - json=init_request - ) as response: + async with self._session.post(self.server_url, json=init_request) as response: if response.status != 200: error_text = await response.text() raise MCPConnectionError(f"初始化失败 {response.status}: {error_text}") @@ -127,21 +233,16 @@ class SimpleMCPClient: if "error" in init_response: raise MCPConnectionError(f"初始化失败: {init_response['error']}") - # 获取 session ID session_id = response.headers.get("Mcp-Session-Id") or response.headers.get("mcp-session-id") if session_id: self._session.headers.update({"Mcp-Session-Id": session_id}) - # 发送 initialized 通知 initialized_notification = { "jsonrpc": "2.0", "method": "notifications/initialized" } - async with self._session.post( - self.server_url, - json=initialized_notification - ) as notif_response: + async with self._session.post(self.server_url, json=initialized_notification): pass except aiohttp.ClientError as e: @@ -149,12 +250,18 @@ class SimpleMCPClient: def _build_headers(self) -> Dict[str, str]: """构建请求头""" + # 基础 headers headers = { "Content-Type": "application/json", "Accept": "application/json, text/event-stream" } - # 添加认证头 + # 合并 connection_config 中的自定义 headers + custom_headers = self.connection_config.get("headers", {}) + if custom_headers: + headers.update(custom_headers) + + # 处理认证配置(认证 headers 优先级更高) auth_config = self.connection_config.get("auth_config", {}) auth_type = self.connection_config.get("auth_type", "none") @@ -178,7 +285,7 @@ class SimpleMCPClient: return headers async def _send_initialize(self): - """发送初始化消息""" + """发送初始化消息(WebSocket)""" init_message = { "jsonrpc": "2.0", "id": self._get_request_id(), @@ -186,124 +293,90 @@ class SimpleMCPClient: "params": { "protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, - "clientInfo": { - "name": "MemoryBear", - "version": "1.0.0" - } + "clientInfo": {"name": "MemoryBear", "version": "1.0.0"} } } await self._websocket.send(json.dumps(init_message)) + response = await self._websocket.recv() + response_data = json.loads(response) - # 等待初始化响应 - response = await asyncio.wait_for( - self._websocket.recv(), - timeout=self.timeout - ) + if "error" in response_data: + raise MCPConnectionError(f"初始化失败: {response_data['error']}") - init_response = json.loads(response) - if "error" in init_response: - raise MCPConnectionError(f"初始化失败: {init_response['error']}") + result = response_data.get("result", {}) + self._server_capabilities = result.get("capabilities", {}) + + await self._websocket.send(json.dumps({ + "jsonrpc": "2.0", + "method": "notifications/initialized" + })) + + async def list_tools(self) -> List[Dict[str, Any]]: + """获取工具列表""" + request = { + "jsonrpc": "2.0", + "id": self._get_request_id(), + "method": "tools/list" + } + + if self.is_websocket: + await self._websocket.send(json.dumps(request)) + response = await self._websocket.recv() + response_data = json.loads(response) + elif self.is_sse: + response_data = await self._send_sse_request(request) + else: + async with self._session.post(self.server_url, json=request) as response: + response_data = await response.json() + + if "error" in response_data: + raise MCPConnectionError(f"获取工具列表失败: {response_data['error']}") + + result = response_data.get("result", {}) + return result.get("tools", []) + + async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any: + """调用工具""" + request = { + "jsonrpc": "2.0", + "id": self._get_request_id(), + "method": "tools/call", + "params": {"name": tool_name, "arguments": arguments} + } + + if self.is_websocket: + await self._websocket.send(json.dumps(request)) + response = await self._websocket.recv() + response_data = json.loads(response) + elif self.is_sse: + response_data = await self._send_sse_request(request) + else: + async with self._session.post(self.server_url, json=request) as response: + response_data = await response.json() + + if "error" in response_data: + error = response_data["error"] + raise MCPConnectionError(f"工具调用失败: {error.get('message', '未知错误')}") + + return response_data.get("result", {}) + + def _get_request_id(self) -> int: + """生成请求 ID""" + self._request_id += 1 + return self._request_id async def _handle_websocket_messages(self): """处理 WebSocket 消息""" try: - while self._websocket and not self._websocket.closed: - try: - message = await self._websocket.recv() - data = json.loads(message) - - # 处理响应 - if "id" in data: - request_id = str(data["id"]) - if request_id in self._pending_requests: - future = self._pending_requests.pop(request_id) - if not future.done(): - future.set_result(data) - - except ConnectionClosed: - break - except Exception as e: - logger.error(f"处理WebSocket消息失败: {e}") - + async for message in self._websocket: + data = json.loads(message) + request_id = data.get("id") + if request_id and request_id in self._pending_requests: + future = self._pending_requests.pop(request_id) + if not future.done(): + future.set_result(data) + except ConnectionClosed: + logger.info("WebSocket 连接已关闭") except Exception as e: - logger.error(f"WebSocket消息处理异常: {e}") - - async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any: - """调用工具""" - request_data = { - "jsonrpc": "2.0", - "id": self._get_request_id(), - "method": "tools/call", - "params": { - "name": tool_name, - "arguments": arguments - } - } - - if self.is_websocket: - response = await self._send_websocket_request(request_data) - else: - response = await self._send_http_request(request_data) - - if "error" in response: - error = response["error"] - raise MCPConnectionError(f"工具调用失败: {error.get('message', '未知错误')}") - - return response.get("result", {}) - - async def list_tools(self) -> List[Dict[str, Any]]: - """获取工具列表""" - request_data = { - "jsonrpc": "2.0", - "id": self._get_request_id(), - "method": "tools/list", - "params": {} - } - - if self.is_websocket: - response = await self._send_websocket_request(request_data) - else: - response = await self._send_http_request(request_data) - - if "error" in response: - error = response["error"] - raise MCPConnectionError(f"获取工具列表失败: {error.get('message', '未知错误')}") - - result = response.get("result", {}) - return result.get("tools", []) - - async def _send_websocket_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]: - """发送WebSocket请求""" - request_id = str(request_data["id"]) - future = asyncio.Future() - self._pending_requests[request_id] = future - - try: - await self._websocket.send(json.dumps(request_data)) - response = await asyncio.wait_for(future, timeout=self.timeout) - return response - except asyncio.TimeoutError: - self._pending_requests.pop(request_id, None) - raise - - async def _send_http_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]: - """发送HTTP请求""" - try: - async with self._session.post( - self.server_url, - json=request_data - ) as response: - if response.status != 200: - error_text = await response.text() - raise MCPConnectionError(f"HTTP请求失败 {response.status}: {error_text}") - - return await response.json() - - except aiohttp.ClientError as e: - raise MCPConnectionError(f"HTTP请求失败: {e}") - - def _get_request_id(self) -> str: - """获取请求ID""" - self._request_id += 1 - return f"req_{self._request_id}_{int(time.time() * 1000)}" \ No newline at end of file + logger.error(f"WebSocket 消息处理错误: {e}") From 7bbe56d20ae12e1c99a8c43097b36031ecff5a37 Mon Sep 17 00:00:00 2001 From: yujiangping Date: Wed, 14 Jan 2026 17:14:00 +0800 Subject: [PATCH 10/32] style(chat): update message bubble max-width constraint - Change message bubble max-width from `rb:max-w-100` to `rb:max-w-[520px]` - Improve message content layout consistency and readability - Ensure proper text wrapping behavior for longer messages --- web/src/components/Chat/ChatContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/Chat/ChatContent.tsx b/web/src/components/Chat/ChatContent.tsx index ecc2e4e9..c90f9208 100644 --- a/web/src/components/Chat/ChatContent.tsx +++ b/web/src/components/Chat/ChatContent.tsx @@ -55,7 +55,7 @@ const ChatContent: FC = ({ } {/* 消息气泡框 */} -
Date: Wed, 14 Jan 2026 18:13:41 +0800 Subject: [PATCH 11/32] feat(workflow): support async memory writes via Celery --- api/app/core/workflow/nodes/memory/node.py | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/api/app/core/workflow/nodes/memory/node.py b/api/app/core/workflow/nodes/memory/node.py index f1c99ddb..f83ab7e1 100644 --- a/api/app/core/workflow/nodes/memory/node.py +++ b/api/app/core/workflow/nodes/memory/node.py @@ -3,8 +3,9 @@ from typing import Any from app.core.workflow.nodes import WorkflowState from app.core.workflow.nodes.base_node import BaseNode from app.core.workflow.nodes.memory.config import MemoryReadNodeConfig, MemoryWriteNodeConfig -from app.db import get_db_read, get_db_context +from app.db import get_db_read from app.services.memory_agent_service import MemoryAgentService +from app.tasks import write_message_task class MemoryReadNode(BaseNode): @@ -41,20 +42,20 @@ class MemoryWriteNode(BaseNode): self.typed_config = MemoryWriteNodeConfig(**self.config) async def execute(self, state: WorkflowState) -> Any: - with get_db_context() as db: - workspace_id = self.get_variable('sys.workspace_id', state) - end_user_id = self.get_variable("sys.user_id", state) + workspace_id = self.get_variable('sys.workspace_id', state) + end_user_id = self.get_variable("sys.user_id", state) - if not workspace_id: - raise RuntimeError("Workspace id is required") - if not end_user_id: - raise RuntimeError("End user id is required") + if not workspace_id: + raise RuntimeError("Workspace id is required") + if not end_user_id: + raise RuntimeError("End user id is required") - return await MemoryAgentService().write_memory( - group_id=end_user_id, - message=self._render_template(self.typed_config.message, state), - config_id=str(self.typed_config.config_id), - db=db, - storage_type="neo4j", - user_rag_memory_id="" - ) + write_message_task.delay( + end_user_id, + self._render_template(self.typed_config.message, state), + str(self.typed_config.config_id), + "neo4j", + "" + ) + + return "success" From a9901e04950b7b7de18ee3666911915e660bdbcf Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Wed, 14 Jan 2026 18:19:11 +0800 Subject: [PATCH 12/32] perf(workflow): eliminate workspace_id dependency in memory read/write nodes --- api/app/core/workflow/nodes/memory/node.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/api/app/core/workflow/nodes/memory/node.py b/api/app/core/workflow/nodes/memory/node.py index f83ab7e1..08a2b280 100644 --- a/api/app/core/workflow/nodes/memory/node.py +++ b/api/app/core/workflow/nodes/memory/node.py @@ -16,11 +16,8 @@ class MemoryReadNode(BaseNode): async def execute(self, state: WorkflowState) -> Any: self.typed_config = MemoryReadNodeConfig(**self.config) with get_db_read() as db: - workspace_id = self.get_variable('sys.workspace_id', state) end_user_id = self.get_variable("sys.user_id", state) - if not workspace_id: - raise RuntimeError("Workspace id is required") if not end_user_id: raise RuntimeError("End user id is required") @@ -42,11 +39,8 @@ class MemoryWriteNode(BaseNode): self.typed_config = MemoryWriteNodeConfig(**self.config) async def execute(self, state: WorkflowState) -> Any: - workspace_id = self.get_variable('sys.workspace_id', state) end_user_id = self.get_variable("sys.user_id", state) - if not workspace_id: - raise RuntimeError("Workspace id is required") if not end_user_id: raise RuntimeError("End user id is required") From 8262045b1e02dd38ae546f0981542ee39ee52e12 Mon Sep 17 00:00:00 2001 From: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Date: Wed, 14 Jan 2026 18:36:24 +0800 Subject: [PATCH 13/32] Fix/memory bug fix (#118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 --- api/app/services/memory_agent_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/services/memory_agent_service.py b/api/app/services/memory_agent_service.py index 2d78d796..e05daf4a 100644 --- a/api/app/services/memory_agent_service.py +++ b/api/app/services/memory_agent_service.py @@ -634,7 +634,7 @@ class MemoryAgentService: retrieved_content.append({query:statements}) if retrieved_content==[]: retrieved_content='' - if '信息不足,无法回答。' != str(final_answer) :#and retrieved_content!=[] + if '信息不足,无法回答。' != str(final_answer) and str(search_switch).strip() != "2":#and retrieved_content!=[] # 使用 upsert 方法 repo.upsert( end_user_id=end_user_id, # 确保这个变量在作用域内 From 7b6619b8de96c8a30d2ab93b6e8ef933d8027839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=90=E5=8A=9B=E9=BD=90?= <162269739+lanceyq@users.noreply.github.com> Date: Wed, 14 Jan 2026 18:43:08 +0800 Subject: [PATCH 14/32] Fix/problems (#116) * [fix]The repair model does not allow null values and does not support relational networks. * [fix]The repair model does not allow null values and does not support relational networks. * [changes]Restore field restrictions --- api/app/services/user_memory_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/app/services/user_memory_service.py b/api/app/services/user_memory_service.py index 8f25f477..9221ab06 100644 --- a/api/app/services/user_memory_service.py +++ b/api/app/services/user_memory_service.py @@ -16,6 +16,7 @@ from app.db import get_db_context from app.repositories.conversation_repository import ConversationRepository from app.repositories.end_user_repository import EndUserRepository from app.repositories.neo4j.neo4j_connector import Neo4jConnector +from app.schemas.memory_episodic_schema import EmotionSubject, EmotionType, type_mapping from app.services.implicit_memory_service import ImplicitMemoryService from app.services.memory_base_service import MemoryBaseService from app.services.memory_config_service import MemoryConfigService From 6e5e708a362e7fc71d7f9942c9b79f73d5ff39db Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Wed, 14 Jan 2026 20:52:04 +0800 Subject: [PATCH 15/32] fix(workflow): fix execution record insertion failure in released app --- .../controllers/public_share_controller.py | 7 ++-- api/app/services/app_chat_service.py | 2 +- api/app/utils/app_config_utils.py | 41 ++++++++++--------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/api/app/controllers/public_share_controller.py b/api/app/controllers/public_share_controller.py index 354a58ef..04da05df 100644 --- a/api/app/controllers/public_share_controller.py +++ b/api/app/controllers/public_share_controller.py @@ -466,7 +466,7 @@ async def chat( conversation_id=conversation.id, # 使用已创建的会话 ID user_id=str(new_end_user.id), # 转换为字符串 variables=payload.variables, - config= payload.agent_config, + config=agent_config, web_search=payload.web_search, memory=payload.memory, storage_type=storage_type, @@ -565,11 +565,12 @@ async def chat( config = workflow_config_4_app_release(release) if payload.stream: async def event_generator(): + async for event in app_chat_service.workflow_chat_stream( message=payload.message, conversation_id=conversation.id, # 使用已创建的会话 ID - user_id=new_end_user.id, # 转换为字符串 + user_id=end_user_id, # 转换为字符串 variables=payload.variables, config=config, web_search=payload.web_search, @@ -601,7 +602,7 @@ async def chat( message=payload.message, conversation_id=conversation.id, # 使用已创建的会话 ID - user_id=new_end_user.id, # 转换为字符串 + user_id=end_user_id, # 转换为字符串 variables=payload.variables, config=config, web_search=payload.web_search, diff --git a/api/app/services/app_chat_service.py b/api/app/services/app_chat_service.py index 0065c64b..bc2d6ca3 100644 --- a/api/app/services/app_chat_service.py +++ b/api/app/services/app_chat_service.py @@ -558,7 +558,7 @@ class AppChatService: config: WorkflowConfig, app_id: uuid.UUID, workspace_id: uuid.UUID, - user_id: Optional[str] = None, + user_id: str = None, variables: Optional[Dict[str, Any]] = None, web_search: bool = False, memory: bool = True, diff --git a/api/app/utils/app_config_utils.py b/api/app/utils/app_config_utils.py index 834d22af..e936ffb4 100644 --- a/api/app/utils/app_config_utils.py +++ b/api/app/utils/app_config_utils.py @@ -8,9 +8,11 @@ import uuid from typing import Dict, Any, Optional, Union from datetime import datetime +from app.db import get_db_read from app.models import AppRelease, WorkflowConfig from app.models.agent_app_config_model import AgentConfig from app.models.multi_agent_model import MultiAgentConfig +from app.repositories.workflow_repository import WorkflowConfigRepository def model_parameters_to_dict(model_parameters: Any) -> Optional[Dict[str, Any]]: @@ -24,18 +26,18 @@ def model_parameters_to_dict(model_parameters: Any) -> Optional[Dict[str, Any]]: """ if model_parameters is None: return None - + if isinstance(model_parameters, dict): return model_parameters - + # Pydantic v2 if hasattr(model_parameters, 'model_dump'): return model_parameters.model_dump() - + # Pydantic v1 if hasattr(model_parameters, 'dict'): return model_parameters.dict() - + # 其他情况尝试转换 try: return dict(model_parameters) @@ -54,17 +56,18 @@ def dict_to_model_parameters(data: Optional[Dict[str, Any]]) -> Optional[Any]: """ if data is None: return None - + from app.schemas import ModelParameters - + if isinstance(data, ModelParameters): return data - + if isinstance(data, dict): return ModelParameters(**data) - + return None + class AgentConfigProxy: """Proxy class for AgentConfig (legacy compatibility)""" @@ -78,8 +81,7 @@ class AgentConfigProxy: self.default_model_config_id = release.default_model_config_id -def agent_config_4_app_release(release: AppRelease ) -> AgentConfig: - +def agent_config_4_app_release(release: AppRelease) -> AgentConfig: config_dict = release.config agent_config = AgentConfig( @@ -95,11 +97,10 @@ def agent_config_4_app_release(release: AppRelease ) -> AgentConfig: return agent_config -def multi_agent_config_4_app_release(release: AppRelease ) -> MultiAgentConfig: +def multi_agent_config_4_app_release(release: AppRelease) -> MultiAgentConfig: config_dict = release.config - agent_config = MultiAgentConfig( app_id=release.app_id, default_model_config_id=release.default_model_config_id, @@ -116,24 +117,26 @@ def multi_agent_config_4_app_release(release: AppRelease ) -> MultiAgentConfig: return agent_config -def workflow_config_4_app_release(release: AppRelease ) -> WorkflowConfig: +def workflow_config_4_app_release(release: AppRelease) -> WorkflowConfig: config_dict = release.config - + with get_db_read() as db: + source_config = WorkflowConfigRepository(db).get_by_app_id(release.app_id) + source_config_id = source_config.id config = WorkflowConfig( - id=release.id, + id=source_config_id, app_id=release.app_id, nodes=config_dict.get("nodes", []), edges=config_dict.get("edges", []), variables=config_dict.get("variables", []), execution_config=config_dict.get("execution_config", {}), triggers=config_dict.get("triggers", []) - ) return config + def dict_to_multi_agent_config(config_dict: Dict[str, Any], app_id: Optional[uuid.UUID] = None): """Convert dict to MultiAgentConfig model object @@ -276,7 +279,8 @@ def agent_config_to_dict(agent_config) -> Dict[str, Any]: "id": str(agent_config.id), "app_id": str(agent_config.app_id), "system_prompt": agent_config.system_prompt, - "default_model_config_id": str(agent_config.default_model_config_id) if agent_config.default_model_config_id else None, + "default_model_config_id": str( + agent_config.default_model_config_id) if agent_config.default_model_config_id else None, "model_parameters": agent_config.model_parameters, "knowledge_retrieval": agent_config.knowledge_retrieval, "memory": agent_config.memory, @@ -338,6 +342,3 @@ def workflow_config_to_dict(workflow_config) -> Dict[str, Any]: "created_at": workflow_config.created_at.isoformat() if workflow_config.created_at else None, "updated_at": workflow_config.updated_at.isoformat() if workflow_config.updated_at else None } - - - From be285c85ec6aea8bbec25a3b1bcce9e970f38c65 Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Wed, 14 Jan 2026 21:34:34 +0800 Subject: [PATCH 16/32] perf(http): change resource not found status code to 400 --- api/app/core/error_codes.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/api/app/core/error_codes.py b/api/app/core/error_codes.py index 23023ca4..cb0084b7 100644 --- a/api/app/core/error_codes.py +++ b/api/app/core/error_codes.py @@ -110,24 +110,24 @@ HTTP_MAPPING = { BizCode.TOKEN_EXPIRED: 401, BizCode.TOKEN_BLACKLISTED: 401, BizCode.FORBIDDEN: 403, - BizCode.TENANT_NOT_FOUND: 404, + BizCode.TENANT_NOT_FOUND: 400, BizCode.WORKSPACE_NO_ACCESS: 403, - BizCode.NOT_FOUND: 404, + BizCode.NOT_FOUND: 400, BizCode.USER_NOT_FOUND: 200, - BizCode.WORKSPACE_NOT_FOUND: 404, - BizCode.MODEL_NOT_FOUND: 404, - BizCode.KNOWLEDGE_NOT_FOUND: 404, - BizCode.DOCUMENT_NOT_FOUND: 404, - BizCode.FILE_NOT_FOUND: 404, - BizCode.APP_NOT_FOUND: 404, - BizCode.RELEASE_NOT_FOUND: 404, + BizCode.WORKSPACE_NOT_FOUND: 400, + BizCode.MODEL_NOT_FOUND: 400, + BizCode.KNOWLEDGE_NOT_FOUND: 400, + BizCode.DOCUMENT_NOT_FOUND: 400, + BizCode.FILE_NOT_FOUND: 400, + BizCode.APP_NOT_FOUND: 400, + BizCode.RELEASE_NOT_FOUND: 400, BizCode.DUPLICATE_NAME: 409, BizCode.RESOURCE_ALREADY_EXISTS: 409, BizCode.VERSION_ALREADY_EXISTS: 409, BizCode.STATE_CONFLICT: 409, BizCode.PUBLISH_FAILED: 500, BizCode.NO_DRAFT_TO_PUBLISH: 400, - BizCode.ROLLBACK_TARGET_NOT_FOUND: 404, + BizCode.ROLLBACK_TARGET_NOT_FOUND: 400, BizCode.APP_TYPE_NOT_SUPPORTED: 400, BizCode.AGENT_CONFIG_MISSING: 400, BizCode.SHARE_DISABLED: 403, From 59f24fb5b4020a0c2a42283f5dbe0d48eb43db80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BF=8A=E7=94=B7?= Date: Thu, 15 Jan 2026 11:55:00 +0800 Subject: [PATCH 17/32] feat(muti agent): non-streaming output sub-nodes do not record the generated content of the conversation --- api/app/services/draft_run_service.py | 5 +++-- api/app/services/multi_agent_orchestrator.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/api/app/services/draft_run_service.py b/api/app/services/draft_run_service.py index 569684d5..50934226 100644 --- a/api/app/services/draft_run_service.py +++ b/api/app/services/draft_run_service.py @@ -245,7 +245,8 @@ class DraftRunService: storage_type: Optional[str] = None, user_rag_memory_id: Optional[str] = None, web_search: bool = True, - memory: bool = True + memory: bool = True, + sub_agent: bool = False ) -> Dict[str, Any]: """执行试运行(使用 LangChain Agent) @@ -435,7 +436,7 @@ class DraftRunService: elapsed_time = time.time() - start_time # 8. 保存会话消息 - if agent_config.memory and agent_config.memory.get("enabled"): + if not sub_agent and agent_config.memory and agent_config.memory.get("enabled"): await self._save_conversation_message( conversation_id=conversation_id, user_message=message, diff --git a/api/app/services/multi_agent_orchestrator.py b/api/app/services/multi_agent_orchestrator.py index b0c7a957..1972f344 100644 --- a/api/app/services/multi_agent_orchestrator.py +++ b/api/app/services/multi_agent_orchestrator.py @@ -1327,7 +1327,8 @@ class MultiAgentOrchestrator: web_search=web_search, memory=memory, storage_type=storage_type, - user_rag_memory_id=user_rag_memory_id + user_rag_memory_id=user_rag_memory_id, + sub_agent=True ) return result From ec25efcb756f51977cd8664121c9a7fd42096305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BF=8A=E7=94=B7?= Date: Thu, 15 Jan 2026 12:21:24 +0800 Subject: [PATCH 18/32] fix(mcp tool): bug fix for the sse protocol request header in the mcp tool --- api/app/core/tools/mcp/client.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api/app/core/tools/mcp/client.py b/api/app/core/tools/mcp/client.py index e513a147..c082b314 100644 --- a/api/app/core/tools/mcp/client.py +++ b/api/app/core/tools/mcp/client.py @@ -96,10 +96,7 @@ class SimpleMCPClient: """初始化 SSE MCP 会话 - 参考 Dify 实现""" try: # 建立 SSE 连接 - response = await self._session.get( - self.server_url, - headers={"Accept": "text/event-stream"} - ) + response = await self._session.get(self.server_url) if response.status != 200: error_text = await response.text() From a3f053ed02133070814a00c1de46febef266091c Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Thu, 15 Jan 2026 12:33:49 +0800 Subject: [PATCH 19/32] fix(workflow): Fix missing user ID in trial run sessions --- api/app/controllers/app_controller.py | 14 ++++++++++++-- api/app/services/workflow_service.py | 20 ++------------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index f55ea5b5..147f24d7 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -11,15 +11,16 @@ from app.core.response_utils import success from app.db import get_db from app.dependencies import get_current_user, cur_workspace_access_guard from app.models import User -from app.models.app_model import AppType, App +from app.models.app_model import AppType from app.repositories import knowledge_repository +from app.repositories.end_user_repository import EndUserRepository from app.schemas import app_schema from app.schemas.response_schema import PageData, PageMeta +from app.schemas.workflow_schema import WorkflowConfig as WorkflowConfigSchema from app.schemas.workflow_schema import WorkflowConfigUpdate from app.services import app_service, workspace_service from app.services.agent_config_helper import enrich_agent_config from app.services.app_service import AppService -from app.schemas.workflow_schema import WorkflowConfig as WorkflowConfigSchema from app.services.workflow_service import WorkflowService, get_workflow_service router = APIRouter(prefix="/apps", tags=["Apps"]) @@ -405,6 +406,15 @@ async def draft_run( # 只读操作,允许访问共享应用 service._validate_app_accessible(app, workspace_id) + if not payload.user_id: + end_user_repo = EndUserRepository(db) + new_end_user = end_user_repo.get_or_create_end_user( + app_id=app_id, + other_id=str(current_user.id), + original_user_id=str(current_user.id) # Save original user_id to other_id + ) + payload.user_id = str(new_end_user.id) + # 处理会话ID(创建或验证) conversation_id = await draft_service._ensure_conversation( conversation_id=payload.conversation_id, diff --git a/api/app/services/workflow_service.py b/api/app/services/workflow_service.py index f9988352..f7fc76a4 100644 --- a/api/app/services/workflow_service.py +++ b/api/app/services/workflow_service.py @@ -483,14 +483,6 @@ class WorkflowService: try: # 更新状态为运行中 self.update_execution_status(execution.execution_id, "running") - with get_db_context() as db: - end_user_repo = EndUserRepository(db) - new_end_user = end_user_repo.get_or_create_end_user( - app_id=app_id, - other_id=payload.user_id, - original_user_id=payload.user_id # Save original user_id to other_id - ) - end_user_id = str(new_end_user.id) executions = self.execution_repo.get_by_conversation_id(conversation_id=conversation_id_uuid) @@ -511,7 +503,7 @@ class WorkflowService: input_data=input_data, execution_id=execution.execution_id, workspace_id=str(workspace_id), - user_id=end_user_id + user_id=payload.user_id ) # 更新执行结果 @@ -638,14 +630,6 @@ class WorkflowService: try: # 更新状态为运行中 self.update_execution_status(execution.execution_id, "running") - with get_db_context() as db: - end_user_repo = EndUserRepository(db) - new_end_user = end_user_repo.get_or_create_end_user( - app_id=app_id, - other_id=payload.user_id, - original_user_id=payload.user_id # Save original user_id to other_id - ) - end_user_id = str(new_end_user.id) executions = self.execution_repo.get_by_conversation_id(conversation_id=conversation_id_uuid) for exec_res in executions: @@ -665,7 +649,7 @@ class WorkflowService: input_data=input_data, execution_id=execution.execution_id, workspace_id=str(workspace_id), - user_id=end_user_id + user_id=payload.user_id ): if event.get("event") == "workflow_end": From 6010e9e4ff6f0695b5820d73501d2de0851b38b1 Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Thu, 15 Jan 2026 13:15:53 +0800 Subject: [PATCH 20/32] fix(workflow): Fix missing user ID in trial run sessions --- api/app/services/workflow_service.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/app/services/workflow_service.py b/api/app/services/workflow_service.py index f7fc76a4..974d5418 100644 --- a/api/app/services/workflow_service.py +++ b/api/app/services/workflow_service.py @@ -13,11 +13,10 @@ from sqlalchemy.orm import Session from app.core.error_codes import BizCode from app.core.exceptions import BusinessException from app.core.workflow.validator import validate_workflow_config -from app.db import get_db, get_db_context +from app.db import get_db +from app.models.conversation_model import Message from app.models.workflow_model import WorkflowConfig, WorkflowExecution from app.repositories.conversation_repository import MessageRepository -from app.models.conversation_model import Message -from app.repositories.end_user_repository import EndUserRepository from app.repositories.workflow_repository import ( WorkflowConfigRepository, WorkflowExecutionRepository, From 2e1744c66b2b5d821d0fcb897f1da3ae3ed3798c Mon Sep 17 00:00:00 2001 From: Eternity <1533512157@qq.com> Date: Thu, 15 Jan 2026 13:18:50 +0800 Subject: [PATCH 21/32] fix(workflow): Fix missing user ID in trial run sessions --- api/app/controllers/app_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index 147f24d7..43f177ef 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -406,7 +406,7 @@ async def draft_run( # 只读操作,允许访问共享应用 service._validate_app_accessible(app, workspace_id) - if not payload.user_id: + if payload.user_id is None: end_user_repo = EndUserRepository(db) new_end_user = end_user_repo.get_or_create_end_user( app_id=app_id, From 973a0b2d472fa1de1129a3ce4585b200948d40af Mon Sep 17 00:00:00 2001 From: yujiangping Date: Thu, 15 Jan 2026 15:18:25 +0800 Subject: [PATCH 22/32] feat(home): add help center quick operation link - Add helpCenter.svg and helpCenter_active.svg menu icons for help center navigation - Add "Help Center" translation strings to English and Chinese i18n files - Update QuickOperation component to include help center as fourth quick operation - Implement external link handler that opens help documentation based on current language (zh or en) - Change grid layout from 3 columns to 4 columns to accommodate new help center card - Add file header documentation to QuickOperation component - Help center link redirects to https://docs.redbearai.com/s/{lang}-memorybear with language-specific routing --- web/src/assets/images/menu/helpCenter.svg | 14 ++++++++ .../assets/images/menu/helpCenter_active.svg | 14 ++++++++ web/src/i18n/en.ts | 5 ++- web/src/i18n/zh.ts | 3 +- .../views/Home/components/QuickOperation.tsx | 28 ++++++++++++++-- .../[knowledgeBaseId]/Private.tsx | 33 ++++++++++++++++++- 6 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 web/src/assets/images/menu/helpCenter.svg create mode 100644 web/src/assets/images/menu/helpCenter_active.svg diff --git a/web/src/assets/images/menu/helpCenter.svg b/web/src/assets/images/menu/helpCenter.svg new file mode 100644 index 00000000..504e309c --- /dev/null +++ b/web/src/assets/images/menu/helpCenter.svg @@ -0,0 +1,14 @@ + + + 使用帮助备份 + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/menu/helpCenter_active.svg b/web/src/assets/images/menu/helpCenter_active.svg new file mode 100644 index 00000000..2840c421 --- /dev/null +++ b/web/src/assets/images/menu/helpCenter_active.svg @@ -0,0 +1,14 @@ + + + 使用帮助 + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 27986e76..9201372d 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -91,6 +91,7 @@ export const en = { memberManagement: 'Member Management', memorySummary: 'Memory Summary', memoryConversation: 'Memory Validation', + helpCenter: 'Help Center', memorySummaryHandlers: 'Memory Summary Handlers', createMemorySummary: 'Create Memory Summary', memoryManagement: 'Memory Management', @@ -190,7 +191,8 @@ export const en = { memoryConversation: 'Memory Conversation', memoryConversationDesc: 'Memory Conversation', - + helpCenter: 'Help Center', + helpCenterDesc: 'Help Center', memorySummary: 'View Memory Summary', memorySummaryDesc: 'View Memory Summary Report', @@ -616,6 +618,7 @@ export const en = { retrieve:'Retrieve', processing: 'Processing', processingMode: 'Processing Mode', + processMsg: 'Processing Message', dataSize: 'Data Size', createUpdateTime: 'Create/Update Time', operation: 'Operation', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 8fa73fd3..63799463 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -789,7 +789,8 @@ export const zh = { memoryConversation: '记忆对话', memoryConversationDesc: '记忆对话', - + helpCenter: '帮助中心', + helpCenterDesc: '帮助中心', memorySummary: '查看记忆摘要', memorySummaryDesc: '查看记忆摘要报告', diff --git a/web/src/views/Home/components/QuickOperation.tsx b/web/src/views/Home/components/QuickOperation.tsx index 892dd8a0..d894417a 100644 --- a/web/src/views/Home/components/QuickOperation.tsx +++ b/web/src/views/Home/components/QuickOperation.tsx @@ -1,3 +1,11 @@ +/* + * @Description: + * @Version: 0.0.1 + * @Author: yujiangping + * @Date: 2026-01-05 17:22:23 + * @LastEditors: yujiangping + * @LastEditTime: 2026-01-15 14:55:51 + */ import { type FC } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom'; @@ -5,33 +13,49 @@ import Card from './Card'; import applicationIcon from '@/assets/images/menu/application_active.svg'; import knowledgeIcon from '@/assets/images/menu/knowledge_active.svg'; import memoryConversationIcon from '@/assets/images/menu/memoryConversation_active.svg'; +import helpCenterIcon from '@/assets/images/menu/helpCenter_active.svg' import arrowTopRight from '@/assets/images/home/arrow_top_right.svg'; const quickOperations = [ { key: 'createNewApplication', url: '/application' }, { key: 'createNewKnowledge', url: '/knowledge-base' }, { key: 'memoryConversation', url: '/memory-conversation' }, + { key: 'helpCenter', url: '' }, ] const quickOperationIcons: {[key: string]: string | undefined} = { createNewApplication: applicationIcon, createNewKnowledge: knowledgeIcon, memoryConversation: memoryConversationIcon, + helpCenter: helpCenterIcon } const QuickOperation:FC = () => { - const { t } = useTranslation() + const { t, i18n } = useTranslation() const navigate = useNavigate(); const handleJump = (url: string | null) => { if (url) { navigate(url) + }else{ + const currentLang = i18n.language; + const lang = currentLang === 'zh' ? 'zh' : 'en'; + const helpUrl = `https://docs.redbearai.com/s/${lang}-memorybear`; + + // 创建隐藏的 a 标签来避免弹窗拦截 + const link = document.createElement('a'); + link.href = helpUrl; + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); } } return ( -
+
{quickOperations.map(item => (
handleJump(item.url)}>
diff --git a/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx b/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx index 8087e596..382deac0 100644 --- a/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx +++ b/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useRef, useCallback, type FC } from 'react'; import { useNavigate, useParams, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Switch, Button, Dropdown, Space, Modal, message, Radio } from 'antd'; +import { Switch, Button, Dropdown, Space, Modal, message, Radio, Tooltip } from 'antd'; import type { MenuProps } from 'antd'; import SearchInput from '@/components/SearchInput' import Table, { type TableRef } from '@/components/Table' @@ -564,6 +564,37 @@ const Private: FC = () => { ); } + },{ + title: t('knowledgeBase.processMsg'), + dataIndex: 'progress_msg', + key: 'progress_msg', + width: 320, + render: (value: string) => { + if (!value) return '-'; + + // 解析日志格式,将 \n 转换为换行 + const formattedText = value.replace(/\\n/g, '\n'); + + return ( + {formattedText}} placement="topLeft"> +
+ {formattedText} +
+
+ ); + } }, { title: t('knowledgeBase.processingMode'), From 61f3a1805c5daee0142f23b19453b5b66b078317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=90=E5=8A=9B=E9=BD=90?= <162269739+lanceyq@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:45:20 +0800 Subject: [PATCH 23/32] [fix]Fix the timestamp in milliseconds (#127) --- api/app/services/memory_forget_service.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/app/services/memory_forget_service.py b/api/app/services/memory_forget_service.py index 8979682d..2db4cdc7 100644 --- a/api/app/services/memory_forget_service.py +++ b/api/app/services/memory_forget_service.py @@ -267,14 +267,14 @@ class MemoryForgetService: elif node_type_label == 'memorysummary': node_type_label = 'summary' - # 将 Neo4j DateTime 对象转换为时间戳 + # 将 Neo4j DateTime 对象转换为时间戳(毫秒) last_access_time = result['last_access_time'] last_access_dt = convert_neo4j_datetime_to_python(last_access_time) # 确保 datetime 带有时区信息(假定为 UTC),避免 naive datetime 导致的时区偏差 if last_access_dt: if last_access_dt.tzinfo is None: last_access_dt = last_access_dt.replace(tzinfo=timezone.utc) - last_access_timestamp = int(last_access_dt.timestamp()) + last_access_timestamp = int(last_access_dt.timestamp() * 1000) else: last_access_timestamp = 0 @@ -520,7 +520,7 @@ class MemoryForgetService: 'average_activation_value': result['average_activation'], 'low_activation_nodes': result['low_activation_nodes'] or 0, 'forgetting_threshold': forgetting_threshold, - 'timestamp': int(datetime.now().timestamp()) + 'timestamp': int(datetime.now().timestamp() * 1000) } else: activation_metrics = { @@ -530,7 +530,7 @@ class MemoryForgetService: 'average_activation_value': None, 'low_activation_nodes': 0, 'forgetting_threshold': forgetting_threshold, - 'timestamp': int(datetime.now().timestamp()) + 'timestamp': int(datetime.now().timestamp() * 1000) } # 收集节点类型分布 @@ -620,7 +620,7 @@ class MemoryForgetService: 'merged_count': record.merged_count, 'average_activation': record.average_activation_value, 'total_nodes': record.total_nodes, - 'execution_time': int(record.execution_time.timestamp()) + 'execution_time': int(record.execution_time.timestamp() * 1000) }) api_logger.info(f"成功获取最近 {len(recent_trends)} 个日期的历史趋势数据") @@ -661,7 +661,7 @@ class MemoryForgetService: 'node_distribution': node_distribution, 'recent_trends': recent_trends, 'pending_nodes': pending_nodes, - 'timestamp': int(datetime.now().timestamp()) + 'timestamp': int(datetime.now().timestamp() * 1000) } api_logger.info( From cdfe43ce2ce8bece8739d1683006b98f4995ec27 Mon Sep 17 00:00:00 2001 From: Eternity <61316157+myhMARS@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:45:52 +0800 Subject: [PATCH 24/32] fix(memory): Fix issue where no response is returned when conversation content is empty (#126) --- api/app/services/conversation_service.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/api/app/services/conversation_service.py b/api/app/services/conversation_service.py index 3695a222..275d6413 100644 --- a/api/app/services/conversation_service.py +++ b/api/app/services/conversation_service.py @@ -516,8 +516,16 @@ class ConversationService: conversation_messages = self.get_conversation_history( conversation_id=conversation_id, - max_history=30 + max_history=20 ) + if len(conversation_messages) == 0: + return ConversationOut( + theme="", + question=[], + summary="", + takeaways=[], + info_score=0, + ) with open('app/services/prompt/conversation_summary_system.jinja2', 'r', encoding='utf-8') as f: system_prompt = f.read() @@ -536,6 +544,7 @@ class ConversationService: ] logger.info(f"Invoking LLM for conversation_id={conversation_id}") model_resp = await llm.ainvoke(messages) + try: if isinstance(model_resp.content, str): result = json_repair.repair_json(model_resp.content, return_objects=True) From 000fbf6e9844102e4109aea5ac89839bb1885902 Mon Sep 17 00:00:00 2001 From: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:54:09 +0800 Subject: [PATCH 25/32] Fix/memory bug fix (#128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 读取的接口,去掉全局锁 --- api/app/services/memory_agent_service.py | 402 +++++++++++------------ 1 file changed, 195 insertions(+), 207 deletions(-) diff --git a/api/app/services/memory_agent_service.py b/api/app/services/memory_agent_service.py index e05daf4a..f0756764 100644 --- a/api/app/services/memory_agent_service.py +++ b/api/app/services/memory_agent_service.py @@ -9,7 +9,7 @@ import os import re import time import uuid -from threading import Lock + from typing import Any, AsyncGenerator, Dict, List, Optional import redis @@ -51,9 +51,7 @@ _neo4j_connector = Neo4jConnector() class MemoryAgentService: """Service for memory agent operations""" - def __init__(self): - self.user_locks: Dict[str, Lock] = {} - self.locks_lock = Lock() + def writer_messages_deal(self,messages,start_time,group_id,config_id,message): messages = str(messages).replace("'", '"').replace('\\n', '').replace('\n', '').replace('\\', '') @@ -83,12 +81,7 @@ class MemoryAgentService: raise ValueError(f"写入失败: {messages}") - def get_group_lock(self, group_id: str) -> Lock: - """Get lock for specific group to prevent concurrent processing""" - with self.locks_lock: - if group_id not in self.user_locks: - self.user_locks[group_id] = Lock() - return self.user_locks[group_id] + def extract_tool_call_info(self, event: Dict) -> bool: """Extract tool call information from event""" @@ -417,241 +410,236 @@ class MemoryAgentService: except ImportError: audit_logger = None - # Get group lock to prevent concurrent processing - group_lock = self.get_group_lock(group_id) + try: + config_service = MemoryConfigService(db) + memory_config = config_service.load_memory_config( + config_id=config_id, + service_name="MemoryAgentService" + ) + logger.info(f"Configuration loaded successfully: {memory_config.config_name}") + except ConfigurationError as e: + error_msg = f"Failed to load configuration for config_id: {config_id}: {e}" + logger.error(error_msg) - with group_lock: - # Step 1: Load configuration from database only - try: - config_service = MemoryConfigService(db) - memory_config = config_service.load_memory_config( + # Log failed operation + if audit_logger: + duration = time.time() - start_time + audit_logger.log_operation( + operation="READ", config_id=config_id, - service_name="MemoryAgentService" + group_id=group_id, + success=False, + duration=duration, + error=error_msg ) - logger.info(f"Configuration loaded successfully: {memory_config.config_name}") - except ConfigurationError as e: - error_msg = f"Failed to load configuration for config_id: {config_id}: {e}" - logger.error(error_msg) - # Log failed operation - if audit_logger: - duration = time.time() - start_time - audit_logger.log_operation( - operation="READ", - config_id=config_id, - group_id=group_id, - success=False, - duration=duration, - error=error_msg - ) + raise ValueError(error_msg) - raise ValueError(error_msg) + # Step 2: Prepare history + history.append({"role": "user", "content": message}) + logger.debug(f"Group ID:{group_id}, Message:{message}, History:{history}, Config ID:{config_id}") - # Step 2: Prepare history - history.append({"role": "user", "content": message}) - logger.debug(f"Group ID:{group_id}, Message:{message}, History:{history}, Config ID:{config_id}") + # Step 3: Initialize MCP client and execute read workflow + mcp_config = get_mcp_server_config() + client = MultiServerMCPClient(mcp_config) - # Step 3: Initialize MCP client and execute read workflow - mcp_config = get_mcp_server_config() - client = MultiServerMCPClient(mcp_config) + async with client.session('data_flow') as session: + session_start = time.time() + logger.debug("Connected to MCP Server: data_flow") - async with client.session('data_flow') as session: - session_start = time.time() - logger.debug("Connected to MCP Server: data_flow") - - tools_start = time.time() - tools = await load_mcp_tools(session) - tools_time = time.time() - tools_start - logger.info(f"[PERF] MCP tools loading took: {tools_time:.4f}s") - - outputs = [] - intermediate_outputs = [] - seen_intermediates = set() # Track seen intermediate outputs to avoid duplicates + tools_start = time.time() + tools = await load_mcp_tools(session) + tools_time = time.time() - tools_start + logger.info(f"[PERF] MCP tools loading took: {tools_time:.4f}s") - # Pass memory_config to the graph workflow - graph_start = time.time() - async with make_read_graph(group_id, tools, search_switch, group_id, group_id, memory_config=memory_config, storage_type=storage_type, user_rag_memory_id=user_rag_memory_id) as graph: - graph_init_time = time.time() - graph_start - logger.info(f"[PERF] Graph initialization took: {graph_init_time:.4f}s") - - start = time.time() - config = {"configurable": {"thread_id": group_id}} - workflow_errors = [] # Track errors from workflow - - event_count = 0 - async for event in graph.astream( - {"messages": history, "memory_config": memory_config, "errors": []}, - stream_mode="values", - config=config - ): - event_count += 1 - event_start = time.time() - messages = event.get('messages') - # Capture any errors from the state - if event.get('errors'): - workflow_errors.extend(event.get('errors', [])) + outputs = [] + intermediate_outputs = [] + seen_intermediates = set() # Track seen intermediate outputs to avoid duplicates - for msg in messages: - msg_content = msg.content - msg_role = msg.__class__.__name__.lower().replace("message", "") - outputs.append({ - "role": msg_role, - "content": msg_content - }) + # Pass memory_config to the graph workflow + graph_start = time.time() + async with make_read_graph(group_id, tools, search_switch, group_id, group_id, memory_config=memory_config, storage_type=storage_type, user_rag_memory_id=user_rag_memory_id) as graph: + graph_init_time = time.time() - graph_start + logger.info(f"[PERF] Graph initialization took: {graph_init_time:.4f}s") - # Extract intermediate outputs - if hasattr(msg, 'content'): - try: - # Handle MCP content format: [{'type': 'text', 'text': '...'}] - content_to_parse = msg_content - if isinstance(msg_content, list): - for block in msg_content: - if isinstance(block, dict) and block.get('type') == 'text': - content_to_parse = block.get('text', '') - break - else: - continue # No text block found + start = time.time() + config = {"configurable": {"thread_id": group_id}} + workflow_errors = [] # Track errors from workflow - # Try to parse content as JSON - if isinstance(content_to_parse, str): - try: - parsed = json.loads(content_to_parse) - if isinstance(parsed, dict): - # Check for single intermediate output - if '_intermediate' in parsed: - intermediate_data = parsed['_intermediate'] + event_count = 0 + async for event in graph.astream( + {"messages": history, "memory_config": memory_config, "errors": []}, + stream_mode="values", + config=config + ): + event_count += 1 + event_start = time.time() + messages = event.get('messages') + # Capture any errors from the state + if event.get('errors'): + workflow_errors.extend(event.get('errors', [])) + + for msg in messages: + msg_content = msg.content + msg_role = msg.__class__.__name__.lower().replace("message", "") + outputs.append({ + "role": msg_role, + "content": msg_content + }) + + # Extract intermediate outputs + if hasattr(msg, 'content'): + try: + # Handle MCP content format: [{'type': 'text', 'text': '...'}] + content_to_parse = msg_content + if isinstance(msg_content, list): + for block in msg_content: + if isinstance(block, dict) and block.get('type') == 'text': + content_to_parse = block.get('text', '') + break + else: + continue # No text block found + + # Try to parse content as JSON + if isinstance(content_to_parse, str): + try: + parsed = json.loads(content_to_parse) + if isinstance(parsed, dict): + # Check for single intermediate output + if '_intermediate' in parsed: + intermediate_data = parsed['_intermediate'] + output_key = self._create_intermediate_key(intermediate_data) + + if output_key not in seen_intermediates: + seen_intermediates.add(output_key) + intermediate_outputs.append(self._format_intermediate_output(intermediate_data)) + + # Check for multiple intermediate outputs (from Retrieve) + if '_intermediates' in parsed: + for intermediate_data in parsed['_intermediates']: output_key = self._create_intermediate_key(intermediate_data) if output_key not in seen_intermediates: seen_intermediates.add(output_key) intermediate_outputs.append(self._format_intermediate_output(intermediate_data)) + except (json.JSONDecodeError, ValueError): + pass + except Exception as e: + logger.debug(f"Failed to extract intermediate output: {e}") - # Check for multiple intermediate outputs (from Retrieve) - if '_intermediates' in parsed: - for intermediate_data in parsed['_intermediates']: - output_key = self._create_intermediate_key(intermediate_data) + event_time = time.time() - event_start + logger.info(f"[PERF] Event {event_count} processing took: {event_time:.4f}s") - if output_key not in seen_intermediates: - seen_intermediates.add(output_key) - intermediate_outputs.append(self._format_intermediate_output(intermediate_data)) - except (json.JSONDecodeError, ValueError): - pass - except Exception as e: - logger.debug(f"Failed to extract intermediate output: {e}") - - event_time = time.time() - event_start - logger.info(f"[PERF] Event {event_count} processing took: {event_time:.4f}s") + workflow_duration = time.time() - start + session_duration = time.time() - session_start + logger.info(f"[PERF] Read graph workflow completed in {workflow_duration}s") + logger.info(f"[PERF] Total session duration: {session_duration:.4f}s") + logger.info(f"[PERF] Total events processed: {event_count}") + # Extract final answer + final_answer = "" + for messages in outputs: + if messages['role'] == 'tool': + message = messages['content'] - workflow_duration = time.time() - start - session_duration = time.time() - session_start - logger.info(f"[PERF] Read graph workflow completed in {workflow_duration}s") - logger.info(f"[PERF] Total session duration: {session_duration:.4f}s") - logger.info(f"[PERF] Total events processed: {event_count}") - # Extract final answer - final_answer = "" - for messages in outputs: - if messages['role'] == 'tool': - message = messages['content'] + # Handle MCP content format: [{'type': 'text', 'text': '...'}] + if isinstance(message, list): + # Extract text from MCP content blocks + for block in message: + if isinstance(block, dict) and block.get('type') == 'text': + message = block.get('text', '') + break + else: + continue # No text block found - # Handle MCP content format: [{'type': 'text', 'text': '...'}] - if isinstance(message, list): - # Extract text from MCP content blocks - for block in message: - if isinstance(block, dict) and block.get('type') == 'text': - message = block.get('text', '') - break - else: - continue # No text block found + try: + parsed = json.loads(message) if isinstance(message, str) else message + if isinstance(parsed, dict): + if parsed.get('status') == 'success': + summary_result = parsed.get('summary_result') + if summary_result: + final_answer = summary_result + except (json.JSONDecodeError, ValueError): + pass - try: - parsed = json.loads(message) if isinstance(message, str) else message - if isinstance(parsed, dict): - if parsed.get('status') == 'success': - summary_result = parsed.get('summary_result') - if summary_result: - final_answer = summary_result - except (json.JSONDecodeError, ValueError): - pass + # 记录成功的操作 + total_duration = time.time() - start_time - # 记录成功的操作 - total_duration = time.time() - start_time + # Check for workflow errors + if workflow_errors: + error_details = "; ".join([f"{e['tool']}: {e['error']}" for e in workflow_errors]) + logger.warning(f"Read workflow completed with errors: {error_details}") - # Check for workflow errors - if workflow_errors: - error_details = "; ".join([f"{e['tool']}: {e['error']}" for e in workflow_errors]) - logger.warning(f"Read workflow completed with errors: {error_details}") - - if audit_logger: - audit_logger.log_operation( - operation="READ", - config_id=config_id, - group_id=group_id, - success=False, - duration=total_duration, - error=error_details, - details={ - "search_switch": search_switch, - "history_length": len(history), - "intermediate_outputs_count": len(intermediate_outputs), - "has_answer": bool(final_answer), - "errors": workflow_errors - } - ) - - # Raise error if no answer was produced - if not final_answer: - raise ValueError(f"Read workflow failed: {error_details}") - - if audit_logger and not workflow_errors: + if audit_logger: audit_logger.log_operation( operation="READ", config_id=config_id, group_id=group_id, - success=True, + success=False, duration=total_duration, + error=error_details, details={ "search_switch": search_switch, "history_length": len(history), "intermediate_outputs_count": len(intermediate_outputs), - "has_answer": bool(final_answer) + "has_answer": bool(final_answer), + "errors": workflow_errors } ) - retrieved_content=[] - repo = ShortTermMemoryRepository(db) - if str(search_switch)!="2": - for intermediate in intermediate_outputs: - print(intermediate) - intermediate_type=intermediate['type'] - if intermediate_type=="search_result": - query=intermediate['query'] - raw_results=intermediate['raw_results'] - reranked_results=raw_results.get('reranked_results',[]) - try: - statements=[statement['statement'] for statement in reranked_results.get('statements', [])] - except Exception: - statements=[] - statements=list(set(statements)) - retrieved_content.append({query:statements}) - if retrieved_content==[]: - retrieved_content='' - if '信息不足,无法回答。' != str(final_answer) and str(search_switch).strip() != "2":#and retrieved_content!=[] - # 使用 upsert 方法 - repo.upsert( - end_user_id=end_user_id, # 确保这个变量在作用域内 - messages=ori_message, - aimessages=final_answer, - retrieved_content=retrieved_content, - search_switch=str(search_switch) - ) - print("写入成功") + + # Raise error if no answer was produced + if not final_answer: + raise ValueError(f"Read workflow failed: {error_details}") + + if audit_logger and not workflow_errors: + audit_logger.log_operation( + operation="READ", + config_id=config_id, + group_id=group_id, + success=True, + duration=total_duration, + details={ + "search_switch": search_switch, + "history_length": len(history), + "intermediate_outputs_count": len(intermediate_outputs), + "has_answer": bool(final_answer) + } + ) + retrieved_content=[] + repo = ShortTermMemoryRepository(db) + if str(search_switch)!="2": + for intermediate in intermediate_outputs: + print(intermediate) + intermediate_type=intermediate['type'] + if intermediate_type=="search_result": + query=intermediate['query'] + raw_results=intermediate['raw_results'] + reranked_results=raw_results.get('reranked_results',[]) + try: + statements=[statement['statement'] for statement in reranked_results.get('statements', [])] + except Exception: + statements=[] + statements=list(set(statements)) + retrieved_content.append({query:statements}) + if retrieved_content==[]: + retrieved_content='' + if '信息不足,无法回答。' != str(final_answer) and str(search_switch).strip() != "2":#and retrieved_content!=[] + # 使用 upsert 方法 + repo.upsert( + end_user_id=end_user_id, # 确保这个变量在作用域内 + messages=ori_message, + aimessages=final_answer, + retrieved_content=retrieved_content, + search_switch=str(search_switch) + ) + print("写入成功") - return { - "answer": final_answer, - "intermediate_outputs": intermediate_outputs - } - + return { + "answer": final_answer, + "intermediate_outputs": intermediate_outputs + } + def _create_intermediate_key(self, output: Dict) -> str: """ Create a unique key for an intermediate output to detect duplicates. From d1f44ef650b71ba3c9ab62eafcdf29c89a4b3bf5 Mon Sep 17 00:00:00 2001 From: yujiangping Date: Thu, 15 Jan 2026 17:33:47 +0800 Subject: [PATCH 26/32] fix(knowledge-graph): improve tooltip styling and text wrapping - Add max-width constraint to node and edge tooltip containers - Enable word breaking and preserve whitespace formatting for edge descriptions - Prevent tooltip overflow and improve readability of long description text --- web/src/views/KnowledgeBase/components/KnowledgeGraph.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/views/KnowledgeBase/components/KnowledgeGraph.tsx b/web/src/views/KnowledgeBase/components/KnowledgeGraph.tsx index 8ec367c5..55f8fcfc 100644 --- a/web/src/views/KnowledgeBase/components/KnowledgeGraph.tsx +++ b/web/src/views/KnowledgeBase/components/KnowledgeGraph.tsx @@ -292,7 +292,7 @@ const KnowledgeGraph: FC = ({ data, loading = false }) => { if (params.dataType === 'node') { const node = params.data as KnowledgeNode return ` -
+
${node.entity_name}
类型: ${node.entity_type}
重要度: ${(node.pagerank * 100).toFixed(2)}%
@@ -301,10 +301,10 @@ const KnowledgeGraph: FC = ({ data, loading = false }) => { } else if (params.dataType === 'edge') { const edge = params.data as KnowledgeEdge return ` -
+
关系
权重: ${edge.weight}
-
${edge.description}
+
${edge.description}
` } From d8fd58563189bb5467e8fae761cfd6a0d4a2378f Mon Sep 17 00:00:00 2001 From: yujiangping Date: Thu, 15 Jan 2026 18:48:04 +0800 Subject: [PATCH 27/32] feat(conversation): enhance empty state UI and improve quick action descriptions - Add new chat empty state image asset (chatEmpty.png) - Update English quick action descriptions with more compelling copy for applications, knowledge base, memory conversation, and help center - Update Chinese quick action descriptions with concise, marketing-focused messaging - Replace conversation empty state image from generic background to dedicated chat empty illustration - Improve user experience with clearer value propositions for each quick action feature --- web/src/assets/images/empty/chatEmpty.png | Bin 0 -> 189509 bytes web/src/i18n/en.ts | 8 ++++---- web/src/i18n/zh.ts | 10 +++++----- web/src/views/Conversation/index.tsx | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 web/src/assets/images/empty/chatEmpty.png diff --git a/web/src/assets/images/empty/chatEmpty.png b/web/src/assets/images/empty/chatEmpty.png new file mode 100644 index 0000000000000000000000000000000000000000..8ce1f7195180b1af6c385d5ecadbbd8b6a7cef04 GIT binary patch literal 189509 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 Fe*ksQq#OVM literal 0 HcmV?d00001 diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index b10f3d5c..a8f9207d 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -184,15 +184,15 @@ export const en = { createNewMemorySummary: 'Create New Memory Entry', createNewApplication: 'Create New Application', - createNewApplicationDesc: 'Create New Space Application', + createNewApplicationDesc: 'Build an app in just 3 minutes with zero-code drag-and-drop.', createNewKnowledge: 'Create New Knowledge', - createNewKnowledgeDesc: 'Create a new memory entry', + createNewKnowledgeDesc: 'Transform your data into a fully searchable, dedicated knowledge base in seconds.', memoryConversation: 'Memory Conversation', - memoryConversationDesc: 'Memory Conversation', + memoryConversationDesc: 'The more you use it, the better AI understands you.', helpCenter: 'Help Center', - helpCenterDesc: 'Help Center', + helpCenterDesc: 'One-stop support to answer your questions and get you started fast.', memorySummary: 'View Memory Summary', memorySummaryDesc: 'View Memory Summary Report', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 6c3e3ec1..832e5d29 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -782,15 +782,15 @@ export const zh = { createNewMemorySummary: '创建新记忆条目', createNewApplication: '创建新应用', - createNewApplicationDesc: '创建新空间应用', + createNewApplicationDesc: '零代码拖拽3分钟创应用', - createNewKnowledge: '创建新知识', - createNewKnowledgeDesc: '创建新记忆条目', + createNewKnowledge: '创建知识库', + createNewKnowledgeDesc: '秒变可搜索的专属知识库', memoryConversation: '记忆对话', - memoryConversationDesc: '记忆对话', + memoryConversationDesc: '让AI越用越懂你', helpCenter: '帮助中心', - helpCenterDesc: '帮助中心', + helpCenterDesc: '一站式解决疑问快速上手', memorySummary: '查看记忆摘要', memorySummaryDesc: '查看记忆摘要报告', diff --git a/web/src/views/Conversation/index.tsx b/web/src/views/Conversation/index.tsx index 6ccb35ec..873cb56c 100644 --- a/web/src/views/Conversation/index.tsx +++ b/web/src/views/Conversation/index.tsx @@ -11,6 +11,7 @@ import Empty from '@/components/Empty' import { formatDateTime } from '@/utils/format'; import { randomString } from '@/utils/common' import BgImg from '@/assets/images/conversation/bg.png' +import ChatEmpty from '@/assets/images/empty/chatEmpty.png' import Chat from '@/components/Chat' import type { ChatItem } from '@/components/Chat/types' import ButtonCheckbox from '@/components/ButtonCheckbox' @@ -261,7 +262,7 @@ const Conversation: FC = () => {

} + empty={} contentClassName="rb:h-[calc(100%-152px)] " data={chatList} streamLoading={streamLoading} From 99d7061a4fcf1d898bdfb6ea1359cce97d4b401e Mon Sep 17 00:00:00 2001 From: yujiangping Date: Thu, 15 Jan 2026 19:00:28 +0800 Subject: [PATCH 28/32] feat(conversation): add empty state title for memory conversation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add chatEmpty translation key to English i18n file with message "Is there anything I can help you with?" - Add chatEmpty translation key to Chinese i18n file with message "有什么我可以帮您的吗?" - Update Chat component empty state to display title using chatEmpty translation instead of only showing subTitle - Improve empty state UX by providing a welcoming greeting message to users --- web/src/i18n/en.ts | 1 + web/src/i18n/zh.ts | 1 + web/src/views/Conversation/index.tsx | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index ccb8d1ce..bf3dffc6 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -1449,6 +1449,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re deduplication_desc: 'Deduplication and disambiguation completed, {{count}} unique entities in total' }, memoryConversation: { + chatEmpty:'Is there anything I can help you with?', searchPlaceholder: 'Input user ID...', userID: 'User ID', testMemoryConversation: 'Test Memory Conversation', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 6ea8b1af..2d76f474 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -1523,6 +1523,7 @@ export const zh = { deduplication_desc: '去重消歧完成,最终{{count}}个唯一实体' }, memoryConversation: { + chatEmpty:'有什么我可以帮您的吗?', searchPlaceholder: '输入用户ID...', userID: '用户ID', testMemoryConversation: '测试记忆对话', diff --git a/web/src/views/Conversation/index.tsx b/web/src/views/Conversation/index.tsx index 873cb56c..12d17cda 100644 --- a/web/src/views/Conversation/index.tsx +++ b/web/src/views/Conversation/index.tsx @@ -262,7 +262,7 @@ const Conversation: FC = () => {
} + empty={} contentClassName="rb:h-[calc(100%-152px)] " data={chatList} streamLoading={streamLoading} From 1fb18cc11c8c3d87e04a869e15d555e1ac6feb0c Mon Sep 17 00:00:00 2001 From: yujiangping Date: Thu, 15 Jan 2026 20:25:05 +0800 Subject: [PATCH 29/32] fix(quick-actions): correct space management navigation route - Fix typo in space management quick action route from '/spce' to '/space' - Ensure users are correctly navigated to the space management page when clicking the quick action --- web/src/views/Index/components/QuickActions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/views/Index/components/QuickActions.tsx b/web/src/views/Index/components/QuickActions.tsx index edf5166e..063014df 100644 --- a/web/src/views/Index/components/QuickActions.tsx +++ b/web/src/views/Index/components/QuickActions.tsx @@ -47,7 +47,7 @@ const QuickActions: FC = ({ onNavigate }) => { key: 'space-management', icon: spaceIcon, title: t('quickActions.spaceManagement'), - onClick: () => onNavigate?.('/spce') + onClick: () => onNavigate?.('/space') }, // { // key: 'workflow-orchestration', From 281746031c56911a1203cb33ca19bc14957f073e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BF=8A=E7=94=B7?= Date: Thu, 15 Jan 2026 20:46:06 +0800 Subject: [PATCH 30/32] fix(multi agent): the default value of the collaboration mode has been changed to "supervisor" --- api/app/controllers/multi_agent_controller.py | 2 +- api/app/utils/app_config_utils.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/app/controllers/multi_agent_controller.py b/api/app/controllers/multi_agent_controller.py index 55614dea..dbcc2536 100644 --- a/api/app/controllers/multi_agent_controller.py +++ b/api/app/controllers/multi_agent_controller.py @@ -74,7 +74,7 @@ def get_multi_agent_configs( "app_id": str(app_id), "default_model_config_id": None, "model_parameters": None, - "orchestration_mode": "conditional", + "orchestration_mode": "supervisor", "sub_agents": [], "routing_rules": [], "execution_config": { diff --git a/api/app/utils/app_config_utils.py b/api/app/utils/app_config_utils.py index e936ffb4..4a35a4cc 100644 --- a/api/app/utils/app_config_utils.py +++ b/api/app/utils/app_config_utils.py @@ -107,7 +107,7 @@ def multi_agent_config_4_app_release(release: AppRelease) -> MultiAgentConfig: model_parameters=config_dict.get("model_parameters"), master_agent_id=config_dict.get("master_agent_id"), master_agent_name=config_dict.get("master_agent_name"), - orchestration_mode=config_dict.get("orchestration_mode", "conditional"), + orchestration_mode=config_dict.get("orchestration_mode", "supervisor"), sub_agents=config_dict.get("sub_agents", []), routing_rules=config_dict.get("routing_rules"), execution_config=config_dict.get("execution_config", {}), @@ -152,7 +152,7 @@ def dict_to_multi_agent_config(config_dict: Dict[str, Any], app_id: Optional[uui ... "app_id": "uuid-here", ... "master_agent_id": "master-uuid", ... "master_agent_name": "Master Agent", - ... "orchestration_mode": "conditional", + ... "orchestration_mode": "supervisor", ... "sub_agents": [ ... {"agent_id": "sub1-uuid", "name": "Sub Agent 1", "role": "specialist", "priority": 1}, ... {"agent_id": "sub2-uuid", "name": "Sub Agent 2", "role": "specialist", "priority": 2} @@ -189,7 +189,7 @@ def dict_to_multi_agent_config(config_dict: Dict[str, Any], app_id: Optional[uui app_id=final_app_id, master_agent_id=master_agent_id, master_agent_name=config_dict.get("master_agent_name"), - orchestration_mode=config_dict.get("orchestration_mode", "conditional"), + orchestration_mode=config_dict.get("orchestration_mode", "supervisor"), sub_agents=config_dict.get("sub_agents", []), routing_rules=config_dict.get("routing_rules"), execution_config=config_dict.get("execution_config", {}), From f9a35d0cdc0f95cb263f57d354955b9d277f9504 Mon Sep 17 00:00:00 2001 From: yujiangping Date: Thu, 15 Jan 2026 21:03:07 +0800 Subject: [PATCH 31/32] feat(i18n): customize Tour component button text and add finish button label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add finishButtonText translation key to English and Chinese locale files - Create customZhCN locale with Chinese Tour button labels (下一步, 上一步, 立即体验) - Create customEnUS locale with English Tour button labels (Next, Previous, Try it now) - Update locale store to use custom locale configurations instead of default Ant Design locales - Fix changeLanguage method to apply custom locale mappings correctly - Add file headers with metadata to GuideCard and locale store files - Improve Tour component UX by providing localized button text for better user experience --- web/src/i18n/en.ts | 1 + web/src/i18n/zh.ts | 1 + web/src/store/locale.ts | 34 ++++++++++++++++++-- web/src/views/Index/components/GuideCard.tsx | 8 +++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index bf3dffc6..c718528c 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -71,6 +71,7 @@ export const en = { stepTwoDescription: 'Here you can create and manage spaces to organize models and data for different use cases.Once your spaces are ready, head to User Management to invite members and manage access.👉 Click User Management in the left menu to continue.', stepThree: 'This is User Management', stepThreeDescription: 'Here you can create users, assign roles, and manage access for your team.Once users are set up, the basic configuration is complete and you’re ready to start using the platform 🎉', + finishButtonText: 'Get Started', }, menu: { home: 'Home', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 2d76f474..805b21b8 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -71,6 +71,7 @@ export const zh = { stepTwoDescription: '你可以在这里创建和管理不同的空间,把模型和数据组织到具体的使用场景中。空间创建完成后,可以去 User Management 邀请成员、分配权限,一起协作使用。👉 点击左侧 User Management 继续。', stepThree: '这里是用户管理页', stepThreeDescription: '你可以在这里创建用户、分配角色,并管理团队成员的访问权限。完成用户设置后,基础配置就准备好了,可以开始实际使用平台的各项功能了 🎉', + finishButtonText: '开始使用', }, menu: { home: '首页', diff --git a/web/src/store/locale.ts b/web/src/store/locale.ts index e0393b9d..4fbd79ed 100644 --- a/web/src/store/locale.ts +++ b/web/src/store/locale.ts @@ -1,3 +1,11 @@ +/* + * @Description: + * @Version: 0.0.1 + * @Author: yujiangping + * @Date: 2026-01-05 17:22:23 + * @LastEditors: yujiangping + * @LastEditTime: 2026-01-15 21:02:43 + */ import { create } from 'zustand' import enUS from 'antd/locale/en_US'; import zhCN from 'antd/locale/zh_CN'; @@ -12,6 +20,28 @@ import { timezoneToAntdLocaleMap } from '@/utils/timezones'; dayjs.extend(utc); dayjs.extend(timezone); +// 自定义中文 locale,修改 Tour 组件的按钮文字 +const customZhCN: Locale = { + ...zhCN, + Tour: { + ...zhCN.Tour, + Next: '下一步', + Previous: '上一步', + Finish: '立即体验', + }, +}; + +// 自定义英文 locale,修改 Tour 组件的按钮文字 +const customEnUS: Locale = { + ...enUS, + Tour: { + ...enUS.Tour, + Next: 'Next', + Previous: 'Previous', + Finish: 'Try it now', + }, +}; + interface I18nState { language: string; @@ -23,7 +53,7 @@ interface I18nState { const initialTimeZone = localStorage.getItem('timeZone') || 'Asia/Shanghai' const initialLanguage = localStorage.getItem('language') || 'en' -const initialLocale = initialLanguage === 'en' ? enUS : zhCN +const initialLocale = initialLanguage === 'en' ? customEnUS : customZhCN i18n.changeLanguage(initialLanguage) export const useI18n = create((set, get) => ({ @@ -32,7 +62,7 @@ export const useI18n = create((set, get) => ({ timeZone: initialTimeZone, changeLanguage: (language: string) => { i18n.changeLanguage(language) - const localeName = timezoneToAntdLocaleMap[language] || enUS; + const localeName = language === 'en' ? customEnUS : customZhCN; set({ language: language, locale: localeName }) }, changeTimeZone: (timeZone: string) => { diff --git a/web/src/views/Index/components/GuideCard.tsx b/web/src/views/Index/components/GuideCard.tsx index d60eae36..a8560136 100644 --- a/web/src/views/Index/components/GuideCard.tsx +++ b/web/src/views/Index/components/GuideCard.tsx @@ -1,3 +1,11 @@ +/* + * @Description: + * @Version: 0.0.1 + * @Author: yujiangping + * @Date: 2026-01-13 11:44:06 + * @LastEditors: yujiangping + * @LastEditTime: 2026-01-15 20:59:57 + */ import React, { useState, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; From c2998154e0440b6d9315c7b7e7207e2550ad12b4 Mon Sep 17 00:00:00 2001 From: lixinyue11 <94037597+lixinyue11@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:10:10 +0800 Subject: [PATCH 32/32] Fix/memory bug fix (#134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 图谱数据量限制数量去掉 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 用户详情优化 * 读取的接口,去掉全局锁 * 输出数组 --- api/app/services/memory_entity_relationship_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/services/memory_entity_relationship_service.py b/api/app/services/memory_entity_relationship_service.py index ca97fb39..eedb7c29 100644 --- a/api/app/services/memory_entity_relationship_service.py +++ b/api/app/services/memory_entity_relationship_service.py @@ -597,7 +597,7 @@ class MemoryInteraction: group_id = ori_data[0]['group_id'] Space_User = await self.connector.execute_query(Memory_Space_User, group_id=group_id) if not Space_User: - return '不存在用户' + return [] user_id=Space_User[0]['id'] results = await self.connector.execute_query(Memory_Space_Associative, id=self.id,user_id=user_id)