Ver Fonte

feat: add conversation_id and user_id in chatflow/workflow system vars (#3771)

Co-authored-by: Joel <iamjoel007@gmail.com>
takatost há 1 ano atrás
pai
commit
3da179f77b

+ 11 - 2
api/core/app/apps/advanced_chat/app_runner.py

@@ -18,7 +18,7 @@ from core.workflow.entities.node_entities import SystemVariable
 from core.workflow.nodes.base_node import UserFrom
 from core.workflow.workflow_engine_manager import WorkflowEngineManager
 from extensions.ext_database import db
-from models.model import App, Conversation, Message
+from models.model import App, Conversation, EndUser, Message
 from models.workflow import Workflow
 
 logger = logging.getLogger(__name__)
@@ -56,6 +56,14 @@ class AdvancedChatAppRunner(AppRunner):
         query = application_generate_entity.query
         files = application_generate_entity.files
 
+        user_id = None
+        if application_generate_entity.invoke_from in [InvokeFrom.WEB_APP, InvokeFrom.SERVICE_API]:
+            end_user = db.session.query(EndUser).filter(EndUser.id == application_generate_entity.user_id).first()
+            if end_user:
+                user_id = end_user.session_id
+        else:
+            user_id = application_generate_entity.user_id
+
         # moderation
         if self.handle_input_moderation(
                 queue_manager=queue_manager,
@@ -98,7 +106,8 @@ class AdvancedChatAppRunner(AppRunner):
             system_inputs={
                 SystemVariable.QUERY: query,
                 SystemVariable.FILES: files,
-                SystemVariable.CONVERSATION: conversation.id,
+                SystemVariable.CONVERSATION_ID: conversation.id,
+                SystemVariable.USER_ID: user_id
             },
             callbacks=workflow_callbacks
         )

+ 7 - 1
api/core/app/apps/advanced_chat/generate_task_pipeline.py

@@ -84,13 +84,19 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc
         """
         super().__init__(application_generate_entity, queue_manager, user, stream)
 
+        if isinstance(self._user, EndUser):
+            user_id = self._user.session_id
+        else:
+            user_id = self._user.id
+
         self._workflow = workflow
         self._conversation = conversation
         self._message = message
         self._workflow_system_variables = {
             SystemVariable.QUERY: message.query,
             SystemVariable.FILES: application_generate_entity.files,
-            SystemVariable.CONVERSATION: conversation.id,
+            SystemVariable.CONVERSATION_ID: conversation.id,
+            SystemVariable.USER_ID: user_id
         }
 
         self._task_state = AdvancedChatTaskState(

+ 11 - 2
api/core/app/apps/workflow/app_runner.py

@@ -14,7 +14,7 @@ from core.workflow.entities.node_entities import SystemVariable
 from core.workflow.nodes.base_node import UserFrom
 from core.workflow.workflow_engine_manager import WorkflowEngineManager
 from extensions.ext_database import db
-from models.model import App
+from models.model import App, EndUser
 from models.workflow import Workflow
 
 logger = logging.getLogger(__name__)
@@ -36,6 +36,14 @@ class WorkflowAppRunner:
         app_config = application_generate_entity.app_config
         app_config = cast(WorkflowAppConfig, app_config)
 
+        user_id = None
+        if application_generate_entity.invoke_from in [InvokeFrom.WEB_APP, InvokeFrom.SERVICE_API]:
+            end_user = db.session.query(EndUser).filter(EndUser.id == application_generate_entity.user_id).first()
+            if end_user:
+                user_id = end_user.session_id
+        else:
+            user_id = application_generate_entity.user_id
+
         app_record = db.session.query(App).filter(App.id == app_config.app_id).first()
         if not app_record:
             raise ValueError("App not found")
@@ -67,7 +75,8 @@ class WorkflowAppRunner:
             else UserFrom.END_USER,
             user_inputs=inputs,
             system_inputs={
-                SystemVariable.FILES: files
+                SystemVariable.FILES: files,
+                SystemVariable.USER_ID: user_id
             },
             callbacks=workflow_callbacks
         )

+ 6 - 0
api/core/app/apps/workflow/generate_task_pipeline.py

@@ -71,9 +71,15 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa
         """
         super().__init__(application_generate_entity, queue_manager, user, stream)
 
+        if isinstance(self._user, EndUser):
+            user_id = self._user.session_id
+        else:
+            user_id = self._user.id
+
         self._workflow = workflow
         self._workflow_system_variables = {
             SystemVariable.FILES: application_generate_entity.files,
+            SystemVariable.USER_ID: user_id
         }
 
         self._task_state = WorkflowTaskState()

+ 2 - 1
api/core/workflow/entities/node_entities.py

@@ -43,7 +43,8 @@ class SystemVariable(Enum):
     """
     QUERY = 'query'
     FILES = 'files'
-    CONVERSATION = 'conversation'
+    CONVERSATION_ID = 'conversation_id'
+    USER_ID = 'user_id'
 
     @classmethod
     def value_of(cls, value: str) -> 'SystemVariable':

+ 4 - 1
api/core/workflow/nodes/llm/llm_node.py

@@ -385,7 +385,7 @@ class LLMNode(BaseNode):
             return None
 
         # get conversation id
-        conversation_id = variable_pool.get_variable_value(['sys', SystemVariable.CONVERSATION.value])
+        conversation_id = variable_pool.get_variable_value(['sys', SystemVariable.CONVERSATION_ID.value])
         if conversation_id is None:
             return None
 
@@ -545,6 +545,9 @@ class LLMNode(BaseNode):
         if node_data.vision.enabled:
             variable_mapping['#files#'] = ['sys', SystemVariable.FILES.value]
 
+        if node_data.memory:
+            variable_mapping['#sys.query#'] = ['sys', SystemVariable.QUERY.value]
+
         return variable_mapping
 
     @classmethod

+ 1 - 4
api/core/workflow/nodes/start/start_node.py

@@ -1,6 +1,6 @@
 
 from core.workflow.entities.base_node_data_entities import BaseNodeData
-from core.workflow.entities.node_entities import NodeRunResult, NodeType, SystemVariable
+from core.workflow.entities.node_entities import NodeRunResult, NodeType
 from core.workflow.entities.variable_pool import VariablePool
 from core.workflow.nodes.base_node import BaseNode
 from core.workflow.nodes.start.entities import StartNodeData
@@ -21,9 +21,6 @@ class StartNode(BaseNode):
         cleaned_inputs = variable_pool.user_inputs
 
         for var in variable_pool.system_variables:
-            if var == SystemVariable.CONVERSATION:
-                continue
-
             cleaned_inputs['sys.' + var.value] = variable_pool.system_variables[var]
 
         return NodeRunResult(

+ 2 - 1
api/tests/integration_tests/workflow/nodes/test_llm.py

@@ -65,7 +65,8 @@ def test_execute_llm(setup_openai_mock):
     pool = VariablePool(system_variables={
         SystemVariable.QUERY: 'what\'s the weather today?',
         SystemVariable.FILES: [],
-        SystemVariable.CONVERSATION: 'abababa'
+        SystemVariable.CONVERSATION_ID: 'abababa',
+        SystemVariable.USER_ID: 'aaa'
     }, user_inputs={})
     pool.append_variable(node_id='abc', variable_key_list=['output'], value='sunny')
 

+ 2 - 2
api/tests/unit_tests/core/prompt/test_simple_prompt_transform.py

@@ -238,8 +238,8 @@ def test__get_completion_model_prompt_messages():
     prompt_rules = prompt_template['prompt_rules']
     full_inputs = {**inputs, '#context#': context, '#query#': query, '#histories#': memory.get_history_prompt_text(
         max_token_limit=2000,
-        ai_prefix=prompt_rules['human_prefix'] if 'human_prefix' in prompt_rules else 'Human',
-        human_prefix=prompt_rules['assistant_prefix'] if 'assistant_prefix' in prompt_rules else 'Assistant'
+        human_prefix=prompt_rules['human_prefix'] if 'human_prefix' in prompt_rules else 'Human',
+        ai_prefix=prompt_rules['assistant_prefix'] if 'assistant_prefix' in prompt_rules else 'Assistant'
     )}
     real_prompt = prompt_template['prompt_template'].format(full_inputs)
 

+ 1 - 0
api/tests/unit_tests/core/workflow/nodes/test_answer.py

@@ -28,6 +28,7 @@ def test_execute_answer():
     # construct variable pool
     pool = VariablePool(system_variables={
         SystemVariable.FILES: [],
+        SystemVariable.USER_ID: 'aaa'
     }, user_inputs={})
     pool.append_variable(node_id='start', variable_key_list=['weather'], value='sunny')
     pool.append_variable(node_id='llm', variable_key_list=['text'], value='You are a helpful AI.')

+ 2 - 0
api/tests/unit_tests/core/workflow/nodes/test_if_else.py

@@ -118,6 +118,7 @@ def test_execute_if_else_result_true():
     # construct variable pool
     pool = VariablePool(system_variables={
         SystemVariable.FILES: [],
+        SystemVariable.USER_ID: 'aaa'
     }, user_inputs={})
     pool.append_variable(node_id='start', variable_key_list=['array_contains'], value=['ab', 'def'])
     pool.append_variable(node_id='start', variable_key_list=['array_not_contains'], value=['ac', 'def'])
@@ -179,6 +180,7 @@ def test_execute_if_else_result_false():
     # construct variable pool
     pool = VariablePool(system_variables={
         SystemVariable.FILES: [],
+        SystemVariable.USER_ID: 'aaa'
     }, user_inputs={})
     pool.append_variable(node_id='start', variable_key_list=['array_contains'], value=['1ab', 'def'])
     pool.append_variable(node_id='start', variable_key_list=['array_not_contains'], value=['ab', 'def'])

+ 2 - 2
api/tests/unit_tests/services/workflow/test_workflow_converter.py

@@ -89,7 +89,7 @@ def test__convert_to_http_request_node_for_chatbot(default_variables):
         )
     ]
 
-    nodes = workflow_converter._convert_to_http_request_node(
+    nodes, _ = workflow_converter._convert_to_http_request_node(
         app_model=app_model,
         variables=default_variables,
         external_data_variables=external_data_variables
@@ -159,7 +159,7 @@ def test__convert_to_http_request_node_for_workflow_app(default_variables):
         )
     ]
 
-    nodes = workflow_converter._convert_to_http_request_node(
+    nodes, _ = workflow_converter._convert_to_http_request_node(
         app_model=app_model,
         variables=default_variables,
         external_data_variables=external_data_variables

+ 8 - 0
web/app/components/workflow/nodes/_base/components/variable/utils.ts

@@ -80,7 +80,15 @@ const formatItem = (item: any, isChatMode: boolean, filterVar: (payload: Var, se
           variable: 'sys.query',
           type: VarType.string,
         })
+        res.vars.push({
+          variable: 'sys.conversation_id',
+          type: VarType.string,
+        })
       }
+      res.vars.push({
+        variable: 'sys.user_id',
+        type: VarType.string,
+      })
       res.vars.push({
         variable: 'sys.files',
         type: VarType.arrayFile,

+ 27 - 0
web/app/components/workflow/nodes/start/panel.tsx

@@ -70,6 +70,7 @@ const Panel: FC<NodePanelProps<StartNodeType>> = ({
                     }
                   />)
               }
+
               <VarItem
                 readonly
                 payload={{
@@ -81,6 +82,32 @@ const Panel: FC<NodePanelProps<StartNodeType>> = ({
                   </div>
                 }
               />
+              {
+                isChatMode && (
+                  <VarItem
+                    readonly
+                    payload={{
+                      variable: 'sys.conversation_id',
+                    } as any}
+                    rightContent={
+                      <div className='text-xs font-normal text-gray-500'>
+                        String
+                      </div>
+                    }
+                  />
+                )
+              }
+              <VarItem
+                readonly
+                payload={{
+                  variable: 'sys.user_id',
+                } as any}
+                rightContent={
+                  <div className='text-xs font-normal text-gray-500'>
+                    String
+                  </div>
+                }
+              />
             </div>
 
           </>