issues fixing in chat workflow

This commit is contained in:
ValueOn AG 2025-11-02 19:53:36 +01:00
parent 46fcd089c4
commit 6060d74eb2
9 changed files with 111 additions and 97 deletions

View file

@ -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)

View file

@ -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)
) )

View file

@ -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

View file

@ -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
) )

View file

@ -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

View file

@ -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,
} }
) )

View file

@ -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)

View file

@ -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"""

View file

@ -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