task planner works
This commit is contained in:
parent
b907d068b3
commit
f86b3a9e2e
15 changed files with 589 additions and 251 deletions
|
|
@ -15,7 +15,7 @@ Connector_AiOpenai_MAX_TOKENS = 2000
|
||||||
# Anthropic configuration
|
# Anthropic configuration
|
||||||
Connector_AiAnthropic_API_URL = https://api.anthropic.com/v1/messages
|
Connector_AiAnthropic_API_URL = https://api.anthropic.com/v1/messages
|
||||||
Connector_AiAnthropic_API_SECRET = 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
|
Connector_AiAnthropic_API_SECRET = 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
|
||||||
Connector_AiAnthropic_MODEL_NAME = claude-3-opus-20240229
|
Connector_AiAnthropic_MODEL_NAME = claude-3-5-sonnet-20241022
|
||||||
Connector_AiAnthropic_TEMPERATURE = 0.2
|
Connector_AiAnthropic_TEMPERATURE = 0.2
|
||||||
Connector_AiAnthropic_MAX_TOKENS = 2000
|
Connector_AiAnthropic_MAX_TOKENS = 2000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,11 @@ import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Any, List, Optional, Union
|
from typing import Dict, Any, List, Optional, Union
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from modules.interfaces.interfaceChatAccess import ChatAccess
|
from modules.interfaces.interfaceChatAccess import ChatAccess
|
||||||
from modules.interfaces.interfaceChatModel import (
|
from modules.interfaces.interfaceChatModel import (
|
||||||
TaskStatus, UserInputRequest, ChatDocument, TaskItem, ChatStat, ChatLog, ChatMessage, ChatWorkflow, TaskAction
|
TaskStatus, UserInputRequest, ChatDocument, TaskItem, ChatStat, ChatLog, ChatMessage, ChatWorkflow, TaskAction, TaskResult, ActionResult
|
||||||
)
|
)
|
||||||
from modules.interfaces.interfaceAppModel import User
|
from modules.interfaces.interfaceAppModel import User
|
||||||
|
|
||||||
|
|
@ -169,7 +168,7 @@ class ChatObjects:
|
||||||
logs=[ChatLog(**log) for log in workflow.get("logs", [])],
|
logs=[ChatLog(**log) for log in workflow.get("logs", [])],
|
||||||
messages=[ChatMessage(**msg) for msg in workflow.get("messages", [])],
|
messages=[ChatMessage(**msg) for msg in workflow.get("messages", [])],
|
||||||
stats=ChatStat(**workflow.get("dataStats", {})) if workflow.get("dataStats") else None,
|
stats=ChatStat(**workflow.get("dataStats", {})) if workflow.get("dataStats") else None,
|
||||||
mandateId=workflow.get("mandateId", self.currentUser.get("mandateId"))
|
mandateId=workflow.get("mandateId", self.currentUser.mandateId)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error validating workflow data: {str(e)}")
|
logger.error(f"Error validating workflow data: {str(e)}")
|
||||||
|
|
@ -202,7 +201,7 @@ class ChatObjects:
|
||||||
logs=[],
|
logs=[],
|
||||||
messages=[],
|
messages=[],
|
||||||
stats=ChatStat(**created.get("dataStats", {})) if created.get("dataStats") else None,
|
stats=ChatStat(**created.get("dataStats", {})) if created.get("dataStats") else None,
|
||||||
mandateId=created.get("mandateId", self.currentUser.get("mandateId"))
|
mandateId=created.get("mandateId", self.currentUser.mandateId)
|
||||||
)
|
)
|
||||||
|
|
||||||
def updateWorkflow(self, workflowId: str, workflowData: Dict[str, Any]) -> ChatWorkflow:
|
def updateWorkflow(self, workflowId: str, workflowData: Dict[str, Any]) -> ChatWorkflow:
|
||||||
|
|
@ -264,6 +263,9 @@ class ChatObjects:
|
||||||
def createWorkflowMessage(self, messageData: Dict[str, Any]) -> ChatMessage:
|
def createWorkflowMessage(self, messageData: Dict[str, Any]) -> ChatMessage:
|
||||||
"""Creates a message for a workflow if user has access."""
|
"""Creates a message for a workflow if user has access."""
|
||||||
try:
|
try:
|
||||||
|
# Ensure ID is present
|
||||||
|
if "id" not in messageData or not messageData["id"]:
|
||||||
|
messageData["id"] = f"msg_{uuid.uuid4()}"
|
||||||
# Check required fields
|
# Check required fields
|
||||||
requiredFields = ["id", "workflowId"]
|
requiredFields = ["id", "workflowId"]
|
||||||
for field in requiredFields:
|
for field in requiredFields:
|
||||||
|
|
@ -299,16 +301,6 @@ class ChatObjects:
|
||||||
# Create message in database
|
# Create message in database
|
||||||
createdMessage = self.db.recordCreate("workflowMessages", messageData)
|
createdMessage = self.db.recordCreate("workflowMessages", messageData)
|
||||||
|
|
||||||
# Update workflow's messageIds if this is a new message
|
|
||||||
if createdMessage:
|
|
||||||
# Get current messageIds or initialize empty list
|
|
||||||
messageIds = workflow.messageIds if hasattr(workflow, 'messageIds') else []
|
|
||||||
|
|
||||||
# Add the new message ID if not already in the list
|
|
||||||
if createdMessage["id"] not in messageIds:
|
|
||||||
messageIds.append(createdMessage["id"])
|
|
||||||
self.updateWorkflow(workflowId, {"messageIds": messageIds})
|
|
||||||
|
|
||||||
# Convert to ChatMessage model
|
# Convert to ChatMessage model
|
||||||
return ChatMessage(
|
return ChatMessage(
|
||||||
id=createdMessage["id"],
|
id=createdMessage["id"],
|
||||||
|
|
@ -319,7 +311,7 @@ class ChatObjects:
|
||||||
message=createdMessage.get("message"),
|
message=createdMessage.get("message"),
|
||||||
role=createdMessage.get("role", "assistant"),
|
role=createdMessage.get("role", "assistant"),
|
||||||
status=createdMessage.get("status", "step"),
|
status=createdMessage.get("status", "step"),
|
||||||
sequenceNr=len(messageIds), # Set sequence number based on message position
|
sequenceNr=len(workflow.messages) + 1, # Use messages list length for sequence number
|
||||||
publishedAt=createdMessage.get("publishedAt", self._getCurrentTimestamp()),
|
publishedAt=createdMessage.get("publishedAt", self._getCurrentTimestamp()),
|
||||||
stats=ChatStat(**createdMessage.get("stats", {})) if createdMessage.get("stats") else None
|
stats=ChatStat(**createdMessage.get("stats", {})) if createdMessage.get("stats") else None
|
||||||
)
|
)
|
||||||
|
|
@ -702,15 +694,6 @@ class ChatObjects:
|
||||||
messageCount = len(messages)
|
messageCount = len(messages)
|
||||||
logger.debug(f"Loaded {messageCount} messages for workflow {workflowId}")
|
logger.debug(f"Loaded {messageCount} messages for workflow {workflowId}")
|
||||||
|
|
||||||
# Check if messageIds exists and is valid
|
|
||||||
messageIds = workflow.get("messageIds", [])
|
|
||||||
if not messageIds or len(messageIds) != len(messages):
|
|
||||||
# Rebuild messageIds from messages
|
|
||||||
messageIds = [msg.get("id") for msg in messages]
|
|
||||||
# Update in database
|
|
||||||
self.updateWorkflow(workflowId, {"messageIds": messageIds})
|
|
||||||
logger.debug(f"Rebuilt messageIds for workflow {workflowId}")
|
|
||||||
|
|
||||||
# Log document counts for each message
|
# Log document counts for each message
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
docCount = len(msg.get("documents", []))
|
docCount = len(msg.get("documents", []))
|
||||||
|
|
@ -725,7 +708,6 @@ class ChatObjects:
|
||||||
# Assemble complete workflow object
|
# Assemble complete workflow object
|
||||||
completeWorkflow = workflow.copy()
|
completeWorkflow = workflow.copy()
|
||||||
completeWorkflow["messages"] = messages
|
completeWorkflow["messages"] = messages
|
||||||
completeWorkflow["messageIds"] = messageIds
|
|
||||||
completeWorkflow["logs"] = logs
|
completeWorkflow["logs"] = logs
|
||||||
|
|
||||||
return completeWorkflow
|
return completeWorkflow
|
||||||
|
|
@ -862,6 +844,7 @@ class ChatObjects:
|
||||||
return TaskItem(
|
return TaskItem(
|
||||||
id=task["id"],
|
id=task["id"],
|
||||||
workflowId=task["workflowId"],
|
workflowId=task["workflowId"],
|
||||||
|
userInput=task.get("userInput", ""),
|
||||||
status=task.get("status", TaskStatus.PENDING),
|
status=task.get("status", TaskStatus.PENDING),
|
||||||
error=task.get("error"),
|
error=task.get("error"),
|
||||||
startedAt=task.get("startedAt"),
|
startedAt=task.get("startedAt"),
|
||||||
|
|
@ -892,6 +875,9 @@ class ChatObjects:
|
||||||
def createTask(self, taskData: Dict[str, Any]) -> TaskItem:
|
def createTask(self, taskData: Dict[str, Any]) -> TaskItem:
|
||||||
"""Creates a new task if user has access to the workflow."""
|
"""Creates a new task if user has access to the workflow."""
|
||||||
try:
|
try:
|
||||||
|
# Ensure ID is present
|
||||||
|
if "id" not in taskData or not taskData["id"]:
|
||||||
|
taskData["id"] = f"task_{uuid.uuid4()}"
|
||||||
# Check workflow access
|
# Check workflow access
|
||||||
workflowId = taskData.get("workflowId")
|
workflowId = taskData.get("workflowId")
|
||||||
if not workflowId:
|
if not workflowId:
|
||||||
|
|
@ -908,9 +894,6 @@ class ChatObjects:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Ensure required fields
|
# Ensure required fields
|
||||||
if "id" not in taskData:
|
|
||||||
taskData["id"] = f"task_{uuid.uuid4()}"
|
|
||||||
|
|
||||||
if "status" not in taskData:
|
if "status" not in taskData:
|
||||||
taskData["status"] = TaskStatus.PENDING
|
taskData["status"] = TaskStatus.PENDING
|
||||||
|
|
||||||
|
|
@ -924,6 +907,7 @@ class ChatObjects:
|
||||||
task = TaskItem(
|
task = TaskItem(
|
||||||
id=createdTask["id"],
|
id=createdTask["id"],
|
||||||
workflowId=createdTask["workflowId"],
|
workflowId=createdTask["workflowId"],
|
||||||
|
userInput=createdTask.get("userInput", ""),
|
||||||
status=createdTask.get("status", TaskStatus.PENDING),
|
status=createdTask.get("status", TaskStatus.PENDING),
|
||||||
error=createdTask.get("error"),
|
error=createdTask.get("error"),
|
||||||
startedAt=createdTask.get("startedAt"),
|
startedAt=createdTask.get("startedAt"),
|
||||||
|
|
@ -938,7 +922,7 @@ class ChatObjects:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update workflow's task list
|
# Update workflow's task list
|
||||||
workflowTasks = workflow.get("tasks", [])
|
workflowTasks = workflow.tasks if hasattr(workflow, 'tasks') else []
|
||||||
if task.id not in workflowTasks:
|
if task.id not in workflowTasks:
|
||||||
workflowTasks.append(task.id)
|
workflowTasks.append(task.id)
|
||||||
self.updateWorkflow(workflowId, {"tasks": workflowTasks})
|
self.updateWorkflow(workflowId, {"tasks": workflowTasks})
|
||||||
|
|
@ -975,6 +959,7 @@ class ChatObjects:
|
||||||
return TaskItem(
|
return TaskItem(
|
||||||
id=updatedTask["id"],
|
id=updatedTask["id"],
|
||||||
workflowId=updatedTask["workflowId"],
|
workflowId=updatedTask["workflowId"],
|
||||||
|
userInput=updatedTask.get("userInput", task.userInput),
|
||||||
status=updatedTask.get("status", task.status),
|
status=updatedTask.get("status", task.status),
|
||||||
error=updatedTask.get("error", task.error),
|
error=updatedTask.get("error", task.error),
|
||||||
startedAt=updatedTask.get("startedAt", task.startedAt),
|
startedAt=updatedTask.get("startedAt", task.startedAt),
|
||||||
|
|
@ -1014,7 +999,7 @@ class ChatObjects:
|
||||||
# Delete task
|
# Delete task
|
||||||
if self.db.recordDelete("tasks", taskId):
|
if self.db.recordDelete("tasks", taskId):
|
||||||
# Update workflow's task list
|
# Update workflow's task list
|
||||||
workflowTasks = workflow.get("tasks", [])
|
workflowTasks = workflow.tasks if hasattr(workflow, 'tasks') else []
|
||||||
if taskId in workflowTasks:
|
if taskId in workflowTasks:
|
||||||
workflowTasks.remove(taskId)
|
workflowTasks.remove(taskId)
|
||||||
self.updateWorkflow(task.workflowId, {"tasks": workflowTasks})
|
self.updateWorkflow(task.workflowId, {"tasks": workflowTasks})
|
||||||
|
|
@ -1025,6 +1010,128 @@ class ChatObjects:
|
||||||
logger.error(f"Error deleting task: {str(e)}")
|
logger.error(f"Error deleting task: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Task Result Management
|
||||||
|
|
||||||
|
def createTaskResult(self, resultData: Dict[str, Any]) -> 'TaskResult':
|
||||||
|
"""Creates a new task result if user has access to the workflow."""
|
||||||
|
try:
|
||||||
|
# Ensure ID is present
|
||||||
|
if "id" not in resultData or not resultData["id"]:
|
||||||
|
resultData["id"] = f"result_{uuid.uuid4()}"
|
||||||
|
|
||||||
|
# Check workflow access if taskId is provided
|
||||||
|
taskId = resultData.get("taskId")
|
||||||
|
if taskId:
|
||||||
|
task = self.getTask(taskId)
|
||||||
|
if task:
|
||||||
|
workflow = self.getWorkflow(task.workflowId)
|
||||||
|
if not workflow:
|
||||||
|
logger.warning(f"No access to workflow {task.workflowId}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not self._canModify("workflows", task.workflowId):
|
||||||
|
logger.warning(f"No permission to modify workflow {task.workflowId}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Ensure required fields
|
||||||
|
if "status" not in resultData:
|
||||||
|
resultData["status"] = TaskStatus.PENDING
|
||||||
|
|
||||||
|
if "success" not in resultData:
|
||||||
|
resultData["success"] = False
|
||||||
|
|
||||||
|
# Create result in database
|
||||||
|
createdResult = self.db.recordCreate("taskResults", resultData)
|
||||||
|
|
||||||
|
# Convert to TaskResult model
|
||||||
|
return TaskResult(
|
||||||
|
taskId=createdResult.get("taskId", ""),
|
||||||
|
status=createdResult.get("status", TaskStatus.PENDING),
|
||||||
|
success=createdResult.get("success", False),
|
||||||
|
feedback=createdResult.get("feedback"),
|
||||||
|
error=createdResult.get("error")
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating task result: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def createActionResult(self, resultData: Dict[str, Any]) -> 'ActionResult':
|
||||||
|
"""Creates a new action result."""
|
||||||
|
try:
|
||||||
|
# Ensure ID is present
|
||||||
|
if "id" not in resultData or not resultData["id"]:
|
||||||
|
resultData["id"] = f"action_result_{uuid.uuid4()}"
|
||||||
|
|
||||||
|
# Ensure required fields
|
||||||
|
if "success" not in resultData:
|
||||||
|
resultData["success"] = False
|
||||||
|
|
||||||
|
if "data" not in resultData:
|
||||||
|
resultData["data"] = {}
|
||||||
|
|
||||||
|
# Create result in database
|
||||||
|
createdResult = self.db.recordCreate("actionResults", resultData)
|
||||||
|
|
||||||
|
# Convert to ActionResult model
|
||||||
|
return ActionResult(
|
||||||
|
success=createdResult.get("success", False),
|
||||||
|
data=createdResult.get("data", {}),
|
||||||
|
metadata=createdResult.get("metadata", {}),
|
||||||
|
validation=createdResult.get("validation", []),
|
||||||
|
error=createdResult.get("error")
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating action result: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def createTaskAction(self, actionData: Dict[str, Any]) -> TaskAction:
|
||||||
|
"""Creates a new task action."""
|
||||||
|
try:
|
||||||
|
# Ensure ID is present
|
||||||
|
if "id" not in actionData or not actionData["id"]:
|
||||||
|
actionData["id"] = f"action_{uuid.uuid4()}"
|
||||||
|
|
||||||
|
# Ensure required fields
|
||||||
|
if "status" not in actionData:
|
||||||
|
actionData["status"] = TaskStatus.PENDING
|
||||||
|
|
||||||
|
if "execMethod" not in actionData:
|
||||||
|
logger.error("execMethod is required for task action")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "execAction" not in actionData:
|
||||||
|
logger.error("execAction is required for task action")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "execParameters" not in actionData:
|
||||||
|
actionData["execParameters"] = {}
|
||||||
|
|
||||||
|
# Create action in database
|
||||||
|
createdAction = self.db.recordCreate("taskActions", actionData)
|
||||||
|
|
||||||
|
# Convert to TaskAction model
|
||||||
|
return TaskAction(
|
||||||
|
id=createdAction["id"],
|
||||||
|
execMethod=createdAction["execMethod"],
|
||||||
|
execAction=createdAction["execAction"],
|
||||||
|
execParameters=createdAction.get("execParameters", {}),
|
||||||
|
execResultLabel=createdAction.get("execResultLabel"),
|
||||||
|
status=createdAction.get("status", TaskStatus.PENDING),
|
||||||
|
error=createdAction.get("error"),
|
||||||
|
retryCount=createdAction.get("retryCount", 0),
|
||||||
|
retryMax=createdAction.get("retryMax", 3),
|
||||||
|
processingTime=createdAction.get("processingTime"),
|
||||||
|
timestamp=datetime.fromisoformat(createdAction.get("timestamp", datetime.now().isoformat())),
|
||||||
|
result=createdAction.get("result"),
|
||||||
|
resultDocuments=createdAction.get("resultDocuments", [])
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating task action: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
def getInterface(currentUser: Optional[User] = None) -> 'ChatObjects':
|
def getInterface(currentUser: Optional[User] = None) -> 'ChatObjects':
|
||||||
"""
|
"""
|
||||||
Returns a ChatObjects instance for the current user.
|
Returns a ChatObjects instance for the current user.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from typing import Dict, Any, Optional
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import logging
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
|
|
||||||
from modules.workflow.managerDocument import DocumentManager
|
from modules.workflow.managerDocument import DocumentManager
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from datetime import datetime, UTC
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -23,14 +23,21 @@ class ExcelService:
|
||||||
"""Get Microsoft connection from connection reference"""
|
"""Get Microsoft connection from connection reference"""
|
||||||
try:
|
try:
|
||||||
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
||||||
if userConnection and userConnection.authority == "microsoft" and userConnection.enabled:
|
if not userConnection or userConnection.authority != "msft" or userConnection.status != "active":
|
||||||
return {
|
return None
|
||||||
"id": userConnection.id,
|
|
||||||
"accessToken": userConnection.accessToken,
|
# Get the corresponding token for this user and authority
|
||||||
"refreshToken": userConnection.refreshToken,
|
token = self.serviceContainer.interfaceApp.getToken(userConnection.authority)
|
||||||
"scopes": userConnection.scopes
|
if not token:
|
||||||
}
|
logger.warning(f"No token found for user {userConnection.userId} and authority {userConnection.authority}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": userConnection.id,
|
||||||
|
"accessToken": token.tokenAccess,
|
||||||
|
"refreshToken": token.tokenRefresh,
|
||||||
|
"scopes": ["Mail.ReadWrite", "User.Read"] # Default Microsoft scopes
|
||||||
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import Dict, List, Any, Optional
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from typing import Dict, Any, List, Optional
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -22,14 +22,21 @@ class OutlookService:
|
||||||
"""Get Microsoft connection from connection reference"""
|
"""Get Microsoft connection from connection reference"""
|
||||||
try:
|
try:
|
||||||
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
||||||
if userConnection and userConnection.authority == "microsoft" and userConnection.enabled:
|
if not userConnection or userConnection.authority != "msft" or userConnection.status != "active":
|
||||||
return {
|
return None
|
||||||
"id": userConnection.id,
|
|
||||||
"accessToken": userConnection.accessToken,
|
# Get the corresponding token for this user and authority
|
||||||
"refreshToken": userConnection.refreshToken,
|
token = self.serviceContainer.interfaceApp.getToken(userConnection.authority)
|
||||||
"scopes": userConnection.scopes
|
if not token:
|
||||||
}
|
logger.warning(f"No token found for user {userConnection.userId} and authority {userConnection.authority}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": userConnection.id,
|
||||||
|
"accessToken": token.tokenAccess,
|
||||||
|
"refreshToken": token.tokenRefresh,
|
||||||
|
"scopes": ["Mail.ReadWrite", "User.Read"] # Default Microsoft scopes
|
||||||
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from datetime import datetime, UTC
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -23,14 +23,21 @@ class PowerpointService:
|
||||||
"""Get Microsoft connection from connection reference"""
|
"""Get Microsoft connection from connection reference"""
|
||||||
try:
|
try:
|
||||||
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
||||||
if userConnection and userConnection.authority == "microsoft" and userConnection.enabled:
|
if not userConnection or userConnection.authority != "msft" or userConnection.status != "active":
|
||||||
return {
|
return None
|
||||||
"id": userConnection.id,
|
|
||||||
"accessToken": userConnection.accessToken,
|
# Get the corresponding token for this user and authority
|
||||||
"refreshToken": userConnection.refreshToken,
|
token = self.serviceContainer.interfaceApp.getToken(userConnection.authority)
|
||||||
"scopes": userConnection.scopes
|
if not token:
|
||||||
}
|
logger.warning(f"No token found for user {userConnection.userId} and authority {userConnection.authority}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": userConnection.id,
|
||||||
|
"accessToken": token.tokenAccess,
|
||||||
|
"refreshToken": token.tokenRefresh,
|
||||||
|
"scopes": ["Mail.ReadWrite", "User.Read"] # Default Microsoft scopes
|
||||||
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from typing import Dict, Any, List, Optional
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -22,14 +22,21 @@ class SharepointService:
|
||||||
"""Get Microsoft connection from connection reference"""
|
"""Get Microsoft connection from connection reference"""
|
||||||
try:
|
try:
|
||||||
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
|
||||||
if userConnection and userConnection.authority == "microsoft" and userConnection.enabled:
|
if not userConnection or userConnection.authority != "msft" or userConnection.status != "active":
|
||||||
return {
|
return None
|
||||||
"id": userConnection.id,
|
|
||||||
"accessToken": userConnection.accessToken,
|
# Get the corresponding token for this user and authority
|
||||||
"refreshToken": userConnection.refreshToken,
|
token = self.serviceContainer.interfaceApp.getToken(userConnection.authority)
|
||||||
"scopes": userConnection.scopes
|
if not token:
|
||||||
}
|
logger.warning(f"No token found for user {userConnection.userId} and authority {userConnection.authority}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": userConnection.id,
|
||||||
|
"accessToken": token.tokenAccess,
|
||||||
|
"refreshToken": token.tokenRefresh,
|
||||||
|
"scopes": ["Mail.ReadWrite", "User.Read"] # Default Microsoft scopes
|
||||||
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
logger.error(f"Error getting Microsoft connection: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from modules.methods.methodBase import MethodBase, ActionResult, action
|
from modules.workflow.methodBase import MethodBase, ActionResult, action
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ import logging
|
||||||
from typing import Dict, Any, Optional, List, Union
|
from typing import Dict, Any, Optional, List, Union
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
import json
|
import json
|
||||||
import uuid
|
|
||||||
import time
|
|
||||||
|
|
||||||
from modules.interfaces.interfaceAppModel import User
|
from modules.interfaces.interfaceAppModel import User
|
||||||
from modules.interfaces.interfaceChatModel import (
|
from modules.interfaces.interfaceChatModel import (
|
||||||
|
|
@ -29,10 +27,39 @@ class ChatManager:
|
||||||
self.workflow = workflow
|
self.workflow = workflow
|
||||||
self.service = ServiceContainer(self.currentUser, self.workflow)
|
self.service = ServiceContainer(self.currentUser, self.workflow)
|
||||||
|
|
||||||
|
def _extractJsonFromResponse(self, response: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Extract JSON from verbose AI response that may contain explanatory text"""
|
||||||
|
try:
|
||||||
|
# First try direct JSON parsing
|
||||||
|
return json.loads(response)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
# Try to find JSON in the response
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Look for JSON object patterns
|
||||||
|
json_patterns = [
|
||||||
|
r'\{.*\}', # Basic JSON object
|
||||||
|
r'\[\{.*\}\]', # JSON array of objects
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in json_patterns:
|
||||||
|
matches = re.findall(pattern, response, re.DOTALL)
|
||||||
|
for match in matches:
|
||||||
|
try:
|
||||||
|
return json.loads(match)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If no JSON found, log the full response for debugging
|
||||||
|
logger.error(f"Could not extract JSON from response: {response[:500]}...")
|
||||||
|
return None
|
||||||
|
|
||||||
# ===== Task Creation and Management =====
|
# ===== Task Creation and Management =====
|
||||||
async def createInitialTask(self, workflow: ChatWorkflow, initialMessage: ChatMessage) -> Optional[TaskItem]:
|
async def createInitialTask(self, workflow: ChatWorkflow, initialMessage: ChatMessage) -> Optional[TaskItem]:
|
||||||
"""Create the initial task from the first message"""
|
"""Create the initial task from the first message"""
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"Creating initial task for workflow {workflow.id}")
|
||||||
|
|
||||||
# Create task definition prompt
|
# Create task definition prompt
|
||||||
prompt = await self._createTaskDefinitionPrompt(initialMessage.message, workflow)
|
prompt = await self._createTaskDefinitionPrompt(initialMessage.message, workflow)
|
||||||
|
|
||||||
|
|
@ -40,13 +67,13 @@ class ChatManager:
|
||||||
response = await self.service.callAiTextAdvanced(prompt)
|
response = await self.service.callAiTextAdvanced(prompt)
|
||||||
|
|
||||||
# Parse response
|
# Parse response
|
||||||
try:
|
taskDef = self._extractJsonFromResponse(response)
|
||||||
taskDef = json.loads(response)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
logger.error(f"Invalid JSON in task definition: {response}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Validate task definition
|
# Validate task definition
|
||||||
|
if not taskDef:
|
||||||
|
logger.error("Could not extract valid JSON from AI response")
|
||||||
|
return None
|
||||||
|
|
||||||
if not isinstance(taskDef, dict):
|
if not isinstance(taskDef, dict):
|
||||||
logger.error("Task definition must be a JSON object")
|
logger.error("Task definition must be a JSON object")
|
||||||
return None
|
return None
|
||||||
|
|
@ -61,6 +88,8 @@ class ChatManager:
|
||||||
logger.error("Actions must be a list")
|
logger.error("Actions must be a list")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
logger.info(f"Task definition validated: {len(taskDef['actions'])} actions")
|
||||||
|
|
||||||
# Create task using interface
|
# Create task using interface
|
||||||
taskData = {
|
taskData = {
|
||||||
"workflowId": workflow.id,
|
"workflowId": workflow.id,
|
||||||
|
|
@ -79,17 +108,40 @@ class ChatManager:
|
||||||
if not all(field in actionDef for field in requiredFields):
|
if not all(field in actionDef for field in requiredFields):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
action = TaskAction(
|
# Create action using interface
|
||||||
id=str(uuid.uuid4()),
|
actionData = {
|
||||||
execMethod=actionDef["method"],
|
"execMethod": actionDef["method"],
|
||||||
execAction=actionDef["action"],
|
"execAction": actionDef["action"],
|
||||||
execParameters=actionDef["parameters"],
|
"execParameters": actionDef["parameters"],
|
||||||
execResultLabel=actionDef.get("resultLabel")
|
"execResultLabel": actionDef.get("resultLabel")
|
||||||
)
|
}
|
||||||
taskData["actionList"].append(action)
|
action = self.chatInterface.createTaskAction(actionData)
|
||||||
|
if action:
|
||||||
|
# Convert TaskAction object to dictionary for database storage
|
||||||
|
actionDict = {
|
||||||
|
"id": action.id,
|
||||||
|
"execMethod": action.execMethod,
|
||||||
|
"execAction": action.execAction,
|
||||||
|
"execParameters": action.execParameters,
|
||||||
|
"execResultLabel": action.execResultLabel,
|
||||||
|
"status": action.status,
|
||||||
|
"error": action.error,
|
||||||
|
"retryCount": action.retryCount,
|
||||||
|
"retryMax": action.retryMax,
|
||||||
|
"processingTime": action.processingTime,
|
||||||
|
"timestamp": action.timestamp.isoformat() if action.timestamp else None,
|
||||||
|
"result": action.result,
|
||||||
|
"resultDocuments": action.resultDocuments
|
||||||
|
}
|
||||||
|
taskData["actionList"].append(actionDict)
|
||||||
|
|
||||||
# Create task using interface
|
# Create task using interface
|
||||||
task = self.chatInterface.createTask(taskData)
|
task = self.chatInterface.createTask(taskData)
|
||||||
|
if task:
|
||||||
|
logger.info(f"Task created successfully: {task.id}")
|
||||||
|
else:
|
||||||
|
logger.error("Failed to create task")
|
||||||
|
|
||||||
return task
|
return task
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -99,6 +151,8 @@ class ChatManager:
|
||||||
async def createNextTask(self, workflow: ChatWorkflow, previousResult: TaskResult) -> Optional[TaskItem]:
|
async def createNextTask(self, workflow: ChatWorkflow, previousResult: TaskResult) -> Optional[TaskItem]:
|
||||||
"""Create next task based on previous result"""
|
"""Create next task based on previous result"""
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"Creating next task for workflow {workflow.id}")
|
||||||
|
|
||||||
# Check if previous result was successful
|
# Check if previous result was successful
|
||||||
if not previousResult.success:
|
if not previousResult.success:
|
||||||
logger.error(f"Previous task failed: {previousResult.error}")
|
logger.error(f"Previous task failed: {previousResult.error}")
|
||||||
|
|
@ -111,13 +165,13 @@ class ChatManager:
|
||||||
response = await self.service.callAiTextAdvanced(prompt)
|
response = await self.service.callAiTextAdvanced(prompt)
|
||||||
|
|
||||||
# Parse response
|
# Parse response
|
||||||
try:
|
taskDef = self._extractJsonFromResponse(response)
|
||||||
taskDef = json.loads(response)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
logger.error(f"Invalid JSON in task definition: {response}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Validate task definition
|
# Validate task definition
|
||||||
|
if not taskDef:
|
||||||
|
logger.error("Could not extract valid JSON from AI response")
|
||||||
|
return None
|
||||||
|
|
||||||
if not isinstance(taskDef, dict):
|
if not isinstance(taskDef, dict):
|
||||||
logger.error("Task definition must be a JSON object")
|
logger.error("Task definition must be a JSON object")
|
||||||
return None
|
return None
|
||||||
|
|
@ -132,6 +186,8 @@ class ChatManager:
|
||||||
logger.error("Actions must be a list")
|
logger.error("Actions must be a list")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
logger.info(f"Next task definition validated: {len(taskDef['actions'])} actions")
|
||||||
|
|
||||||
# Create task using interface
|
# Create task using interface
|
||||||
taskData = {
|
taskData = {
|
||||||
"workflowId": workflow.id,
|
"workflowId": workflow.id,
|
||||||
|
|
@ -150,17 +206,40 @@ class ChatManager:
|
||||||
if not all(field in actionDef for field in requiredFields):
|
if not all(field in actionDef for field in requiredFields):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
action = TaskAction(
|
# Create action using interface
|
||||||
id=str(uuid.uuid4()),
|
actionData = {
|
||||||
execMethod=actionDef["method"],
|
"execMethod": actionDef["method"],
|
||||||
execAction=actionDef["action"],
|
"execAction": actionDef["action"],
|
||||||
execParameters=actionDef["parameters"],
|
"execParameters": actionDef["parameters"],
|
||||||
execResultLabel=actionDef.get("resultLabel")
|
"execResultLabel": actionDef.get("resultLabel")
|
||||||
)
|
}
|
||||||
taskData["actionList"].append(action)
|
action = self.chatInterface.createTaskAction(actionData)
|
||||||
|
if action:
|
||||||
|
# Convert TaskAction object to dictionary for database storage
|
||||||
|
actionDict = {
|
||||||
|
"id": action.id,
|
||||||
|
"execMethod": action.execMethod,
|
||||||
|
"execAction": action.execAction,
|
||||||
|
"execParameters": action.execParameters,
|
||||||
|
"execResultLabel": action.execResultLabel,
|
||||||
|
"status": action.status,
|
||||||
|
"error": action.error,
|
||||||
|
"retryCount": action.retryCount,
|
||||||
|
"retryMax": action.retryMax,
|
||||||
|
"processingTime": action.processingTime,
|
||||||
|
"timestamp": action.timestamp.isoformat() if action.timestamp else None,
|
||||||
|
"result": action.result,
|
||||||
|
"resultDocuments": action.resultDocuments
|
||||||
|
}
|
||||||
|
taskData["actionList"].append(actionDict)
|
||||||
|
|
||||||
# Create task using interface
|
# Create task using interface
|
||||||
task = self.chatInterface.createTask(taskData)
|
task = self.chatInterface.createTask(taskData)
|
||||||
|
if task:
|
||||||
|
logger.info(f"Next task created successfully: {task.id}")
|
||||||
|
else:
|
||||||
|
logger.error("Failed to create next task")
|
||||||
|
|
||||||
return task
|
return task
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -198,9 +277,8 @@ Example format:
|
||||||
response = await self.service.callAiTextBasic(prompt)
|
response = await self.service.callAiTextBasic(prompt)
|
||||||
|
|
||||||
# Parse response
|
# Parse response
|
||||||
try:
|
result = self._extractJsonFromResponse(response)
|
||||||
result = json.loads(response)
|
if not result:
|
||||||
except json.JSONDecodeError:
|
|
||||||
logger.error(f"Invalid JSON in action result: {response}")
|
logger.error(f"Invalid JSON in action result: {response}")
|
||||||
action.status = "failed"
|
action.status = "failed"
|
||||||
action.error = "Invalid result format"
|
action.error = "Invalid result format"
|
||||||
|
|
@ -229,6 +307,8 @@ Example format:
|
||||||
if message:
|
if message:
|
||||||
self.workflow.messages.append(message)
|
self.workflow.messages.append(message)
|
||||||
|
|
||||||
|
logger.info(f"Action execution logged: {action.execMethod}.{action.execAction} - {action.status}")
|
||||||
|
|
||||||
# If action failed, stop execution
|
# If action failed, stop execution
|
||||||
if action.status == "failed":
|
if action.status == "failed":
|
||||||
break
|
break
|
||||||
|
|
@ -324,33 +404,35 @@ Example format:
|
||||||
response = await self.service.callAiTextBasic(prompt)
|
response = await self.service.callAiTextBasic(prompt)
|
||||||
|
|
||||||
# Parse response
|
# Parse response
|
||||||
try:
|
result = self._extractJsonFromResponse(response)
|
||||||
result = json.loads(response)
|
if not result:
|
||||||
except json.JSONDecodeError:
|
|
||||||
logger.error(f"Invalid JSON in next task identification: {response}")
|
logger.error(f"Invalid JSON in next task identification: {response}")
|
||||||
return TaskResult(
|
# Create error result using interface
|
||||||
taskId=str(uuid.uuid4()),
|
errorResultData = {
|
||||||
status="failed",
|
"status": "failed",
|
||||||
success=False,
|
"success": False,
|
||||||
error="Invalid result format"
|
"error": "Invalid result format"
|
||||||
)
|
}
|
||||||
|
return self.chatInterface.createTaskResult(errorResultData)
|
||||||
|
|
||||||
return TaskResult(
|
# Create result using interface
|
||||||
taskId=str(uuid.uuid4()),
|
resultData = {
|
||||||
status="completed" if result.get("success", False) else "failed",
|
"status": "completed" if result.get("success", False) else "failed",
|
||||||
success=result.get("success", False),
|
"success": result.get("success", False),
|
||||||
feedback=result.get("feedback", ""),
|
"feedback": result.get("feedback", ""),
|
||||||
error=result.get("error", "")
|
"error": result.get("error", "")
|
||||||
)
|
}
|
||||||
|
return self.chatInterface.createTaskResult(resultData)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error identifying next task: {str(e)}")
|
logger.error(f"Error identifying next task: {str(e)}")
|
||||||
return TaskResult(
|
# Create error result using interface
|
||||||
taskId=str(uuid.uuid4()),
|
errorResultData = {
|
||||||
status="failed",
|
"status": "failed",
|
||||||
success=False,
|
"success": False,
|
||||||
error=str(e)
|
"error": str(e)
|
||||||
)
|
}
|
||||||
|
return self.chatInterface.createTaskResult(errorResultData)
|
||||||
|
|
||||||
async def generateWorkflowFeedback(self, workflow: ChatWorkflow) -> str:
|
async def generateWorkflowFeedback(self, workflow: ChatWorkflow) -> str:
|
||||||
"""Generate final feedback for the workflow"""
|
"""Generate final feedback for the workflow"""
|
||||||
|
|
@ -408,79 +490,93 @@ Please provide a comprehensive summary of this workflow."""
|
||||||
docRefs = self.service.getDocumentReferenceList()
|
docRefs = self.service.getDocumentReferenceList()
|
||||||
connRefs = self.service.getConnectionReferenceList()
|
connRefs = self.service.getConnectionReferenceList()
|
||||||
|
|
||||||
return f"""
|
prompt = f"""You are a task planning AI that creates structured task definitions in JSON format.
|
||||||
Task Definition for: {userInput}
|
|
||||||
|
|
||||||
Chat History:
|
TASK REQUEST: {userInput}
|
||||||
{messageSummary}
|
|
||||||
|
|
||||||
Available Methods:
|
CONTEXT:
|
||||||
{chr(10).join(f"- {method}" for method in methodList)}
|
Chat History: {messageSummary}
|
||||||
|
|
||||||
Available Documents:
|
AVAILABLE RESOURCES:
|
||||||
{chr(10).join(f"- {doc['documentReference']} ({doc['datetime']})" for doc in docRefs.get('chat', []))}
|
Methods: {chr(10).join(f"- {method}" for method in methodList)}
|
||||||
|
|
||||||
Available Connections:
|
Documents: {chr(10).join(f"- {doc['documentReference']} ({doc['datetime']})" for doc in docRefs.get('chat', []))}
|
||||||
{chr(10).join(f"- {conn['connectionReference']} ({conn['authority']})" for conn in connRefs)}
|
|
||||||
|
|
||||||
Your Task:
|
Connections: {chr(10).join(f"- {conn['connectionReference']} ({conn['authority']})" for conn in connRefs)}
|
||||||
1. Analyze the user input and chat history
|
|
||||||
2. Determine what actions are needed to accomplish the task
|
|
||||||
3. Create a sequence of actions using only the available methods, documents, and connections
|
|
||||||
4. Provide feedback about what will be done and what needs to be done next
|
|
||||||
|
|
||||||
Required Output:
|
INSTRUCTIONS:
|
||||||
1. A JSON object containing:
|
1. Analyze the task request and available resources
|
||||||
- status: Current state of the task ("pending", "running", "completed", or "failed")
|
2. Create a sequence of actions to accomplish the task
|
||||||
- feedback: Explanation of what will be done and what needs to be done next
|
3. Use ONLY the provided methods, documents, and connections
|
||||||
- actions: List of actions to execute, each containing:
|
4. Return a VALID JSON object with the exact structure shown below
|
||||||
* method: The method to use
|
|
||||||
* action: The specific action to perform
|
|
||||||
* parameters: Required parameters for the action
|
|
||||||
* resultLabel: Label for the action's result
|
|
||||||
|
|
||||||
2. Available Data:
|
REQUIRED JSON STRUCTURE:
|
||||||
- Use only provided document references in Available Documents section
|
|
||||||
- Use only provided connection references in Available Connections section
|
|
||||||
|
|
||||||
3. Method Usage Rules:
|
|
||||||
- Syntax: method.action([parameter:type])->resultLabel:type
|
|
||||||
- resultLabel format: documentList_<uuid>_<label>
|
|
||||||
- Actions must be in processing sequence
|
|
||||||
- Parameters must be from:
|
|
||||||
* Available document references
|
|
||||||
* Available connection references
|
|
||||||
* Result labels from previous actions
|
|
||||||
|
|
||||||
4. Result Labels:
|
|
||||||
- Use consistent naming for related documents
|
|
||||||
- Include descriptive labels for document sets
|
|
||||||
- Labels will be used to track document sets in messages
|
|
||||||
|
|
||||||
5. Error Handling:
|
|
||||||
- Include validation for each action
|
|
||||||
- Specify retry behavior if needed
|
|
||||||
- Provide clear error messages
|
|
||||||
- Errors will be recorded in messages with .error: suffix
|
|
||||||
|
|
||||||
Example Format:
|
|
||||||
{{
|
{{
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"feedback": "Will search for documents about project X and analyze them",
|
"feedback": "Clear explanation of what will be done",
|
||||||
|
"actions": [
|
||||||
|
{{
|
||||||
|
"method": "method_name",
|
||||||
|
"action": "action_name",
|
||||||
|
"parameters": {{
|
||||||
|
"param1": "value1",
|
||||||
|
"param2": "value2"
|
||||||
|
}},
|
||||||
|
"resultLabel": "documentList_uuid_label"
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
|
||||||
|
JSON FIELD REQUIREMENTS:
|
||||||
|
- "status": Must be "pending", "running", "completed", or "failed"
|
||||||
|
- "feedback": Human-readable explanation of the task plan
|
||||||
|
- "actions": Array of action objects (can be empty if no actions needed)
|
||||||
|
- "method": Must be one of the available methods listed above
|
||||||
|
- "action": Must be a valid action for that method
|
||||||
|
- "parameters": Object with method-specific parameters
|
||||||
|
- "resultLabel": Format: "documentList_uuid_descriptive_label"
|
||||||
|
|
||||||
|
PARAMETER RULES:
|
||||||
|
- Use only document references from "Documents" section above
|
||||||
|
- Use only connection references from "Connections" section above
|
||||||
|
- Use result labels from previous actions in the sequence
|
||||||
|
- All parameter values must be strings
|
||||||
|
|
||||||
|
EXAMPLE VALID JSON:
|
||||||
|
{{
|
||||||
|
"status": "pending",
|
||||||
|
"feedback": "I will search SharePoint for sales documents and then analyze the quarterly data to create a business intelligence report.",
|
||||||
"actions": [
|
"actions": [
|
||||||
{{
|
{{
|
||||||
"method": "sharepoint",
|
"method": "sharepoint",
|
||||||
"action": "search",
|
"action": "search",
|
||||||
"parameters": {{
|
"parameters": {{
|
||||||
"query": "project X",
|
"query": "sales quarterly report",
|
||||||
"site": "projects"
|
"site": "connection_123_msft_testuser@example.com"
|
||||||
}},
|
}},
|
||||||
"resultLabel": "documentList_<uuid>_search_results"
|
"resultLabel": "documentList_abc123_sales_documents"
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
"method": "excel",
|
||||||
|
"action": "analyze",
|
||||||
|
"parameters": {{
|
||||||
|
"document": "documentList_abc123_sales_documents"
|
||||||
|
}},
|
||||||
|
"resultLabel": "documentList_def456_analysis_results"
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
|
|
||||||
Please provide the task definition in JSON format following these rules."""
|
CRITICAL: Respond with ONLY the JSON object. Do not include any explanatory text, markdown formatting, or additional content outside the JSON structure."""
|
||||||
|
|
||||||
|
# Log the generated prompt for debugging
|
||||||
|
logger.debug("=" * 80)
|
||||||
|
logger.debug("TASK DEFINITION PROMPT:")
|
||||||
|
logger.debug("=" * 80)
|
||||||
|
logger.debug(prompt)
|
||||||
|
logger.debug("=" * 80)
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
|
||||||
# ===== Utility Methods =====
|
# ===== Utility Methods =====
|
||||||
async def processFileIds(self, fileIds: List[str]) -> List[ChatDocument]:
|
async def processFileIds(self, fileIds: List[str]) -> List[ChatDocument]:
|
||||||
|
|
@ -491,14 +587,16 @@ Please provide the task definition in JSON format following these rules."""
|
||||||
# Get file info from service
|
# Get file info from service
|
||||||
fileInfo = self.service.getFileInfo(fileId)
|
fileInfo = self.service.getFileInfo(fileId)
|
||||||
if fileInfo:
|
if fileInfo:
|
||||||
document = ChatDocument(
|
# Create document using interface
|
||||||
id=str(uuid.uuid4()),
|
documentData = {
|
||||||
fileId=fileId,
|
"fileId": fileId,
|
||||||
filename=fileInfo.get("filename", "unknown"),
|
"filename": fileInfo.get("filename", "unknown"),
|
||||||
fileSize=fileInfo.get("size", 0),
|
"fileSize": fileInfo.get("size", 0),
|
||||||
mimeType=fileInfo.get("mimeType", "application/octet-stream")
|
"mimeType": fileInfo.get("mimeType", "application/octet-stream")
|
||||||
)
|
}
|
||||||
documents.append(document)
|
document = self.chatInterface.createChatDocument(documentData)
|
||||||
|
if document:
|
||||||
|
documents.append(document)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing file ID {fileId}: {str(e)}")
|
logger.error(f"Error processing file ID {fileId}: {str(e)}")
|
||||||
return documents
|
return documents
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
import uuid
|
|
||||||
|
|
||||||
from modules.interfaces.interfaceAppObjects import User
|
from modules.interfaces.interfaceAppObjects import User
|
||||||
|
|
||||||
|
|
@ -31,6 +30,8 @@ class WorkflowManager:
|
||||||
async def workflowProcess(self, userInput: UserInputRequest, workflow: ChatWorkflow) -> TaskItem:
|
async def workflowProcess(self, userInput: UserInputRequest, workflow: ChatWorkflow) -> TaskItem:
|
||||||
"""Process a workflow with user input"""
|
"""Process a workflow with user input"""
|
||||||
|
|
||||||
|
logger.info(f"Processing workflow: {workflow.id}")
|
||||||
|
|
||||||
# Initialize chat manager
|
# Initialize chat manager
|
||||||
await self.chatManager.initialize(workflow)
|
await self.chatManager.initialize(workflow)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ from modules.interfaces.interfaceAiCalls import AiCalls
|
||||||
from modules.interfaces.interfaceChatObjects import getInterface as getChatObjects
|
from modules.interfaces.interfaceChatObjects import getInterface as getChatObjects
|
||||||
from modules.interfaces.interfaceChatModel import ActionResult
|
from modules.interfaces.interfaceChatModel import ActionResult
|
||||||
from modules.interfaces.interfaceComponentObjects import getInterface as getComponentObjects
|
from modules.interfaces.interfaceComponentObjects import getInterface as getComponentObjects
|
||||||
|
from modules.interfaces.interfaceAppObjects import getInterface as getAppObjects
|
||||||
from modules.workflow.managerDocument import DocumentManager
|
from modules.workflow.managerDocument import DocumentManager
|
||||||
from modules.methods.methodBase import MethodBase
|
from modules.workflow.methodBase import MethodBase
|
||||||
import uuid
|
import uuid
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ class ServiceContainer:
|
||||||
# Initialize managers
|
# Initialize managers
|
||||||
self.interfaceChat = getChatObjects(currentUser)
|
self.interfaceChat = getChatObjects(currentUser)
|
||||||
self.interfaceComponent = getComponentObjects(currentUser)
|
self.interfaceComponent = getComponentObjects(currentUser)
|
||||||
|
self.interfaceApp = getAppObjects(currentUser)
|
||||||
self.interfaceAiCalls = AiCalls()
|
self.interfaceAiCalls = AiCalls()
|
||||||
self.documentManager = DocumentManager(self)
|
self.documentManager = DocumentManager(self)
|
||||||
|
|
||||||
|
|
@ -64,14 +66,12 @@ class ServiceContainer:
|
||||||
|
|
||||||
# Discover actions from public methods
|
# Discover actions from public methods
|
||||||
actions = {}
|
actions = {}
|
||||||
for methodName, method in inspect.getmembers(methodInstance, predicate=inspect.isfunction):
|
for methodName, method in inspect.getmembers(type(methodInstance), predicate=inspect.iscoroutinefunction):
|
||||||
# Skip private methods and inherited methods
|
if not methodName.startswith('_') and methodName not in ['execute', 'validateParameters']:
|
||||||
if not methodName.startswith('_') and methodName not in ['execute', 'actions', 'validateParameters']:
|
# Bind the method to the instance
|
||||||
# Get method signature
|
bound_method = method.__get__(methodInstance, type(methodInstance))
|
||||||
sig = inspect.signature(method)
|
sig = inspect.signature(method)
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
# Convert parameters to action definition
|
|
||||||
for paramName, param in sig.parameters.items():
|
for paramName, param in sig.parameters.items():
|
||||||
if paramName not in ['self', 'authData']:
|
if paramName not in ['self', 'authData']:
|
||||||
params[paramName] = {
|
params[paramName] = {
|
||||||
|
|
@ -79,12 +79,10 @@ class ServiceContainer:
|
||||||
'required': param.default == param.empty,
|
'required': param.default == param.empty,
|
||||||
'description': param.default.__doc__ if hasattr(param.default, '__doc__') else None
|
'description': param.default.__doc__ if hasattr(param.default, '__doc__') else None
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add action definition
|
|
||||||
actions[methodName] = {
|
actions[methodName] = {
|
||||||
'description': method.__doc__ or '',
|
'description': method.__doc__ or '',
|
||||||
'parameters': params,
|
'parameters': params,
|
||||||
'method': method
|
'method': bound_method
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add method instance with discovered actions
|
# Add method instance with discovered actions
|
||||||
|
|
@ -96,7 +94,7 @@ class ServiceContainer:
|
||||||
logger.info(f"Discovered method: {methodInstance.name} with {len(actions)} actions")
|
logger.info(f"Discovered method: {methodInstance.name} with {len(actions)} actions")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error loading method module {name}: {str(e)}")
|
logger.error(f"Error loading method module {name}: {str(e)}", exc_info=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error discovering methods: {str(e)}")
|
logger.error(f"Error discovering methods: {str(e)}")
|
||||||
|
|
@ -255,9 +253,11 @@ class ServiceContainer:
|
||||||
def getConnectionReferenceList(self) -> List[Dict[str, str]]:
|
def getConnectionReferenceList(self) -> List[Dict[str, str]]:
|
||||||
"""Get list of all UserConnection objects as references"""
|
"""Get list of all UserConnection objects as references"""
|
||||||
connections = []
|
connections = []
|
||||||
for conn in self.user.connections:
|
# Get user connections through AppObjects interface
|
||||||
|
user_connections = self.interfaceApp.getUserConnections(self.user.id)
|
||||||
|
for conn in user_connections:
|
||||||
connections.append({
|
connections.append({
|
||||||
"connectionReference": f"connection_{conn.id}_{conn.authority}",
|
"connectionReference": f"connection_{conn.id}_{conn.authority}_{conn.externalUsername}",
|
||||||
"authority": conn.authority
|
"authority": conn.authority
|
||||||
})
|
})
|
||||||
# Sort by authority
|
# Sort by authority
|
||||||
|
|
@ -265,22 +265,26 @@ class ServiceContainer:
|
||||||
|
|
||||||
def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str:
|
def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str:
|
||||||
"""Get connection reference from UserConnection"""
|
"""Get connection reference from UserConnection"""
|
||||||
return f"connection_{connection.id}_{connection.authority}"
|
return f"connection_{connection.id}_{connection.authority}_{connection.externalUsername}"
|
||||||
|
|
||||||
def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]:
|
def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]:
|
||||||
"""Get UserConnection from reference string"""
|
"""Get UserConnection from reference string"""
|
||||||
try:
|
try:
|
||||||
# Parse reference format: connection_{id}_{authority}
|
# Parse reference format: connection_{id}_{authority}_{username}
|
||||||
parts = connectionReference.split('_')
|
parts = connectionReference.split('_')
|
||||||
if len(parts) != 3 or parts[0] != "connection":
|
if len(parts) != 4 or parts[0] != "connection":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
conn_id = parts[1]
|
conn_id = parts[1]
|
||||||
authority = parts[2]
|
authority = parts[2]
|
||||||
|
username = parts[3]
|
||||||
|
|
||||||
|
# Get user connections through AppObjects interface
|
||||||
|
user_connections = self.interfaceApp.getUserConnections(self.user.id)
|
||||||
|
|
||||||
# Find matching connection
|
# Find matching connection
|
||||||
for conn in self.user.connections:
|
for conn in user_connections:
|
||||||
if str(conn.id) == conn_id and conn.authority == authority:
|
if str(conn.id) == conn_id and conn.authority == authority and conn.externalUsername == username:
|
||||||
return conn
|
return conn
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -379,7 +383,17 @@ Please provide a clear summary of this message."""
|
||||||
|
|
||||||
def getFileInfo(self, fileId: str) -> Dict[str, Any]:
|
def getFileInfo(self, fileId: str) -> Dict[str, Any]:
|
||||||
"""Get file information"""
|
"""Get file information"""
|
||||||
return self.interfaceComponent.getFileInfo(fileId)
|
file_item = self.interfaceComponent.getFile(fileId)
|
||||||
|
if file_item:
|
||||||
|
return {
|
||||||
|
"id": file_item.id,
|
||||||
|
"filename": file_item.filename,
|
||||||
|
"size": file_item.fileSize,
|
||||||
|
"mimeType": file_item.mimeType,
|
||||||
|
"fileHash": file_item.fileHash,
|
||||||
|
"creationDate": file_item.creationDate
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
def getFileData(self, fileId: str) -> bytes:
|
def getFileData(self, fileId: str) -> bytes:
|
||||||
"""Get file data by ID"""
|
"""Get file data by ID"""
|
||||||
|
|
|
||||||
178
test_workflow.py
178
test_workflow.py
|
|
@ -7,46 +7,47 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, UTC
|
import json
|
||||||
|
from datetime import datetime, UTC, timedelta
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
# Set up test configuration
|
print("Starting test_workflow.py...")
|
||||||
os.environ['POWERON_CONFIG_FILE'] = 'test_config.ini'
|
|
||||||
|
|
||||||
# Simple imports from modules (same as app.py)
|
# Configure logging FIRST, before any other imports
|
||||||
from modules.interfaces.interfaceAppObjects import User, UserConnection
|
|
||||||
from modules.interfaces.interfaceChatObjects import ChatObjects
|
|
||||||
from modules.interfaces.interfaceChatModel import UserInputRequest, ChatWorkflow
|
|
||||||
from modules.workflow.managerWorkflow import WorkflowManager
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.DEBUG,
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
handlers=[
|
handlers=[
|
||||||
logging.StreamHandler(sys.stdout),
|
logging.StreamHandler(sys.stdout),
|
||||||
logging.FileHandler('test_workflow.log')
|
logging.FileHandler('test_workflow.log', encoding='utf-8')
|
||||||
]
|
],
|
||||||
|
force=True # Force reconfiguration even if already configured
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
print("Logger level:", logger.level)
|
||||||
|
logger.info("Logger is working!")
|
||||||
|
print("Logger test done")
|
||||||
|
|
||||||
|
# Set up test configuration
|
||||||
|
os.environ['POWERON_CONFIG_FILE'] = 'test_config.ini'
|
||||||
|
print("Set POWERON_CONFIG_FILE environment variable")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Simple imports from modules (same as app.py)
|
||||||
|
from modules.interfaces.interfaceAppObjects import User, UserConnection
|
||||||
|
from modules.interfaces.interfaceChatObjects import ChatObjects
|
||||||
|
from modules.interfaces.interfaceChatModel import UserInputRequest, ChatWorkflow
|
||||||
|
from modules.workflow.managerWorkflow import WorkflowManager
|
||||||
|
print("All imports successful")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Import error: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def create_test_user() -> User:
|
def create_test_user() -> User:
|
||||||
"""Create a test user for the workflow"""
|
"""Create a test user for the workflow"""
|
||||||
# Create test connections for Microsoft services
|
|
||||||
connections = [
|
|
||||||
UserConnection(
|
|
||||||
id="conn-001",
|
|
||||||
authority="microsoft",
|
|
||||||
name="Test Microsoft Account",
|
|
||||||
enabled=True,
|
|
||||||
accessToken="test-token-123",
|
|
||||||
refreshToken="test-refresh-456",
|
|
||||||
expiresAt=datetime.now(UTC).isoformat(),
|
|
||||||
scopes=["Files.ReadWrite", "Mail.ReadWrite", "Sites.ReadWrite.All"]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
return User(
|
return User(
|
||||||
id="test-user-001",
|
id="test-user-001",
|
||||||
mandateId="test-mandate-001",
|
mandateId="test-mandate-001",
|
||||||
|
|
@ -56,8 +57,7 @@ def create_test_user() -> User:
|
||||||
enabled=True,
|
enabled=True,
|
||||||
language="en",
|
language="en",
|
||||||
privilege="user",
|
privilege="user",
|
||||||
authenticationAuthority="local",
|
authenticationAuthority="local"
|
||||||
connections=connections
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_test_workflow() -> ChatWorkflow:
|
def create_test_workflow() -> ChatWorkflow:
|
||||||
|
|
@ -103,6 +103,7 @@ def create_test_user_input() -> UserInputRequest:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def test_workflow_process():
|
async def test_workflow_process():
|
||||||
|
print("Inside test_workflow_process()")
|
||||||
"""Test the workflowProcess function"""
|
"""Test the workflowProcess function"""
|
||||||
try:
|
try:
|
||||||
logger.info("Starting workflow process test...")
|
logger.info("Starting workflow process test...")
|
||||||
|
|
@ -112,17 +113,86 @@ async def test_workflow_process():
|
||||||
test_workflow = create_test_workflow()
|
test_workflow = create_test_workflow()
|
||||||
test_user_input = create_test_user_input()
|
test_user_input = create_test_user_input()
|
||||||
|
|
||||||
logger.info(f"Test user: {test_user.username}")
|
# Create test user in database through AppObjects interface
|
||||||
logger.info(f"Test workflow: {test_workflow.id}")
|
from modules.interfaces.interfaceAppObjects import getRootInterface
|
||||||
logger.info(f"Test input prompt: {test_user_input.prompt[:100]}...")
|
from modules.interfaces.interfaceAppModel import AuthAuthority, ConnectionStatus, Token, UserPrivilege
|
||||||
logger.info(f"Test files: {test_user_input.listFileId}")
|
|
||||||
|
|
||||||
# Initialize ChatObjects interface
|
root_interface = getRootInterface()
|
||||||
chat_interface = ChatObjects(test_user)
|
created_user = root_interface.createUser(
|
||||||
logger.info("ChatObjects interface initialized")
|
username=test_user.username,
|
||||||
|
password="testpassword123", # Required for local authentication
|
||||||
|
email=test_user.email,
|
||||||
|
fullName=test_user.fullName,
|
||||||
|
language=test_user.language,
|
||||||
|
enabled=test_user.enabled,
|
||||||
|
privilege=UserPrivilege.USER,
|
||||||
|
authenticationAuthority=AuthAuthority.LOCAL
|
||||||
|
)
|
||||||
|
logger.info(f"Created test user in database: {created_user.id}")
|
||||||
|
|
||||||
|
# Create test connection through AppObjects interface
|
||||||
|
from modules.interfaces.interfaceAppObjects import getInterface as getAppObjects
|
||||||
|
app_interface = getAppObjects(created_user)
|
||||||
|
test_connection = app_interface.addUserConnection(
|
||||||
|
userId=created_user.id,
|
||||||
|
authority=AuthAuthority.MSFT,
|
||||||
|
externalId="msft-user-123",
|
||||||
|
externalUsername="testuser@example.com",
|
||||||
|
externalEmail="testuser@example.com",
|
||||||
|
status=ConnectionStatus.ACTIVE
|
||||||
|
)
|
||||||
|
logger.info(f"Created test connection: {test_connection.id}")
|
||||||
|
|
||||||
|
# Create test token for the connection
|
||||||
|
test_token = Token(
|
||||||
|
userId=created_user.id,
|
||||||
|
authority=AuthAuthority.MSFT,
|
||||||
|
tokenAccess="test-access-token-123",
|
||||||
|
tokenRefresh="test-refresh-token-456",
|
||||||
|
tokenType="bearer",
|
||||||
|
expiresAt=datetime.now(UTC).timestamp() + 3600, # 1 hour from now
|
||||||
|
createdAt=datetime.now(UTC)
|
||||||
|
)
|
||||||
|
app_interface.saveToken(test_token)
|
||||||
|
logger.info(f"Created test token for connection: {test_token.id}")
|
||||||
|
|
||||||
|
logger.info(f"Test user: {created_user.username}")
|
||||||
|
logger.info(f"Test workflow: {test_workflow.id}")
|
||||||
|
|
||||||
|
# Log the full prompt in JSON format
|
||||||
|
logger.debug("=" * 60)
|
||||||
|
logger.debug("USER INPUT PROMPT (JSON):")
|
||||||
|
logger.debug("=" * 60)
|
||||||
|
prompt_data = {
|
||||||
|
"prompt": test_user_input.prompt,
|
||||||
|
"listFileId": test_user_input.listFileId,
|
||||||
|
"userLanguage": test_user_input.userLanguage
|
||||||
|
}
|
||||||
|
logger.debug(json.dumps(prompt_data, indent=2, ensure_ascii=False))
|
||||||
|
logger.debug("=" * 60)
|
||||||
|
|
||||||
|
logger.debug(f"Test files: {test_user_input.listFileId}")
|
||||||
|
|
||||||
|
# Create test workflow in database through ChatObjects interface
|
||||||
|
from modules.interfaces.interfaceChatObjects import getInterface as getChatObjects
|
||||||
|
|
||||||
|
chat_interface = getChatObjects(created_user)
|
||||||
|
workflow_data = {
|
||||||
|
"name": test_workflow.name,
|
||||||
|
"status": test_workflow.status,
|
||||||
|
"mandateId": created_user.mandateId,
|
||||||
|
"currentRound": test_workflow.currentRound,
|
||||||
|
"startedAt": test_workflow.startedAt,
|
||||||
|
"lastActivity": test_workflow.lastActivity
|
||||||
|
}
|
||||||
|
created_workflow = chat_interface.createWorkflow(workflow_data)
|
||||||
|
logger.info(f"Created test workflow: {created_workflow.id}")
|
||||||
|
|
||||||
|
# Update the test_workflow object with the created workflow's ID
|
||||||
|
test_workflow.id = created_workflow.id
|
||||||
|
|
||||||
# Initialize WorkflowManager
|
# Initialize WorkflowManager
|
||||||
workflow_manager = WorkflowManager(chat_interface, test_user)
|
workflow_manager = WorkflowManager(chat_interface, created_user)
|
||||||
logger.info("WorkflowManager initialized")
|
logger.info("WorkflowManager initialized")
|
||||||
|
|
||||||
# Test the workflowProcess function
|
# Test the workflowProcess function
|
||||||
|
|
@ -131,18 +201,37 @@ async def test_workflow_process():
|
||||||
|
|
||||||
# Log results
|
# Log results
|
||||||
if task:
|
if task:
|
||||||
logger.info("✅ Task created successfully!")
|
logger.debug("Task created successfully!")
|
||||||
logger.info(f"Task ID: {task.id}")
|
logger.debug(f"Task ID: {task.id}")
|
||||||
logger.info(f"Task Status: {task.status}")
|
logger.debug(f"Task Status: {task.status}")
|
||||||
logger.info(f"Task Feedback: {task.feedback}")
|
logger.debug(f"Task Feedback: {task.feedback}")
|
||||||
logger.info(f"Number of actions: {len(task.actionList) if task.actionList else 0}")
|
logger.info(f"Number of actions: {len(task.actionList) if task.actionList else 0}")
|
||||||
|
|
||||||
|
# Log the full task object in JSON format
|
||||||
|
logger.debug("=" * 60)
|
||||||
|
logger.debug("TASK OBJECT (JSON):")
|
||||||
|
logger.debug("=" * 60)
|
||||||
|
task_data = {
|
||||||
|
"id": task.id,
|
||||||
|
"status": task.status,
|
||||||
|
"feedback": task.feedback,
|
||||||
|
"actionList": [
|
||||||
|
{
|
||||||
|
"execMethod": action.execMethod,
|
||||||
|
"execAction": action.execAction,
|
||||||
|
"execParameters": action.execParameters
|
||||||
|
} for action in (task.actionList or [])
|
||||||
|
] if task.actionList else []
|
||||||
|
}
|
||||||
|
logger.debug(json.dumps(task_data, indent=2, ensure_ascii=False))
|
||||||
|
logger.debug("=" * 60)
|
||||||
|
|
||||||
if task.actionList:
|
if task.actionList:
|
||||||
for i, action in enumerate(task.actionList):
|
for i, action in enumerate(task.actionList):
|
||||||
logger.info(f"Action {i+1}: {action.execMethod}.{action.execAction}")
|
logger.info(f"Action {i+1}: {action.execMethod}.{action.execAction}")
|
||||||
logger.info(f" Parameters: {action.execParameters}")
|
logger.info(f" Parameters: {action.execParameters}")
|
||||||
else:
|
else:
|
||||||
logger.warning("⚠️ No task was created")
|
logger.warning("No task was created")
|
||||||
|
|
||||||
logger.info("Test completed successfully!")
|
logger.info("Test completed successfully!")
|
||||||
return task
|
return task
|
||||||
|
|
@ -153,7 +242,7 @@ async def test_workflow_process():
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
"""Main function to run the test"""
|
print("Inside main()")
|
||||||
logger.info("=" * 50)
|
logger.info("=" * 50)
|
||||||
logger.info("BUSINESS INTELLIGENCE WORKFLOW TEST")
|
logger.info("BUSINESS INTELLIGENCE WORKFLOW TEST")
|
||||||
logger.info("=" * 50)
|
logger.info("=" * 50)
|
||||||
|
|
@ -171,5 +260,6 @@ async def main():
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Run the test
|
print("About to run main()")
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
print("main() finished")
|
||||||
Loading…
Reference in a new issue