issues fixing in chat workflow
This commit is contained in:
parent
46fcd089c4
commit
6060d74eb2
9 changed files with 111 additions and 97 deletions
|
|
@ -8,30 +8,29 @@ from modules.services import getInterface as getServices
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
async def chatStart(interfaceDbChat, currentUser: User, userInput: UserInputRequest, workflowId: Optional[str] = None, workflowMode: WorkflowModeEnum = WorkflowModeEnum.WORKFLOW_ACTIONPLAN) -> ChatWorkflow:
|
async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode: WorkflowModeEnum, workflowId: Optional[str] = None) -> ChatWorkflow:
|
||||||
"""
|
"""
|
||||||
Starts a new chat or continues an existing one, then launches processing asynchronously.
|
Starts a new chat or continues an existing one, then launches processing asynchronously.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
interfaceDbChat: Chat interface instance
|
|
||||||
currentUser: Current user
|
currentUser: Current user
|
||||||
userInput: User input request
|
userInput: User input request
|
||||||
workflowId: Optional workflow ID to continue existing workflow
|
workflowId: Optional workflow ID to continue existing workflow
|
||||||
workflowMode: "Actionplan" for traditional task planning, "Dynamic" for iterative dynamic-style processing, "Template" for template-based processing
|
workflowMode: "Actionplan" for traditional task planning, "Dynamic" for iterative dynamic-style processing, "Template" for template-based processing
|
||||||
|
|
||||||
Example usage for Dynamic mode:
|
Example usage for Dynamic mode:
|
||||||
workflow = await chatStart(interfaceDbChat, currentUser, userInput, workflowMode=WorkflowModeEnum.WORKFLOW_DYNAMIC)
|
workflow = await chatStart(currentUser, userInput, workflowMode=WorkflowModeEnum.WORKFLOW_DYNAMIC)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
services = getServices(currentUser, None)
|
services = getServices(currentUser, None)
|
||||||
workflowManager = WorkflowManager(services)
|
workflowManager = WorkflowManager(services)
|
||||||
workflow = await workflowManager.workflowStart(userInput, workflowId, workflowMode)
|
workflow = await workflowManager.workflowStart(userInput, workflowMode, workflowId)
|
||||||
return workflow
|
return workflow
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error starting chat: {str(e)}")
|
logger.error(f"Error starting chat: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def chatStop(interfaceDbChat, currentUser: User, workflowId: str) -> ChatWorkflow:
|
async def chatStop(currentUser: User, workflowId: str) -> ChatWorkflow:
|
||||||
"""Stops a running chat."""
|
"""Stops a running chat."""
|
||||||
try:
|
try:
|
||||||
services = getServices(currentUser, None)
|
services = getServices(currentUser, None)
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ class ChatObjects:
|
||||||
messages=[],
|
messages=[],
|
||||||
stats=[],
|
stats=[],
|
||||||
mandateId=created.get("mandateId", self.currentUser.mandateId),
|
mandateId=created.get("mandateId", self.currentUser.mandateId),
|
||||||
workflowMode=created.get("workflowMode", WorkflowModeEnum.WORKFLOW_DYNAMIC),
|
workflowMode=created["workflowMode"],
|
||||||
maxSteps=created.get("maxSteps", 1)
|
maxSteps=created.get("maxSteps", 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ def getServiceChat(currentUser: User):
|
||||||
async def start_workflow(
|
async def start_workflow(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: Optional[str] = Query(None, description="Optional ID of the workflow to continue"),
|
workflowId: Optional[str] = Query(None, description="Optional ID of the workflow to continue"),
|
||||||
workflowMode: WorkflowModeEnum = Query(WorkflowModeEnum.WORKFLOW_DYNAMIC, description="Workflow mode: 'Actionplan', 'Dynamic', or 'Template'"),
|
workflowMode: WorkflowModeEnum = Query(..., description="Workflow mode: 'Actionplan', 'Dynamic', or 'Template' (mandatory)"),
|
||||||
userInput: UserInputRequest = Body(...),
|
userInput: UserInputRequest = Body(...),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> ChatWorkflow:
|
) -> ChatWorkflow:
|
||||||
|
|
@ -51,11 +51,8 @@ async def start_workflow(
|
||||||
workflowMode: "Actionplan" for traditional task planning, "Dynamic" for iterative dynamic-style processing, "Template" for template-based processing
|
workflowMode: "Actionplan" for traditional task planning, "Dynamic" for iterative dynamic-style processing, "Template" for template-based processing
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Get service center
|
|
||||||
interfaceDbChat = getServiceChat(currentUser)
|
|
||||||
|
|
||||||
# Start or continue workflow using playground controller
|
# Start or continue workflow using playground controller
|
||||||
workflow = await chatStart(interfaceDbChat, currentUser, userInput, workflowId, workflowMode)
|
workflow = await chatStart(currentUser, userInput, workflowMode, workflowId)
|
||||||
|
|
||||||
return workflow
|
return workflow
|
||||||
|
|
||||||
|
|
@ -76,11 +73,8 @@ async def stop_workflow(
|
||||||
) -> ChatWorkflow:
|
) -> ChatWorkflow:
|
||||||
"""Stops a running workflow."""
|
"""Stops a running workflow."""
|
||||||
try:
|
try:
|
||||||
# Get service center
|
|
||||||
interfaceDbChat = getServiceChat(currentUser)
|
|
||||||
|
|
||||||
# Stop workflow using playground controller
|
# Stop workflow using playground controller
|
||||||
workflow = await chatStop(interfaceDbChat, currentUser, workflowId)
|
workflow = await chatStop(currentUser, workflowId)
|
||||||
|
|
||||||
return workflow
|
return workflow
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,10 @@ async def login(
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
logger.warning(f"Authentication failed for user {formData.username}: {error_msg}")
|
logger.warning(f"Authentication failed for user {formData.username}: {error_msg}")
|
||||||
|
|
||||||
|
# Check if user is disabled and provide specific message
|
||||||
|
if error_msg == "User is disabled":
|
||||||
|
error_msg = "Your account is disabled. Please send an email to p.motsch@valueon.ch to get access to the PowerOn center."
|
||||||
|
|
||||||
# Log failed login attempt
|
# Log failed login attempt
|
||||||
try:
|
try:
|
||||||
from modules.shared.auditLogger import audit_logger
|
from modules.shared.auditLogger import audit_logger
|
||||||
|
|
@ -209,6 +213,7 @@ async def register_user(
|
||||||
|
|
||||||
# Create user with local authentication
|
# Create user with local authentication
|
||||||
# Set safe default privilege level for new registrations
|
# Set safe default privilege level for new registrations
|
||||||
|
# New users are disabled by default and require admin approval
|
||||||
from modules.datamodels.datamodelUam import UserPrivilege
|
from modules.datamodels.datamodelUam import UserPrivilege
|
||||||
user = appInterface.createUser(
|
user = appInterface.createUser(
|
||||||
username=userData.username,
|
username=userData.username,
|
||||||
|
|
@ -216,7 +221,7 @@ async def register_user(
|
||||||
email=userData.email,
|
email=userData.email,
|
||||||
fullName=userData.fullName,
|
fullName=userData.fullName,
|
||||||
language=userData.language,
|
language=userData.language,
|
||||||
enabled=userData.enabled,
|
enabled=False, # New users are disabled by default
|
||||||
privilege=UserPrivilege.USER, # Always set to USER for new registrations
|
privilege=UserPrivilege.USER, # Always set to USER for new registrations
|
||||||
authenticationAuthority=AuthAuthority.LOCAL
|
authenticationAuthority=AuthAuthority.LOCAL
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ class WorkflowService:
|
||||||
logger.error(f"Error getting workflow context: {str(e)}")
|
logger.error(f"Error getting workflow context: {str(e)}")
|
||||||
return {'currentRound': 0, 'currentTask': 0, 'currentAction': 0}
|
return {'currentRound': 0, 'currentTask': 0, 'currentAction': 0}
|
||||||
|
|
||||||
def setWorkflowContext(self, round_number: int = None, task_number: int = None, action_number: int = None):
|
def setWorkflowContext(self, roundNumber: int = None, taskNumber: int = None, actionNumber: int = None):
|
||||||
"""Set current workflow context for document generation and routing"""
|
"""Set current workflow context for document generation and routing"""
|
||||||
try:
|
try:
|
||||||
workflow = self.services.currentWorkflow
|
workflow = self.services.currentWorkflow
|
||||||
|
|
@ -383,15 +383,15 @@ class WorkflowService:
|
||||||
# Prepare update data
|
# Prepare update data
|
||||||
update_data = {}
|
update_data = {}
|
||||||
|
|
||||||
if round_number is not None:
|
if roundNumber is not None:
|
||||||
workflow.currentRound = round_number
|
workflow.currentRound = roundNumber
|
||||||
update_data["currentRound"] = round_number
|
update_data["currentRound"] = roundNumber
|
||||||
if task_number is not None:
|
if taskNumber is not None:
|
||||||
workflow.currentTask = task_number
|
workflow.currentTask = taskNumber
|
||||||
update_data["currentTask"] = task_number
|
update_data["currentTask"] = taskNumber
|
||||||
if action_number is not None:
|
if actionNumber is not None:
|
||||||
workflow.currentAction = action_number
|
workflow.currentAction = actionNumber
|
||||||
update_data["currentAction"] = action_number
|
update_data["currentAction"] = actionNumber
|
||||||
|
|
||||||
# Persist changes to database if any updates were made
|
# Persist changes to database if any updates were made
|
||||||
if update_data:
|
if update_data:
|
||||||
|
|
@ -863,8 +863,8 @@ class WorkflowService:
|
||||||
logger.error(f"Error getting connection reference list: {str(e)}")
|
logger.error(f"Error getting connection reference list: {str(e)}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def setWorkflowContext(self, workflow):
|
def setCurrentWorkflow(self, workflow):
|
||||||
"""Set the current workflow context for this service"""
|
"""Set the current workflow reference for this service"""
|
||||||
self.workflow = workflow
|
self.workflow = workflow
|
||||||
# Reset progress logger for new workflow
|
# Reset progress logger for new workflow
|
||||||
self._progressLogger = None
|
self._progressLogger = None
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,23 @@ def getModelAttributeDefinitions(modelClass: Type[BaseModel] = None, userLanguag
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Extract default value from field
|
||||||
|
# In Pydantic v2, FieldInfo has a 'default' attribute
|
||||||
|
field_default = None
|
||||||
|
if hasattr(field_info, 'default'):
|
||||||
|
default_value = field_info.default
|
||||||
|
# Handle default_factory (callable that generates default)
|
||||||
|
if hasattr(field_info, 'default_factory') and callable(field_info.default_factory):
|
||||||
|
# Don't call it here - it's meant to be called per-instance
|
||||||
|
# Instead, store a marker that indicates it exists
|
||||||
|
field_default = None # Frontend should use first option or specific logic
|
||||||
|
elif default_value is not ...: # Ellipsis means no default
|
||||||
|
# Convert enum to its value if it's an enum
|
||||||
|
if hasattr(default_value, 'value'):
|
||||||
|
field_default = default_value.value
|
||||||
|
else:
|
||||||
|
field_default = default_value
|
||||||
|
|
||||||
attributes.append(
|
attributes.append(
|
||||||
{
|
{
|
||||||
"name": name,
|
"name": name,
|
||||||
|
|
@ -192,6 +209,7 @@ def getModelAttributeDefinitions(modelClass: Type[BaseModel] = None, userLanguag
|
||||||
"order": len(attributes),
|
"order": len(attributes),
|
||||||
"readonly": frontend_readonly,
|
"readonly": frontend_readonly,
|
||||||
"options": frontend_options,
|
"options": frontend_options,
|
||||||
|
"default": field_default,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ class ActionplanMode(BaseMode):
|
||||||
|
|
||||||
# Update workflow context for this task
|
# Update workflow context for this task
|
||||||
if taskIndex is not None:
|
if taskIndex is not None:
|
||||||
self.services.workflow.setWorkflowContext(task_number=taskIndex)
|
self.services.workflow.setWorkflowContext(taskNumber=taskIndex)
|
||||||
|
|
||||||
# Create task start message
|
# Create task start message
|
||||||
await self.messageCreator.createTaskStartMessage(taskStep, workflow, taskIndex, totalTasks)
|
await self.messageCreator.createTaskStartMessage(taskStep, workflow, taskIndex, totalTasks)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class WorkflowProcessor:
|
||||||
def __init__(self, services, workflow=None):
|
def __init__(self, services, workflow=None):
|
||||||
self.services = services
|
self.services = services
|
||||||
self.workflow = workflow
|
self.workflow = workflow
|
||||||
self.mode = self._createMode(workflow.workflowMode if workflow else WorkflowModeEnum.WORKFLOW_DYNAMIC)
|
self.mode = self._createMode(workflow.workflowMode)
|
||||||
|
|
||||||
def _createMode(self, workflowMode: WorkflowModeEnum) -> BaseMode:
|
def _createMode(self, workflowMode: WorkflowModeEnum) -> BaseMode:
|
||||||
"""Create the appropriate mode implementation based on workflow mode"""
|
"""Create the appropriate mode implementation based on workflow mode"""
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class WorkflowManager:
|
||||||
|
|
||||||
# Exported functions
|
# Exported functions
|
||||||
|
|
||||||
async def workflowStart(self, userInput: UserInputRequest, workflowId: Optional[str] = None, workflowMode: WorkflowModeEnum = WorkflowModeEnum.WORKFLOW_DYNAMIC) -> ChatWorkflow:
|
async def workflowStart(self, userInput: UserInputRequest, workflowMode: WorkflowModeEnum, workflowId: Optional[str] = None) -> ChatWorkflow:
|
||||||
"""Starts a new workflow or continues an existing one, then launches processing."""
|
"""Starts a new workflow or continues an existing one, then launches processing."""
|
||||||
try:
|
try:
|
||||||
# Debug log to check workflowMode parameter
|
# Debug log to check workflowMode parameter
|
||||||
|
|
@ -142,7 +142,7 @@ class WorkflowManager:
|
||||||
self.services.currentUserPrompt = userInput.prompt
|
self.services.currentUserPrompt = userInput.prompt
|
||||||
|
|
||||||
# Update the workflow service with the current workflow context
|
# Update the workflow service with the current workflow context
|
||||||
self.services.workflow.setWorkflowContext(workflow)
|
self.services.workflow.setCurrentWorkflow(workflow)
|
||||||
|
|
||||||
self.workflowProcessor = WorkflowProcessor(self.services, workflow)
|
self.workflowProcessor = WorkflowProcessor(self.services, workflow)
|
||||||
await self._sendFirstMessage(userInput, workflow)
|
await self._sendFirstMessage(userInput, workflow)
|
||||||
|
|
@ -165,10 +165,8 @@ class WorkflowManager:
|
||||||
|
|
||||||
# Create initial message using interface
|
# Create initial message using interface
|
||||||
# For first user message, include round info in the user context label
|
# For first user message, include round info in the user context label
|
||||||
round_num = workflow.currentRound
|
roundNum = workflow.currentRound
|
||||||
task_num = 0
|
contextLabel = f"round{roundNum}_usercontext"
|
||||||
action_num = 0
|
|
||||||
context_label = f"round{round_num}_usercontext"
|
|
||||||
|
|
||||||
messageData = {
|
messageData = {
|
||||||
"workflowId": workflow.id,
|
"workflowId": workflow.id,
|
||||||
|
|
@ -177,7 +175,7 @@ class WorkflowManager:
|
||||||
"status": "first",
|
"status": "first",
|
||||||
"sequenceNr": 1,
|
"sequenceNr": 1,
|
||||||
"publishedAt": self.services.utils.timestampGetUtc(),
|
"publishedAt": self.services.utils.timestampGetUtc(),
|
||||||
"documentsLabel": context_label,
|
"documentsLabel": contextLabel,
|
||||||
"documents": [],
|
"documents": [],
|
||||||
# Add workflow context fields
|
# Add workflow context fields
|
||||||
"roundNumber": workflow.currentRound,
|
"roundNumber": workflow.currentRound,
|
||||||
|
|
@ -189,7 +187,7 @@ class WorkflowManager:
|
||||||
}
|
}
|
||||||
|
|
||||||
# Analyze the user's input to detect language, normalize request, extract intent, and offload bulky context into documents
|
# Analyze the user's input to detect language, normalize request, extract intent, and offload bulky context into documents
|
||||||
created_docs = []
|
createdDocs = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
analyzerPrompt = (
|
analyzerPrompt = (
|
||||||
|
|
@ -271,28 +269,28 @@ class WorkflowManager:
|
||||||
mimeType = (mime or "text/plain").strip()
|
mimeType = (mime or "text/plain").strip()
|
||||||
|
|
||||||
# Neutralize content before storing if neutralization is enabled
|
# Neutralize content before storing if neutralization is enabled
|
||||||
content_bytes = content.encode('utf-8')
|
contentBytes = content.encode('utf-8')
|
||||||
content_bytes = await self._neutralizeContentIfEnabled(content_bytes, mimeType)
|
contentBytes = await self._neutralizeContentIfEnabled(contentBytes, mimeType)
|
||||||
|
|
||||||
# Create file in component storage
|
# Create file in component storage
|
||||||
file_item = self.services.interfaceDbComponent.createFile(
|
fileItem = self.services.interfaceDbComponent.createFile(
|
||||||
name=fileName,
|
name=fileName,
|
||||||
mimeType=mimeType,
|
mimeType=mimeType,
|
||||||
content=content_bytes
|
content=contentBytes
|
||||||
)
|
)
|
||||||
# Persist file data
|
# Persist file data
|
||||||
self.services.interfaceDbComponent.createFileData(file_item.id, content_bytes)
|
self.services.interfaceDbComponent.createFileData(fileItem.id, contentBytes)
|
||||||
|
|
||||||
# Collect file info
|
# Collect file info
|
||||||
file_info = self.services.workflow.getFileInfo(file_item.id)
|
fileInfo = self.services.workflow.getFileInfo(fileItem.id)
|
||||||
from modules.datamodels.datamodelChat import ChatDocument
|
from modules.datamodels.datamodelChat import ChatDocument
|
||||||
doc = ChatDocument(
|
doc = ChatDocument(
|
||||||
fileId=file_item.id,
|
fileId=fileItem.id,
|
||||||
fileName=file_info.get("fileName", fileName) if file_info else fileName,
|
fileName=fileInfo.get("fileName", fileName) if fileInfo else fileName,
|
||||||
fileSize=file_info.get("size", len(content_bytes)) if file_info else len(content_bytes),
|
fileSize=fileInfo.get("size", len(contentBytes)) if fileInfo else len(contentBytes),
|
||||||
mimeType=file_info.get("mimeType", mimeType) if file_info else mimeType
|
mimeType=fileInfo.get("mimeType", mimeType) if fileInfo else mimeType
|
||||||
)
|
)
|
||||||
created_docs.append(doc)
|
createdDocs.append(doc)
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -301,14 +299,14 @@ class WorkflowManager:
|
||||||
# Process user-uploaded documents (fileIds) and combine with context documents
|
# Process user-uploaded documents (fileIds) and combine with context documents
|
||||||
if userInput.listFileId:
|
if userInput.listFileId:
|
||||||
try:
|
try:
|
||||||
user_docs = await self._processFileIds(userInput.listFileId, None)
|
userDocs = await self._processFileIds(userInput.listFileId, None)
|
||||||
if user_docs:
|
if userDocs:
|
||||||
created_docs.extend(user_docs)
|
createdDocs.extend(userDocs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Failed to process user fileIds: {e}")
|
logger.warning(f"Failed to process user fileIds: {e}")
|
||||||
|
|
||||||
# Finally, persist and bind the first message with combined documents (context + user)
|
# Finally, persist and bind the first message with combined documents (context + user)
|
||||||
self.services.workflow.storeMessageWithDocuments(workflow, messageData, created_docs)
|
self.services.workflow.storeMessageWithDocuments(workflow, messageData, createdDocs)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error sending first message: {str(e)}")
|
logger.error(f"Error sending first message: {str(e)}")
|
||||||
|
|
@ -318,33 +316,33 @@ class WorkflowManager:
|
||||||
"""Generate task plan for workflow execution"""
|
"""Generate task plan for workflow execution"""
|
||||||
handling = self.workflowProcessor
|
handling = self.workflowProcessor
|
||||||
# Generate task plan first (shared for both modes)
|
# Generate task plan first (shared for both modes)
|
||||||
task_plan = await handling.generateTaskPlan(userInput.prompt, workflow)
|
taskPlan = await handling.generateTaskPlan(userInput.prompt, workflow)
|
||||||
if not task_plan or not task_plan.tasks:
|
if not taskPlan or not taskPlan.tasks:
|
||||||
raise Exception("No tasks generated in task plan.")
|
raise Exception("No tasks generated in task plan.")
|
||||||
workflow_mode = getattr(workflow, 'workflowMode', WorkflowModeEnum.WORKFLOW_DYNAMIC)
|
workflowMode = getattr(workflow, 'workflowMode')
|
||||||
logger.info(f"Workflow object attributes: {workflow.__dict__ if hasattr(workflow, '__dict__') else 'No __dict__'}")
|
logger.info(f"Workflow object attributes: {workflow.__dict__ if hasattr(workflow, '__dict__') else 'No __dict__'}")
|
||||||
logger.info(f"Executing workflow mode={workflow_mode} with {len(task_plan.tasks)} tasks")
|
logger.info(f"Executing workflow mode={workflowMode} with {len(taskPlan.tasks)} tasks")
|
||||||
return task_plan
|
return taskPlan
|
||||||
|
|
||||||
async def _executeTasks(self, task_plan, workflow: ChatWorkflow) -> None:
|
async def _executeTasks(self, taskPlan, workflow: ChatWorkflow) -> None:
|
||||||
"""Execute all tasks in the task plan and update workflow status."""
|
"""Execute all tasks in the task plan and update workflow status."""
|
||||||
handling = self.workflowProcessor
|
handling = self.workflowProcessor
|
||||||
total_tasks = len(task_plan.tasks)
|
totalTasks = len(taskPlan.tasks)
|
||||||
all_task_results: List = []
|
allTaskResults: List = []
|
||||||
previous_results: List[str] = []
|
previousResults: List[str] = []
|
||||||
|
|
||||||
for idx, task_step in enumerate(task_plan.tasks):
|
for idx, taskStep in enumerate(taskPlan.tasks):
|
||||||
current_task_index = idx + 1
|
currentTaskIndex = idx + 1
|
||||||
logger.info(f"Task {current_task_index}/{total_tasks}: {task_step.objective}")
|
logger.info(f"Task {currentTaskIndex}/{totalTasks}: {taskStep.objective}")
|
||||||
|
|
||||||
# Build TaskContext (mode-specific behavior is inside WorkflowProcessor)
|
# Build TaskContext (mode-specific behavior is inside WorkflowProcessor)
|
||||||
task_context = TaskContext(
|
taskContext = TaskContext(
|
||||||
taskStep=task_step,
|
taskStep=taskStep,
|
||||||
workflow=workflow,
|
workflow=workflow,
|
||||||
workflowId=workflow.id,
|
workflowId=workflow.id,
|
||||||
availableDocuments=None,
|
availableDocuments=None,
|
||||||
availableConnections=None,
|
availableConnections=None,
|
||||||
previousResults=previous_results,
|
previousResults=previousResults,
|
||||||
previousHandover=None,
|
previousHandover=None,
|
||||||
improvements=[],
|
improvements=[],
|
||||||
retryCount=0,
|
retryCount=0,
|
||||||
|
|
@ -361,15 +359,15 @@ class WorkflowManager:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
task_result = await handling.executeTask(task_step, workflow, task_context, current_task_index, total_tasks)
|
taskResult = await handling.executeTask(taskStep, workflow, taskContext, currentTaskIndex, totalTasks)
|
||||||
handover_data = await handling.prepareTaskHandover(task_step, [], task_result, workflow)
|
handoverData = await handling.prepareTaskHandover(taskStep, [], taskResult, workflow)
|
||||||
all_task_results.append({
|
allTaskResults.append({
|
||||||
'task_step': task_step,
|
'taskStep': taskStep,
|
||||||
'task_result': task_result,
|
'taskResult': taskResult,
|
||||||
'handover_data': handover_data
|
'handoverData': handoverData
|
||||||
})
|
})
|
||||||
if task_result.success and task_result.feedback:
|
if taskResult.success and taskResult.feedback:
|
||||||
previous_results.append(task_result.feedback)
|
previousResults.append(taskResult.feedback)
|
||||||
|
|
||||||
# Mark workflow as completed; error/stop cases update status elsewhere
|
# Mark workflow as completed; error/stop cases update status elsewhere
|
||||||
workflow.status = "completed"
|
workflow.status = "completed"
|
||||||
|
|
@ -384,7 +382,7 @@ class WorkflowManager:
|
||||||
logger.info(f"Workflow {workflow.id} was stopped during result processing")
|
logger.info(f"Workflow {workflow.id} was stopped during result processing")
|
||||||
|
|
||||||
# Create final stopped message
|
# Create final stopped message
|
||||||
stopped_message = {
|
stoppedMessage = {
|
||||||
"workflowId": workflow.id,
|
"workflowId": workflow.id,
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"message": "🛑 Workflow stopped by user",
|
"message": "🛑 Workflow stopped by user",
|
||||||
|
|
@ -401,7 +399,7 @@ class WorkflowManager:
|
||||||
"taskProgress": "stopped",
|
"taskProgress": "stopped",
|
||||||
"actionProgress": "stopped"
|
"actionProgress": "stopped"
|
||||||
}
|
}
|
||||||
self.services.workflow.storeMessageWithDocuments(workflow, stopped_message, [])
|
self.services.workflow.storeMessageWithDocuments(workflow, stoppedMessage, [])
|
||||||
|
|
||||||
# Update workflow status to stopped
|
# Update workflow status to stopped
|
||||||
workflow.status = "stopped"
|
workflow.status = "stopped"
|
||||||
|
|
@ -431,7 +429,7 @@ class WorkflowManager:
|
||||||
"taskProgress": "stopped",
|
"taskProgress": "stopped",
|
||||||
"actionProgress": "stopped"
|
"actionProgress": "stopped"
|
||||||
}
|
}
|
||||||
self.services.workflow.storeMessageWithDocuments(workflow, stopped_message, [])
|
self.services.workflow.storeMessageWithDocuments(workflow, stoppedMessage, [])
|
||||||
|
|
||||||
# Update workflow status to stopped
|
# Update workflow status to stopped
|
||||||
workflow.status = "stopped"
|
workflow.status = "stopped"
|
||||||
|
|
@ -453,7 +451,7 @@ class WorkflowManager:
|
||||||
return
|
return
|
||||||
elif workflow.status == 'failed':
|
elif workflow.status == 'failed':
|
||||||
# Create error message
|
# Create error message
|
||||||
error_message = {
|
errorMessage = {
|
||||||
"workflowId": workflow.id,
|
"workflowId": workflow.id,
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"message": f"Workflow failed: {'Unknown error'}",
|
"message": f"Workflow failed: {'Unknown error'}",
|
||||||
|
|
@ -470,7 +468,7 @@ class WorkflowManager:
|
||||||
"taskProgress": "fail",
|
"taskProgress": "fail",
|
||||||
"actionProgress": "fail"
|
"actionProgress": "fail"
|
||||||
}
|
}
|
||||||
self.services.workflow.storeMessageWithDocuments(workflow, error_message, [])
|
self.services.workflow.storeMessageWithDocuments(workflow, errorMessage, [])
|
||||||
|
|
||||||
# Update workflow status to failed
|
# Update workflow status to failed
|
||||||
workflow.status = "failed"
|
workflow.status = "failed"
|
||||||
|
|
@ -514,7 +512,7 @@ class WorkflowManager:
|
||||||
"taskProgress": "fail",
|
"taskProgress": "fail",
|
||||||
"actionProgress": "fail"
|
"actionProgress": "fail"
|
||||||
}
|
}
|
||||||
self.services.workflow.storeMessageWithDocuments(workflow, error_message, [])
|
self.services.workflow.storeMessageWithDocuments(workflow, errorMessage, [])
|
||||||
|
|
||||||
# Update workflow status to failed
|
# Update workflow status to failed
|
||||||
workflow.status = "failed"
|
workflow.status = "failed"
|
||||||
|
|
@ -587,12 +585,12 @@ class WorkflowManager:
|
||||||
self.workflowProcessor._checkWorkflowStopped(workflow)
|
self.workflowProcessor._checkWorkflowStopped(workflow)
|
||||||
|
|
||||||
# Count messages by role
|
# Count messages by role
|
||||||
user_messages = [msg for msg in workflow.messages if msg.role == 'user']
|
userMessages = [msg for msg in workflow.messages if msg.role == 'user']
|
||||||
assistant_messages = [msg for msg in workflow.messages if msg.role == 'assistant']
|
assistantMessages = [msg for msg in workflow.messages if msg.role == 'assistant']
|
||||||
|
|
||||||
# Generate summary feedback
|
# Generate summary feedback
|
||||||
feedback = f"Workflow completed.\n\n"
|
feedback = f"Workflow completed.\n\n"
|
||||||
feedback += f"Processed {len(user_messages)} user inputs and generated {len(assistant_messages)} responses.\n"
|
feedback += f"Processed {len(userMessages)} user inputs and generated {len(assistantMessages)} responses.\n"
|
||||||
|
|
||||||
# Add final status
|
# Add final status
|
||||||
if workflow.status == "completed":
|
if workflow.status == "completed":
|
||||||
|
|
@ -724,45 +722,45 @@ class WorkflowManager:
|
||||||
"""Set user language for the service center"""
|
"""Set user language for the service center"""
|
||||||
self.services.user.language = language
|
self.services.user.language = language
|
||||||
|
|
||||||
async def _neutralizeContentIfEnabled(self, content_bytes: bytes, mimeType: str) -> bytes:
|
async def _neutralizeContentIfEnabled(self, contentBytes: bytes, mimeType: str) -> bytes:
|
||||||
"""Neutralize content if neutralization is enabled in user settings"""
|
"""Neutralize content if neutralization is enabled in user settings"""
|
||||||
try:
|
try:
|
||||||
# Check if neutralization is enabled
|
# Check if neutralization is enabled
|
||||||
config = self.services.neutralization.getConfig()
|
config = self.services.neutralization.getConfig()
|
||||||
if not config or not config.enabled:
|
if not config or not config.enabled:
|
||||||
return content_bytes
|
return contentBytes
|
||||||
|
|
||||||
# Decode content to text for neutralization
|
# Decode content to text for neutralization
|
||||||
try:
|
try:
|
||||||
text_content = content_bytes.decode('utf-8')
|
textContent = contentBytes.decode('utf-8')
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# Try alternative encodings
|
# Try alternative encodings
|
||||||
for enc in ['latin-1', 'cp1252', 'iso-8859-1']:
|
for enc in ['latin-1', 'cp1252', 'iso-8859-1']:
|
||||||
try:
|
try:
|
||||||
text_content = content_bytes.decode(enc)
|
textContent = contentBytes.decode(enc)
|
||||||
break
|
break
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# If unable to decode, return original bytes (binary content)
|
# If unable to decode, return original bytes (binary content)
|
||||||
logger.debug(f"Unable to decode content for neutralization, skipping: {mimeType}")
|
logger.debug(f"Unable to decode content for neutralization, skipping: {mimeType}")
|
||||||
return content_bytes
|
return contentBytes
|
||||||
|
|
||||||
# Neutralize the text content
|
# Neutralize the text content
|
||||||
# Note: The neutralization service should use names from config when processing
|
# Note: The neutralization service should use names from config when processing
|
||||||
result = self.services.neutralization.processText(text_content)
|
result = self.services.neutralization.processText(textContent)
|
||||||
if result and 'neutralized_text' in result:
|
if result and 'neutralized_text' in result:
|
||||||
neutralized_text = result['neutralized_text']
|
neutralizedText = result['neutralized_text']
|
||||||
# Encode back to bytes using the same encoding
|
# Encode back to bytes using the same encoding
|
||||||
try:
|
try:
|
||||||
return neutralized_text.encode('utf-8')
|
return neutralizedText.encode('utf-8')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error encoding neutralized text: {str(e)}")
|
logger.warning(f"Error encoding neutralized text: {str(e)}")
|
||||||
return content_bytes
|
return contentBytes
|
||||||
else:
|
else:
|
||||||
logger.warning("Neutralization did not return neutralized_text")
|
logger.warning("Neutralization did not return neutralized_text")
|
||||||
return content_bytes
|
return contentBytes
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error during content neutralization: {str(e)}")
|
logger.error(f"Error during content neutralization: {str(e)}")
|
||||||
# Return original content on error
|
# Return original content on error
|
||||||
return content_bytes
|
return contentBytes
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue