fix: improve maintenance and synchronize live conversations

This commit is contained in:
WangShen007 committed 2026-10-29 09:46:21 +08:00
1 parent bb541bd212
commit ef86a5d3af
297 files changed
+2983 -1274

No files matched your search

+21 -44
View File
@@ -14,24 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
import traceback
import asyncio
from typing import Optional, Dict, Any, List, Tuple
from typing import Optional, Dict, Any, List
from uuid import UUID
from sqlalchemy.orm import Session
from dataclasses import dataclass
import json
import re
from datetime import datetime, timedelta
from app.models.workflow import Workflow, WorkflowStatus
from app.models.workflow_node import WorkflowNode, NodeType, ExitCondition
from app.models.workflow_connection import WorkflowConnection
from app.models.session_to_agent import SessionToAgent
from app.repositories.workflow import WorkflowRepository
from app.repositories.session_to_agent import SessionToAgentRepository
from app.repositories.chat import ChatRepository
from app.agents.chat_agent import ChatAgent, ChatResponse
from app.agents.chat_agent import ChatAgent
from app.core.logger import get_logger
from app.services.workflow_conditions import (
MISSING,
@@ -114,7 +109,6 @@ class WorkflowExecutionService:
try:
logger.info(f"Executing workflow {workflow_id} for session {session_id}")
logger.debug(f"Current node ID: {current_node_id}")
logger.debug(f"Workflow state: {workflow_state}")
logger.debug(f"User message: {user_message}")
logger.debug(f"Is initial execution: {is_initial_execution}")
@@ -200,7 +194,7 @@ class WorkflowExecutionService:
# Collect messages from MESSAGE nodes during automatic execution
if current_node.node_type == NodeType.MESSAGE and result.message:
intermediate_messages.append(result.message)
logger.debug(f"Collected intermediate message from MESSAGE node: {result.message}")
logger.debug(f"Collected intermediate message from MESSAGE node {current_node.id}")
# Also collect messages from LLM nodes that are not the final stopping node
# This handles the case where LLM node with single execution produces a response
@@ -208,21 +202,21 @@ class WorkflowExecutionService:
if (current_node.node_type == NodeType.LLM and result.message and
result.should_continue and result.next_node_id is not None):
intermediate_messages.append(result.message)
logger.debug(f"Collected intermediate message from LLM node: {result.message}")
logger.debug(f"Collected intermediate message from LLM node {current_node.id}")
# Also collect confirmation messages from USER_INPUT nodes that continue to next node
# This ensures confirmation messages are displayed to the user
if (current_node.node_type == NodeType.USER_INPUT and result.message and
result.should_continue and result.next_node_id is not None):
intermediate_messages.append(result.message)
logger.debug(f"Collected confirmation message from USER_INPUT node: {result.message}")
logger.debug(f"Collected confirmation message from USER_INPUT node {current_node.id}")
final_result = result
final_message_collected = len(intermediate_messages) > collected_before
# Check if we should continue to next node automatically
if not result.success:
logger.error(f"Node execution failed: {result.error}")
logger.error(f"Node execution failed for session {session_id} at node {current_node.id}")
break
# Check stopping conditions
@@ -318,8 +312,7 @@ class WorkflowExecutionService:
)
except Exception as e:
traceback.print_exc()
logger.error(f"Error executing workflow: {str(e)}")
logger.error(f"Error executing workflow {workflow_id} for session {session_id}: {type(e).__name__}")
return WorkflowExecutionResult(
success=False,
message="An error occurred while executing the workflow",
@@ -359,15 +352,14 @@ class WorkflowExecutionService:
"""
try:
logger.info(f"Submitting form for session {session_id}")
logger.debug(f"Form data: {form_data}")
logger.debug(f"Workflow ID: {workflow_id}")
logger.debug(f"Org ID: {org_id}")
logger.debug(f"Agent ID: {agent_id}")
logger.debug(f"Customer ID: {customer_id}")
logger.debug(f"API key: {api_key}")
# Get current session state
session = self.session_repo.get_session(session_id)
if not session:
logger.warning(f"Form submission failed for session {session_id}: session not found")
return WorkflowExecutionResult(
success=False,
message="Session not found",
@@ -379,6 +371,7 @@ class WorkflowExecutionService:
# Validate that we're in a form waiting state
if workflow_state.get("form_state") != "waiting":
logger.warning(f"No form submission expected for session {session_id} at node {current_node_id}")
return WorkflowExecutionResult(
success=False,
message="No form submission expected",
@@ -389,9 +382,7 @@ class WorkflowExecutionService:
workflow_state["form_data"] = form_data
workflow_state["form_state"] = "submitted"
logger.debug(f"Form submission received for session {session_id}")
logger.debug(f"Form data: {form_data}")
logger.debug(f"Updated workflow state: {workflow_state}")
logger.debug(f"Form submission received for session {session_id} at node {current_node_id}")
# Continue workflow execution with form submission
return await self.execute_workflow(
@@ -411,7 +402,7 @@ class WorkflowExecutionService:
)
except Exception as e:
logger.error(f"Error submitting form: {str(e)}")
logger.error(f"Error submitting form for session {session_id}: {type(e).__name__}")
return WorkflowExecutionResult(
success=False,
message="An error occurred while submitting the form",
@@ -500,7 +491,7 @@ class WorkflowExecutionService:
)
except Exception as e:
logger.error(f"Error executing node {node.id}: {str(e)}")
logger.error(f"Error executing node {node.id} for session {session_id}: {type(e).__name__}")
return WorkflowExecutionResult(
success=False,
message=f"Error executing node: {str(e)}",
@@ -598,11 +589,9 @@ class WorkflowExecutionService:
# Transfer to human setting only applies to continuous execution
auto_transfer = False
transfer_group_id = None
ask_for_rating_config = False
if exit_condition == ExitCondition.CONTINUOUS_EXECUTION:
auto_transfer = config.get("auto_transfer_enabled", False)
transfer_group_id = config.get("transfer_group_id")
ask_for_rating_config = config.get("ask_for_rating", True) # Default to True
# Handle empty or null user message by creating structured context
processed_user_message = user_message
@@ -636,7 +625,7 @@ class WorkflowExecutionService:
finally:
await chat_agent.safe_cleanup_mcp_tools()
logger.debug(f"Response: {response}")
logger.debug(f"LLM node {node.id} returned a response")
# Handle exit conditions
next_node_id = None
should_continue = False
@@ -693,7 +682,7 @@ class WorkflowExecutionService:
)
except Exception as e:
logger.error(f"Error executing LLM node: {str(e)}")
logger.error(f"Error executing LLM node {node.id}: {type(e).__name__}")
return WorkflowExecutionResult(
success=False,
message="Sorry, I encountered an error processing your request.",
@@ -730,7 +719,7 @@ class WorkflowExecutionService:
)
except Exception as e:
logger.error(f"Error executing condition node: {str(e)}")
logger.error(f"Error executing condition node {node.id}: {type(e).__name__}")
return WorkflowExecutionResult(
success=False,
message="Error evaluating condition",
@@ -770,7 +759,6 @@ class WorkflowExecutionService:
"fields": form_fields,
"form_full_screen": config.get("form_full_screen", False)
}
logger.debug(f"Form data: {form_data}")
# Mark that we're waiting for form submission
workflow_state["form_state"] = "waiting"
@@ -781,12 +769,10 @@ class WorkflowExecutionService:
should_continue=False, # Wait for form submission
form_data=form_data # Include form data for display
)
logger.debug(f"Form result: {result}")
return result
elif current_state == "submitted":
# Form has been submitted, process and continue
form_submission = workflow_state.get("form_data", {})
logger.debug(f"Form submission: {form_submission}")
# Store form variables for use in other nodes with node-based naming
node_based_variables = {}
@@ -874,7 +860,6 @@ class WorkflowExecutionService:
should_continue=False, # Wait for user to proceed
landing_page_data=landing_page_data # Include landing page data for display
)
logger.debug(f"Landing page result: {result}")
return result
def _execute_action_node(self, node: WorkflowNode, workflow_state: Dict[str, Any]) -> WorkflowExecutionResult:
@@ -884,7 +869,6 @@ class WorkflowExecutionService:
config = node.config or {}
action_type = config.get("action_type")
action_config = config.get("action_config", {})
logger.info(f"Executing action: {action_type}")
@@ -992,7 +976,7 @@ class WorkflowExecutionService:
text_to_check = user_message
elif "variables" in workflow_state and "user_message" in workflow_state["variables"]:
text_to_check = workflow_state["variables"]["user_message"]
logger.debug(f"Using user_message from workflow state: {text_to_check}")
logger.debug(f"Using user message from workflow state for guardrails node {node.id}")
else:
text_to_check = ""
else:
@@ -1031,7 +1015,7 @@ class WorkflowExecutionService:
"checked_text": text_to_check[:100] + "..." if len(text_to_check) > 100 else text_to_check
}
logger.info(f"Guardrails node {node.id} - Passed: {passed}, Results: {results}")
logger.info(f"Guardrails node {node.id} - Passed: {passed}, checks: {len(results)}")
# Find connections - guardrails nodes can have two outputs: "pass" and "fail"
pass_node_id = None
@@ -1262,7 +1246,6 @@ class WorkflowExecutionService:
"""Update session with new workflow state"""
try:
logger.info(f"Updating session {session_id} with next_node_id: {next_node_id}")
logger.info(f"Updating session {session_id} with workflow_state: {workflow_state}")
success = self.session_repo.update_workflow_state(session_id, next_node_id, workflow_state)
if success:
@@ -1271,7 +1254,7 @@ class WorkflowExecutionService:
logger.error(f"Failed to update session {session_id} workflow state")
except Exception as e:
logger.error(f"Error updating session workflow state: {str(e)}")
logger.error(f"Error updating workflow state for session {session_id}: {type(e).__name__}")
async def _build_context_message(self, session_id: str, workflow_state: Dict[str, Any]) -> str:
"""
@@ -1383,11 +1366,10 @@ class WorkflowExecutionService:
# Store variables namespaced by node ID
workflow_state["variables"][str(node_id)] = form_data.copy()
logger.info(f"Stored form variables from node {node_id}: {list(form_data.keys())}")
logger.debug(f"Variable values: {form_data}")
logger.info(f"Stored {len(form_data)} form variables from node {node_id}")
except Exception as e:
logger.error(f"Error storing form variables: {str(e)}")
logger.error(f"Error storing form variables from node {node_id}: {type(e).__name__}")
def _interpolate_variables(self, text: str, workflow_state: Dict[str, Any]) -> str:
"""
@@ -1427,13 +1409,8 @@ class WorkflowExecutionService:
# Replace all variable placeholders
result = re.sub(pattern, replace_variable, text)
if result != text:
logger.debug(f"Variable interpolation: '{text}' -> '{result}'")
return result
except Exception as e:
logger.error(f"Error interpolating variables: {str(e)}")
logger.error(f"Error interpolating workflow variables: {type(e).__name__}")
return text # Return original text on error