fix: improve maintenance and synchronize live conversations
This commit is contained in:
1 parent
bb541bd212
commit
ef86a5d3af
297 files changed
+2983
-1274
No files matched your search
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user