|  | @@ -9,6 +9,7 @@ from sqlalchemy.orm import Session
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom, WorkflowAppGenerateEntity
 | 
	
		
			
				|  |  |  from core.app.entities.queue_entities import (
 | 
	
		
			
				|  |  | +    QueueAgentLogEvent,
 | 
	
		
			
				|  |  |      QueueIterationCompletedEvent,
 | 
	
		
			
				|  |  |      QueueIterationNextEvent,
 | 
	
		
			
				|  |  |      QueueIterationStartEvent,
 | 
	
	
		
			
				|  | @@ -21,6 +22,7 @@ from core.app.entities.queue_entities import (
 | 
	
		
			
				|  |  |      QueueParallelBranchRunSucceededEvent,
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  from core.app.entities.task_entities import (
 | 
	
		
			
				|  |  | +    AgentLogStreamResponse,
 | 
	
		
			
				|  |  |      IterationNodeCompletedStreamResponse,
 | 
	
		
			
				|  |  |      IterationNodeNextStreamResponse,
 | 
	
		
			
				|  |  |      IterationNodeStartStreamResponse,
 | 
	
	
		
			
				|  | @@ -63,6 +65,7 @@ class WorkflowCycleManage:
 | 
	
		
			
				|  |  |      _task_state: WorkflowTaskState
 | 
	
		
			
				|  |  |      _workflow_system_variables: dict[SystemVariableKey, Any]
 | 
	
		
			
				|  |  |      _wip_workflow_node_executions: dict[str, WorkflowNodeExecution]
 | 
	
		
			
				|  |  | +    _wip_workflow_agent_logs: dict[str, list[AgentLogStreamResponse.Data]]
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def _handle_workflow_run_start(self) -> WorkflowRun:
 | 
	
		
			
				|  |  |          max_sequence = (
 | 
	
	
		
			
				|  | @@ -283,9 +286,16 @@ class WorkflowCycleManage:
 | 
	
		
			
				|  |  |          inputs = WorkflowEntry.handle_special_values(event.inputs)
 | 
	
		
			
				|  |  |          process_data = WorkflowEntry.handle_special_values(event.process_data)
 | 
	
		
			
				|  |  |          outputs = WorkflowEntry.handle_special_values(event.outputs)
 | 
	
		
			
				|  |  | -        execution_metadata = (
 | 
	
		
			
				|  |  | -            json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
 | 
	
		
			
				|  |  | -        )
 | 
	
		
			
				|  |  | +        execution_metadata_dict = event.execution_metadata
 | 
	
		
			
				|  |  | +        if self._wip_workflow_agent_logs.get(event.node_execution_id):
 | 
	
		
			
				|  |  | +            if not execution_metadata_dict:
 | 
	
		
			
				|  |  | +                execution_metadata_dict = {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            execution_metadata_dict[NodeRunMetadataKey.AGENT_LOG] = self._wip_workflow_agent_logs.get(
 | 
	
		
			
				|  |  | +                event.node_execution_id, []
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        execution_metadata = json.dumps(jsonable_encoder(execution_metadata_dict)) if execution_metadata_dict else None
 | 
	
		
			
				|  |  |          finished_at = datetime.now(UTC).replace(tzinfo=None)
 | 
	
		
			
				|  |  |          elapsed_time = (finished_at - event.start_at).total_seconds()
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -332,9 +342,16 @@ class WorkflowCycleManage:
 | 
	
		
			
				|  |  |          outputs = WorkflowEntry.handle_special_values(event.outputs)
 | 
	
		
			
				|  |  |          finished_at = datetime.now(UTC).replace(tzinfo=None)
 | 
	
		
			
				|  |  |          elapsed_time = (finished_at - event.start_at).total_seconds()
 | 
	
		
			
				|  |  | -        execution_metadata = (
 | 
	
		
			
				|  |  | -            json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
 | 
	
		
			
				|  |  | -        )
 | 
	
		
			
				|  |  | +        execution_metadata_dict = event.execution_metadata
 | 
	
		
			
				|  |  | +        if self._wip_workflow_agent_logs.get(event.node_execution_id):
 | 
	
		
			
				|  |  | +            if not execution_metadata_dict:
 | 
	
		
			
				|  |  | +                execution_metadata_dict = {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            execution_metadata_dict[NodeRunMetadataKey.AGENT_LOG] = self._wip_workflow_agent_logs.get(
 | 
	
		
			
				|  |  | +                event.node_execution_id, []
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        execution_metadata = json.dumps(jsonable_encoder(execution_metadata_dict)) if execution_metadata_dict else None
 | 
	
		
			
				|  |  |          db.session.query(WorkflowNodeExecution).filter(WorkflowNodeExecution.id == workflow_node_execution.id).update(
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  WorkflowNodeExecution.status: WorkflowNodeExecutionStatus.FAILED.value,
 | 
	
	
		
			
				|  | @@ -746,3 +763,52 @@ class WorkflowCycleManage:
 | 
	
		
			
				|  |  |              raise Exception(f"Workflow node execution not found: {node_execution_id}")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          return workflow_node_execution
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def _handle_agent_log(self, task_id: str, event: QueueAgentLogEvent) -> AgentLogStreamResponse:
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        Handle agent log
 | 
	
		
			
				|  |  | +        :param task_id: task id
 | 
	
		
			
				|  |  | +        :param event: agent log event
 | 
	
		
			
				|  |  | +        :return:
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        node_execution = self._wip_workflow_node_executions.get(event.node_execution_id)
 | 
	
		
			
				|  |  | +        if not node_execution:
 | 
	
		
			
				|  |  | +            raise Exception(f"Workflow node execution not found: {event.node_execution_id}")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        node_execution_id = node_execution.id
 | 
	
		
			
				|  |  | +        original_agent_logs = self._wip_workflow_agent_logs.get(node_execution_id, [])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        # try to find the log with the same id
 | 
	
		
			
				|  |  | +        for log in original_agent_logs:
 | 
	
		
			
				|  |  | +            if log.id == event.id:
 | 
	
		
			
				|  |  | +                # update the log
 | 
	
		
			
				|  |  | +                log.status = event.status
 | 
	
		
			
				|  |  | +                log.error = event.error
 | 
	
		
			
				|  |  | +                log.data = event.data
 | 
	
		
			
				|  |  | +                break
 | 
	
		
			
				|  |  | +        else:
 | 
	
		
			
				|  |  | +            # append the log
 | 
	
		
			
				|  |  | +            original_agent_logs.append(
 | 
	
		
			
				|  |  | +                AgentLogStreamResponse.Data(
 | 
	
		
			
				|  |  | +                    id=event.id,
 | 
	
		
			
				|  |  | +                    parent_id=event.parent_id,
 | 
	
		
			
				|  |  | +                    node_execution_id=node_execution_id,
 | 
	
		
			
				|  |  | +                    error=event.error,
 | 
	
		
			
				|  |  | +                    status=event.status,
 | 
	
		
			
				|  |  | +                    data=event.data,
 | 
	
		
			
				|  |  | +                )
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        self._wip_workflow_agent_logs[node_execution_id] = original_agent_logs
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return AgentLogStreamResponse(
 | 
	
		
			
				|  |  | +            task_id=task_id,
 | 
	
		
			
				|  |  | +            data=AgentLogStreamResponse.Data(
 | 
	
		
			
				|  |  | +                node_execution_id=node_execution_id,
 | 
	
		
			
				|  |  | +                id=event.id,
 | 
	
		
			
				|  |  | +                parent_id=event.parent_id,
 | 
	
		
			
				|  |  | +                error=event.error,
 | 
	
		
			
				|  |  | +                status=event.status,
 | 
	
		
			
				|  |  | +                data=event.data,
 | 
	
		
			
				|  |  | +            ),
 | 
	
		
			
				|  |  | +        )
 |