messageing aligned

This commit is contained in:
ValueOn AG 2025-09-02 11:47:39 +02:00
parent 0e034e253d
commit 91aff56e1c
7 changed files with 177 additions and 296 deletions

View file

@ -192,7 +192,8 @@ class HandlingTasks:
task_plan = TaskPlan(
overview=task_plan_dict.get('overview', ''),
tasks=tasks
tasks=tasks,
userMessage=task_plan_dict.get('userMessage', '')
)
# Set workflow totals for progress tracking
@ -217,24 +218,19 @@ class HandlingTasks:
"""Create a chat message containing the task plan with user-friendly messages"""
try:
# Build task plan summary
task_summary = f"📋 **Task Plan Generated**\n\n"
task_summary += f"**Overview:** {task_plan.overview}\n\n"
task_summary += f"**Total Tasks:** {len(task_plan.tasks)}\n\n"
# Add each task with its user message
for i, task in enumerate(task_plan.tasks):
task_summary += f"**Task {i+1}:** {task.objective}\n"
if task.userMessage:
task_summary += f" 💬 {task.userMessage}\n"
if task.success_criteria:
criteria_str = ', '.join(task.success_criteria)
task_summary += f" ✅ Success Criteria: {criteria_str}\n"
task_summary += "\n"
task_summary = f"📋 **Task Plan**\n\n"
# Get overall user message from task plan if available
overall_message = task_plan.userMessage
if overall_message:
task_summary += f"**Plan Summary:** {overall_message}\n\n"
task_summary += f"{overall_message}\n\n"
# Add each task with its user message
for i, task in enumerate(task_plan.tasks):
if task.userMessage:
task_summary += f"💬 {task.userMessage}\n"
task_summary += "\n"
# Create workflow message
message_data = {
@ -269,76 +265,6 @@ class HandlingTasks:
except Exception as e:
logger.error(f"Error creating task plan message: {str(e)}")
async def createDocumentContextMessage(self, documents: List, workflow):
"""Create a chat message with document context and workflow labeling"""
try:
# Get current workflow context and stats
workflow_context = self.service.getWorkflowContext()
workflow_stats = self.service.getWorkflowStats()
# Create a simple document context message without AI dependency
message_text = f"📄 **Document Context**\n\n"
message_text += f"**Total Documents:** {len(documents)}\n\n"
# Add workflow context information
current_round = workflow_context.get('currentRound', 0)
current_task = workflow_context.get('currentTask', 0)
total_tasks = workflow_stats.get('totalTasks', 0)
current_action = workflow_context.get('currentAction', 0)
total_actions = workflow_stats.get('totalActions', 0)
message_text += f"**Workflow Context:**\n"
message_text += f"- Round: {current_round}\n"
if total_tasks > 0:
message_text += f"- Task: {current_task}/{total_tasks}\n"
else:
message_text += f"- Task: {current_task}\n"
if total_actions > 0:
message_text += f"- Action: {current_action}/{total_actions}\n"
else:
message_text += f"- Action: {current_action}\n"
message_text += f"- Status: {workflow_stats.get('workflowStatus', 'unknown')}\n\n"
# Add document list
if documents:
message_text += "**Available Documents:**\n"
for i, doc in enumerate(documents[:5]): # Show first 5 documents
message_text += f"- {doc.fileName if hasattr(doc, 'fileName') else f'Document {i+1}'}\n"
if len(documents) > 5:
message_text += f"- ... and {len(documents) - 5} more documents\n"
message_text += "\n"
message_text += "Document context information is available for processing."
# Create workflow message
message_data = {
"workflowId": workflow.id,
"role": "assistant",
"message": message_text,
"status": "step",
"sequenceNr": len(workflow.messages) + 1,
"publishedAt": get_utc_timestamp(),
"documentsLabel": "document_context",
"documents": [], # Empty documents for context message
# Add workflow context fields
"roundNumber": workflow_context.get('currentRound', 0),
"taskNumber": workflow_context.get('currentTask', 0),
"actionNumber": workflow_context.get('currentAction', 0),
# Add progress status
"taskProgress": "pending",
"actionProgress": "pending"
}
message = self.chatInterface.createWorkflowMessage(message_data)
if message:
workflow.messages.append(message)
logger.info(f"Document context message created with {len(documents)} documents")
else:
logger.error("Failed to create document context message")
except Exception as e:
logger.error(f"Error creating document context message: {str(e)}")
async def generateTaskActions(self, task_step, workflow, previous_results=None, enhanced_context=None) -> List[TaskAction]:
"""Generate actions for a given task step."""
try:
@ -546,25 +472,13 @@ class HandlingTasks:
# Create database log entry for task start in format expected by frontend
if task_index is not None:
if total_tasks is not None:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"Executing task {task_index}/{total_tasks}",
"type": "info"
})
else:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"Executing task {task_index}/?",
"type": "info"
})
# Create a task start message for the user
task_progress = f"{task_index}/{total_tasks}" if total_tasks is not None else str(task_index)
task_start_message = {
"workflowId": workflow.id,
"role": "assistant",
"message": f"🚀 Starting Task {task_progress}\n\nObjective: {task_step.objective}",
"message": f"🚀 **Task {task_progress}**",
"status": "step",
"sequenceNr": len(workflow.messages) + 1,
"publishedAt": get_utc_timestamp(),
@ -617,11 +531,6 @@ class HandlingTasks:
logger.error("No actions defined for task step, aborting task execution")
break
# Create document context message if documents are available
available_docs = self.service.getAvailableDocuments(workflow)
if available_docs:
await self.createDocumentContextMessage(available_docs, workflow)
action_results = []
for action_idx, action in enumerate(actions):
# Check workflow status before each action execution
@ -639,18 +548,11 @@ class HandlingTasks:
# Log action start in format expected by frontend
logger.info(f"Task {task_index} - Starting action {action_number}/{total_actions}")
# Create database log entry for action start
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"Task {task_index} - Starting action {action_number}/{total_actions}",
"type": "info"
})
# Create an action start message for the user
action_start_message = {
"workflowId": workflow.id,
"role": "assistant",
"message": f"Task {task_index} - Action {action_number}/{total_actions}\n\nMethod: {action.execMethod}.{action.execAction}",
"message": f"⚡ **Action {action_number}/{total_actions}** (Method {action.execMethod}.{action.execAction})",
"status": "step",
"sequenceNr": len(workflow.messages) + 1,
"publishedAt": get_utc_timestamp(),
@ -694,34 +596,19 @@ class HandlingTasks:
if success:
logger.info(f"=== TASK {task_index or '?'} COMPLETED SUCCESSFULLY: {task_step.objective} ===")
# Create database log entry for task completion
if total_tasks is not None:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"🎯 Task {task_index}/{total_tasks} completed",
"type": "success"
})
else:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"🎯 Task {task_index}/? completed",
"type": "success"
})
# Create a task completion message for the user
task_progress = f"{task_index}/{total_tasks}" if total_tasks is not None else str(task_index)
# Enhanced completion message with criteria details
completion_message = f"🎯 Task {task_progress} Completed Successfully!\n\nObjective: {task_step.objective}\n\nFeedback: {feedback or 'Task completed successfully'}"
completion_message = f"🎯 **Task {task_progress}**\n\n{feedback or 'Task completed successfully'}"
# Add criteria status if available
if hasattr(review_result, 'met_criteria') and review_result.met_criteria:
completion_message += f"\n\n✅ **Success Criteria Met:**\n"
for criterion in review_result.met_criteria:
completion_message += f"{criterion}\n"
completion_message += f"\n{criterion}\n"
if hasattr(review_result, 'quality_score'):
completion_message += f"\n📊 **Quality Score:** {review_result.quality_score}/10"
completion_message += f"\n📊 Score {review_result.quality_score}/10"
task_completion_message = {
"workflowId": workflow.id,
@ -740,10 +627,6 @@ class HandlingTasks:
"taskProgress": "success"
}
# Add user-friendly message if available
if task_step.userMessage:
task_completion_message["message"] += f"\n\n💬 {task_step.userMessage}"
message = self.chatInterface.createWorkflowMessage(task_completion_message)
if message:
workflow.messages.append(message)
@ -824,7 +707,7 @@ class HandlingTasks:
retry_message = {
"workflowId": workflow.id,
"role": "assistant",
"message": f"🔄 Task {task_index} requires retry: {review_result.improvements}",
"message": f"🔄 **Task {task_index}** needs retry: {review_result.improvements}",
"status": "step",
"sequenceNr": len(workflow.messages) + 1,
"publishedAt": get_utc_timestamp(),
@ -843,19 +726,19 @@ class HandlingTasks:
continue
else:
logger.error(f"=== TASK {task_index or '?'} FAILED: {task_step.objective} after {attempt+1} attempts ===")
task_progress = f"{task_index}/{total_tasks}" if total_tasks is not None else str(task_index)
# Create user-facing error message for task failure
error_message = f"❌ Task {task_index or '?'} - '{task_step.objective}' failed after {attempt+1} attempts\n\n"
error_message += f"Objective: {task_step.objective}\n\n"
error_message = f"**Task {task_progress}**\n\n'{task_step.objective}' {attempt+1}x failed\n\n"
# Add specific error details if available
if review_result and hasattr(review_result, 'reason') and review_result.reason:
error_message += f"Reason: {review_result.reason}\n\n"
error_message += f"{review_result.reason}\n\n"
# Add criteria progress information if available
if retry_context and hasattr(retry_context, 'criteria_progress'):
progress = retry_context.criteria_progress
error_message += f"📊 **Progress Summary:**\n"
error_message += f"📊 **Details**\n"
if progress.get('met_criteria'):
error_message += f"✅ Met criteria: {', '.join(progress['met_criteria'])}\n"
if progress.get('unmet_criteria'):
@ -908,19 +791,18 @@ class HandlingTasks:
logger.error(f"=== TASK {task_index or '?'} FAILED AFTER ALL RETRIES: {task_step.objective} ===")
# Create user-facing error message for task failure
error_message = f"Task {task_index or '?'} - '{task_step.objective}' failed after all retries\n\n"
error_message += f"Objective: {task_step.objective}\n\n"
error_message = f"**Task {task_index or '?'}**\n\n '{task_step.objective}' failed after all retries\n\n"
error_message += f"{task_step.objective}\n\n"
# Add specific error details if available
if retry_context and hasattr(retry_context, 'previous_review_result') and retry_context.previous_review_result:
reason = retry_context.previous_review_result.get('reason', '')
if reason and reason != "Task failed after all retries.":
error_message += f"Reason: {reason}\n\n"
error_message += f"{reason}\n\n"
# Add retry information
error_message += f"Retries attempted: {retry_context.retry_count if retry_context else 'Unknown'}\n"
error_message += f"Status: Task failed permanently\n\n"
error_message += "Please check the connection and try again, or contact support if the issue persists."
error_message += f"Status: Task failed permanently"
# Create workflow message for user
message_data = {
@ -1170,7 +1052,8 @@ class HandlingTasks:
processingTime=createdAction.get("processingTime"),
timestamp=float(createdAction.get("timestamp", get_utc_timestamp())),
result=createdAction.get("result"),
resultDocuments=createdAction.get("resultDocuments", [])
resultDocuments=createdAction.get("resultDocuments", []),
userMessage=createdAction.get("userMessage")
)
except Exception as e:
@ -1241,20 +1124,6 @@ class HandlingTasks:
# Log action results
logger.info(f"Action completed successfully")
# Create database log entry for action completion
if total_actions is not None:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"✅ Task {task_num} - Action {action_num}/{total_actions} completed",
"type": "success"
})
else:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"✅ Task {task_num} - Action {action_num}/? completed",
"type": "success"
})
if created_documents:
logger.info(f"Output documents ({len(created_documents)}):")
for i, doc in enumerate(created_documents):
@ -1276,19 +1145,12 @@ class HandlingTasks:
await self.createActionMessage(action, result, workflow, result_label, [], task_step, task_index)
# Create database log entry for action failure
if total_actions is not None:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"❌ Task {task_num} - Action {action_num}/{total_actions} failed: {result.error}",
"type": "error"
})
else:
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"❌ Task {task_num} - Action {action_num}/? failed: {result.error}",
"type": "error"
})
self.chatInterface.createWorkflowLog({
"workflowId": workflow.id,
"message": f"❌ **Task {task_num}**\n\n❌ **Action {action_num}/{total_actions}** failed: {result.error}",
"type": "error"
})
# Log action summary
logger.info(f"=== TASK {task_num} ACTION {action_num} COMPLETED ===")
@ -1336,89 +1198,25 @@ class HandlingTasks:
# Create a more meaningful message that includes task context
task_objective = task_step.objective if task_step else 'Unknown task'
# Add comprehensive workflow context
current_round = workflow_context.get('currentRound', 0)
current_task = workflow_context.get('currentTask', 0)
total_tasks = workflow_stats.get('totalTasks', 0)
current_action = workflow_context.get('currentAction', 0)
total_actions = workflow_stats.get('totalActions', 0)
# Build a user-friendly message based on success/failure
if result.success:
if created_documents and len(created_documents) > 0:
doc_names = [doc.fileName for doc in created_documents[:3]]
if len(created_documents) > 3:
doc_names.append(f"... and {len(created_documents) - 3} more")
# Enhanced message with workflow context
message_text = f"✅ **Task {task_index or '?'} - Action {action.execMethod}.{action.execAction} Completed**\n\n"
message_text += f"**Objective:** {task_objective}\n\n"
message_text += f"**Generated {len(created_documents)} document(s):** {', '.join(doc_names)}\n\n"
message_text += f"**Result Label:** {result_label}\n"
# Add comprehensive workflow context
current_round = workflow_context.get('currentRound', 0)
current_task = workflow_context.get('currentTask', 0)
total_tasks = workflow_stats.get('totalTasks', 0)
current_action = workflow_context.get('currentAction', 0)
total_actions = workflow_stats.get('totalActions', 0)
message_text += f"**Workflow Context:**\n"
message_text += f"- Round: {current_round}\n"
if total_tasks > 0:
message_text += f"- Task: {current_task}/{total_tasks}\n"
else:
message_text += f"- Task: {current_task}\n"
if total_actions > 0:
message_text += f"- Action: {current_action}/{total_actions}\n"
else:
message_text += f"- Action: {current_action}\n"
message_text += f"- Status: {workflow_stats.get('workflowStatus', 'unknown')}"
else:
message_text = f"✅ **Task {task_index or '?'} - Action {action.execMethod}.{action.execAction} Completed**\n\n"
message_text += f"**Objective:** {task_objective}\n\n"
message_text += "**Action executed successfully**\n\n"
message_text += f"**Result Label:** {result_label}\n"
# Add comprehensive workflow context
current_round = workflow_context.get('currentRound', 0)
current_task = workflow_context.get('currentTask', 0)
total_tasks = workflow_stats.get('totalTasks', 0)
current_action = workflow_context.get('currentAction', 0)
total_actions = workflow_stats.get('totalActions', 0)
message_text += f"**Workflow Context:**\n"
message_text += f"- Round: {current_round}\n"
if total_tasks > 0:
message_text += f"- Task: {current_task}/{total_tasks}\n"
else:
message_text += f"- Task: {current_task}\n"
if total_actions > 0:
message_text += f"- Action: {current_action}/{total_actions}\n"
else:
message_text += f"- Action: {current_action}\n"
message_text += f"- Status: {workflow_stats.get('workflowStats', 'unknown')}"
message_text = f"**Action {current_action}/{total_actions} ({action.execMethod}.{action.execAction})**\n\n"
message_text += f"{task_objective}\n\n"
else:
# ⚠️ FAILURE MESSAGE - Show error details to user
error_details = result.error if result.error else "Unknown error occurred"
message_text = f"❌ **Task {task_index or '?'} - Action {action.execMethod}.{action.execAction} Failed**\n\n"
message_text += f"**Objective:** {task_objective}\n\n"
message_text += f"**Error:** {error_details}\n\n"
message_text += f"**Result Label:** {result_label}\n"
# Add comprehensive workflow context
current_round = workflow_context.get('currentRound', 0)
current_task = workflow_context.get('currentTask', 0)
total_tasks = workflow_stats.get('totalTasks', 0)
current_action = workflow_context.get('currentAction', 0)
total_actions = workflow_stats.get('totalActions', 0)
message_text += f"**Workflow Context:**\n"
message_text += f"- Round: {current_round}\n"
if total_tasks > 0:
message_text += f"- Task: {current_task}/{total_tasks}\n"
else:
message_text += f"- Task: {current_task}\n"
if total_actions > 0:
message_text += f"- Action: {current_action}/{total_actions}\n"
message_text += f"- Action: {current_action}\n"
message_text += f"- Status: {workflow_stats.get('workflowStatus', 'unknown')}\n\n"
message_text += "Please check the connection and try again."
message_text = f"**Action {current_action}/{total_actions} ({action.execMethod}.{action.execAction})**\n\n"
message_text += f"{task_objective}\n\n"
message_text += f"{error_details}\n\n"
message_data = {
"workflowId": workflow.id,
"role": "assistant",
@ -1432,19 +1230,12 @@ class HandlingTasks:
"documentsLabel": result_label,
"documents": created_documents,
# Add workflow context fields - extract from result_label to match document reference
"roundNumber": workflow_context.get('currentRound', 0),
"taskNumber": task_index,
"actionNumber": self._extractActionNumberFromLabel(result_label) if result_label else workflow_context.get('currentAction', 0),
"roundNumber": current_round,
"taskNumber": current_task,
"actionNumber": current_action,
"actionProgress": "success" if result.success else "fail"
}
# Add user-friendly message if available
if action.userMessage:
if result.success:
message_data["message"] += f"\n\n💬 {action.userMessage}"
else:
message_data["message"] += f"\n\n💬 Action was intended to: {action.userMessage}"
# Add debugging for error messages
if not result.success:
logger.info(f"Creating ERROR message: {message_text}")

View file

@ -34,8 +34,8 @@ INSTRUCTIONS:
3. Focus on business outcomes, not technical operations
4. Each task should produce meaningful, usable outputs
5. Ensure proper handover between tasks using result labels
6. Generate user-friendly messages for each task in the user's language ({user_language})
7. Detect the language of the user request and include it in languageUserDetected
6. Detect the language of the user request and include it in languageUserDetected
7. Generate user-friendly messages for each task in the user's request language
8. Return a JSON object with the exact structure shown below
TASK GROUPING PRINCIPLES:
@ -63,15 +63,15 @@ TASK PLANNING PRINCIPLES:
- Keep tasks at a meaningful level of abstraction
- Each task should produce results that can be used by subsequent tasks
- Ensure clear dependencies and handovers between tasks
- Provide clear, actionable user messages in the user's language ({user_language})
- Provide clear, actionable user messages in the user's request language
- Group related activities to minimize task fragmentation
- Only create multiple tasks when dealing with truly different, independent objectives
REQUIRED JSON STRUCTURE:
{{
"overview": "Brief description of the overall plan",
"userMessage": "User-friendly message explaining the task plan in {user_language}",
"languageUserDetected": "en", // Language code detected from user request (en, de, fr, it, es, etc.)
"userMessage": "User-friendly message explaining the task plan in user's request language",
"tasks": [
{{
"id": "task_1",
@ -79,7 +79,7 @@ REQUIRED JSON STRUCTURE:
"dependencies": ["task_0"], // IDs of tasks that must complete first
"success_criteria": ["criteria1", "criteria2"],
"estimated_complexity": "low|medium|high",
"userMessage": "User-friendly message explaining what this task will accomplish in {user_language}"
"userMessage": "User-friendly message explaining what this task will accomplish in user's request language"
}}
]
}}

View file

@ -350,16 +350,19 @@ class ServiceCenter:
doc_exchange = None
if message.documents:
if message.actionId and message.documentsLabel:
# Use new document label format
# Validate that we use the same label as in the message
validated_label = self._validateDocumentLabelConsistency(message)
# Use the message's actual documentsLabel
doc_refs = []
for doc in message.documents:
doc_ref = self.getDocumentReferenceFromChatDocument(doc, message)
doc_refs.append(doc_ref)
doc_exchange = DocumentExchange(
documentsLabel=message.documentsLabel,
doc_exchange = DocumentExchange(
documentsLabel=validated_label,
documents=doc_refs
)
)
else:
# Generate new labels for documents without explicit labels
doc_refs = []
@ -444,8 +447,21 @@ class ServiceCenter:
if document_list["chat"]:
context += "CURRENT ROUND DOCUMENTS:\n"
for exchange in document_list["chat"]:
# Generate docList reference for the exchange (using message ID)
doc_list_ref = f"docList:{exchange.documentsLabel}"
# Generate docList reference for the exchange (using message ID and label)
# Find the message that corresponds to this exchange
message_id = None
for message in self.workflow.messages:
if hasattr(message, 'documentsLabel') and message.documentsLabel == exchange.documentsLabel:
message_id = message.id
break
if message_id:
doc_list_ref = f"docList:{message_id}:{exchange.documentsLabel}"
else:
# Fallback to label-only format if message ID not found
doc_list_ref = f"docList:{exchange.documentsLabel}"
logger.debug(f"Using document label for action planning: {exchange.documentsLabel} (message_id: {message_id})")
context += f"- {doc_list_ref} contains:\n"
# Generate docItem references for each document in the list
for doc_ref in exchange.documents:
@ -460,8 +476,21 @@ class ServiceCenter:
if document_list["history"]:
context += "WORKFLOW HISTORY DOCUMENTS:\n"
for exchange in document_list["history"]:
# Generate docList reference for the exchange (using message ID)
doc_list_ref = f"docList:{exchange.documentsLabel}"
# Generate docList reference for the exchange (using message ID and label)
# Find the message that corresponds to this exchange
message_id = None
for message in self.workflow.messages:
if hasattr(message, 'documentsLabel') and message.documentsLabel == exchange.documentsLabel:
message_id = message.id
break
if message_id:
doc_list_ref = f"docList:{message_id}:{exchange.documentsLabel}"
else:
# Fallback to label-only format if message ID not found
doc_list_ref = f"docList:{exchange.documentsLabel}"
logger.debug(f"Using history document label for action planning: {exchange.documentsLabel} (message_id: {message_id})")
context += f"- {doc_list_ref} contains:\n"
# Generate docItem references for each document in the list
for doc_ref in exchange.documents:
@ -481,6 +510,16 @@ class ServiceCenter:
logger.error(f"Error generating enhanced document context: {str(e)}")
return "NO DOCUMENTS AVAILABLE - Error generating document context."
def _validateDocumentLabelConsistency(self, message) -> str:
"""Validate that the document label used for references matches the message's actual label"""
if not hasattr(message, 'documentsLabel') or not message.documentsLabel:
logger.debug(f"Message {message.id} has no documentsLabel, returning None")
return None
# Simply return the message's actual documentsLabel - no correction, just validation
logger.debug(f"Using message's documentsLabel for references: '{message.documentsLabel}'")
return message.documentsLabel
def _extractDocumentInfoFromReference(self, doc_ref: str) -> Dict[str, str]:
"""Extract document information from reference string"""
try:
@ -569,19 +608,56 @@ class ServiceCenter:
if message.documents:
for doc in message.documents:
if doc.id == doc_id:
doc_name = getattr(doc, 'fileName', 'unknown')
logger.debug(f"Found docItem reference {doc_ref}: {doc_name}")
all_documents.append(doc)
break
elif doc_ref.startswith("docList:"):
# docList:<messageId>:<label> - extract message ID and find document list
# docList:<messageId>:<label> or docList:<label> - extract message ID and find document list
parts = doc_ref.split(':')
if len(parts) >= 2:
if len(parts) >= 3:
# Format: docList:<messageId>:<label>
message_id = parts[1]
label = parts[2]
# Find the message by ID and get all its documents
for message in self.workflow.messages:
if str(message.id) == message_id:
if message.documents:
doc_names = [doc.fileName for doc in message.documents if hasattr(doc, 'fileName')]
logger.debug(f"Found docList reference {doc_ref}: {len(message.documents)} documents - {doc_names}")
all_documents.extend(message.documents)
else:
logger.debug(f"Found docList reference {doc_ref} but message has no documents")
break
elif len(parts) >= 2:
# Format: docList:<label> - find message by documentsLabel
label = parts[1]
logger.debug(f"Looking for message with documentsLabel: {label}")
# Find messages with matching documentsLabel
matching_messages = []
for message in self.workflow.messages:
# Check both attribute and raw data for documentsLabel
msg_label = getattr(message, 'documentsLabel', None)
if msg_label == label:
matching_messages.append(message)
logger.debug(f"Found message {message.id} with matching documentsLabel: {msg_label}")
else:
# Debug: show what labels we're comparing
logger.debug(f"Message {message.id} has documentsLabel: '{msg_label}' (looking for: '{label}')")
if matching_messages:
# Use the newest message (highest publishedAt)
matching_messages.sort(key=lambda msg: getattr(msg, 'publishedAt', 0), reverse=True)
newest_message = matching_messages[0]
if newest_message.documents:
doc_names = [doc.fileName for doc in newest_message.documents if hasattr(doc, 'fileName')]
logger.debug(f"Found docList reference {doc_ref}: {len(newest_message.documents)} documents - {doc_names}")
all_documents.extend(newest_message.documents)
else:
logger.debug(f"Found docList reference {doc_ref} but message has no documents")
else:
logger.debug(f"No messages found with documentsLabel: {label}")
else:
# Direct label reference (round1_task2_action3_contextinfo)
# Search for messages with matching documentsLabel to find the actual documents
@ -619,8 +695,9 @@ class ServiceCenter:
logger.debug(f"Newest message has {len(newest_message.documents) if newest_message.documents else 0} documents")
if newest_message.documents:
doc_names = [doc.fileName for doc in newest_message.documents if hasattr(doc, 'fileName')]
logger.debug(f"Added {len(newest_message.documents)} documents from newest message {newest_message.id}: {doc_names}")
all_documents.extend(newest_message.documents)
logger.debug(f"Added {len(newest_message.documents)} documents from newest message {newest_message.id}")
else:
logger.debug(f"No documents found in newest message {newest_message.id}")
else:
@ -641,8 +718,9 @@ class ServiceCenter:
logger.debug(f"Using fallback message {newest_fallback.id} with documentsLabel: {getattr(newest_fallback, 'documentsLabel', 'unknown')}")
if newest_fallback.documents:
doc_names = [doc.fileName for doc in newest_fallback.documents if hasattr(doc, 'fileName')]
logger.debug(f"Added {len(newest_fallback.documents)} documents from fallback message {newest_fallback.id}: {doc_names}")
all_documents.extend(newest_fallback.documents)
logger.debug(f"Added {len(newest_fallback.documents)} documents from fallback message {newest_fallback.id}")
else:
logger.debug(f"No documents found in fallback message {newest_fallback.id}")
else:

View file

@ -441,6 +441,10 @@ class MethodDocument(MethodBase):
if len(lines) > 2:
formatted_content = '\n'.join(lines[1:-1])
# For HTML format, check if AI returned complete HTML document
if extension == ".html" and (formatted_content.startswith('<!DOCTYPE') or formatted_content.startswith('<html')):
return formatted_content
return formatted_content
except Exception as e:
@ -643,7 +647,22 @@ class MethodDocument(MethodBase):
raise Exception("AI report generation failed - AI is required for report generation")
# Clean up the AI response and ensure it's valid HTML
if not aiReport.strip().startswith('<html'):
aiReport = aiReport.strip()
# Strip fenced code blocks like ```html ... ``` if present
if aiReport.startswith("```") and aiReport.endswith("```"):
lines = aiReport.split('\n')
if len(lines) >= 2:
# remove first and last fence lines (language tag allowed on first)
aiReport = '\n'.join(lines[1:-1]).strip()
# Check if AI response starts with DOCTYPE or html tag (complete HTML document)
if aiReport.startswith('<!DOCTYPE') or aiReport.startswith('<html'):
# AI returned complete HTML document, use it directly
return aiReport
else:
# AI returned HTML content without document structure, wrap it
# Check if AI response already contains a title/header
has_title = any(title.lower() in aiReport.lower() for title in [title, "outlook", "report", "status"])
@ -660,9 +679,6 @@ class MethodDocument(MethodBase):
html.append(aiReport)
html.append("</body></html>")
return '\n'.join(html)
else:
# AI returned complete HTML, use it directly
return aiReport
except Exception as e:
logger.error(f"Error generating AI report: {str(e)}")

View file

@ -270,15 +270,6 @@ async def get_workflow_messages(
# Get all messages
allMessages = interfaceChat.getWorkflowMessages(workflowId)
# Debug logging: Log attributes for each message
logger.debug(f"Retrieved {len(allMessages)} messages for workflow {workflowId}")
for i, message in enumerate(allMessages):
logger.debug(f"Message {i+1} (ID: {message.id}): {message}")
logger.debug(f" - Type: {getattr(message, 'type', 'N/A')}")
logger.debug(f" - Content: {getattr(message, 'content', 'N/A')[:100]}...")
logger.debug(f" - PublishedAt: {getattr(message, 'publishedAt', 'N/A')}")
logger.debug(f" - All attributes: {message.__dict__}")
# Apply selective data transfer if messageId is provided
if messageId:
# Find the index of the message with the given ID

View file

@ -141,6 +141,12 @@ class WorkflowManager:
self.chatManager.handlingTasks._checkWorkflowStopped()
# Create initial message using interface
# Generate the correct documentsLabel that matches what getDocumentReferenceList() will create
round_num = workflow.currentRound
task_num = 0
action_num = 0
context_label = f"round{round_num}_task{task_num}_action{action_num}_context"
messageData = {
"workflowId": workflow.id,
"role": "user",
@ -148,7 +154,7 @@ class WorkflowManager:
"status": "first",
"sequenceNr": 1,
"publishedAt": get_utc_timestamp(),
"documentsLabel": "workflow_start",
"documentsLabel": context_label,
"documents": [],
# Add workflow context fields
"roundNumber": workflow.currentRound,
@ -390,7 +396,7 @@ class WorkflowManager:
summary_message = {
"workflowId": workflow.id,
"role": "assistant",
"message": f"Workflow completed successfully. Completed {workflow_result.completed_tasks}/{workflow_result.total_tasks} tasks in {workflow_result.execution_time:.2f} seconds.",
"message": f"Workflow completed successfully.",
"status": "last",
"sequenceNr": len(workflow.messages) + 1,
"publishedAt": get_utc_timestamp(),

View file

@ -2,8 +2,7 @@
TODO
# System
- Backend/UI fix Table Connections mit korrekten Token Infos, View jedesmal neu laden im formGeneric
- model reference diagram for all models. who uses who? --> to see the basic building blocks
- chat workflow: messages to user in user language
- neutralizer to activate AND put back placeholders to the returned data
# Tests