From 682bab26bca164181a2c06935c9350109403b12c Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 6 May 2025 00:14:32 +0200
Subject: [PATCH] prod azure 1.0.9
---
connectors/connectorAiOpenai.py | 23 +-
connectors/connectorDbJson.py | 2 -
modules/agentAnalyst.py | 1 +
modules/agentCoach.py | 376 ++++++++++
modules/agentCoder.py | 1 +
modules/agentDocumentation.py | 108 +--
modules/agentWebcrawler.py | 1 +
modules/documentProcessor.py | 18 +-
modules/gatewayInterface.py | 5 +-
modules/lucydomInterface.py | 9 +-
modules/workflowAgentsRegistry.py | 1 +
modules/workflowManager.py | 50 +-
routes/routeUsers.py | 66 +-
static/1_LF-Details.png | Bin 0 -> 253009 bytes
static/2_LF-Details_Description.txt | 295 ++++++++
static/3_generated_code.py | 38 +
static/4_execution_history.json | 19 +
static/5_prime_numbers.txt | 1000 +++++++++++++++++++++++++++
18 files changed, 1877 insertions(+), 136 deletions(-)
create mode 100644 modules/agentCoach.py
create mode 100644 static/1_LF-Details.png
create mode 100644 static/2_LF-Details_Description.txt
create mode 100644 static/3_generated_code.py
create mode 100644 static/4_execution_history.json
create mode 100644 static/5_prime_numbers.txt
diff --git a/connectors/connectorAiOpenai.py b/connectors/connectorAiOpenai.py
index e394030d..fd03af86 100644
--- a/connectors/connectorAiOpenai.py
+++ b/connectors/connectorAiOpenai.py
@@ -1,4 +1,5 @@
import logging
+import base64
import httpx
from typing import Dict, Any, List, Union
from fastapi import HTTPException
@@ -93,7 +94,7 @@ class ChatService:
Analyzes an image with the OpenAI Vision API.
Args:
- imageData: Either a file path (str) or image data (bytes)
+ imageData: base64encoded data
mimeType: The MIME type of the image (optional, only for binary data)
prompt: The prompt for analysis
@@ -101,23 +102,7 @@ class ChatService:
The response from the OpenAI Vision API as text
"""
try:
- logger.debug("Starting image analysis...")
- # Distinguish between file path and binary data
- if isinstance(imageData, str):
- # It's a file path - import filehandling only when needed
- from modules import agentserviceFilemanager as fileHandler
- base64Data, autoMimeType = fileHandler.encodeFileToBase64(imageData)
- mimeType = mimeType or autoMimeType
- else:
- # It's binary data
- import base64
- base64Data = base64.b64encode(imageData).decode('utf-8')
- # MIME type must be specified for binary data
- if not mimeType:
- # Fallback to generic image type
- mimeType = "image/png"
-
- # Prepare the payload for the Vision API
+ logger.debug(f"Starting image analysis for {mimeType} with query '{prompt}' for {mimeType} size {len(imageData)}B...")
messages = [
{
"role": "user",
@@ -126,7 +111,7 @@ class ChatService:
{
"type": "image_url",
"image_url": {
- "url": f"data:{mimeType};base64,{base64Data}"
+ "url": f"data:{mimeType};base64,{imageData}"
}
}
]
diff --git a/connectors/connectorDbJson.py b/connectors/connectorDbJson.py
index 449d3bb3..de8f8b63 100644
--- a/connectors/connectorDbJson.py
+++ b/connectors/connectorDbJson.py
@@ -56,7 +56,6 @@ class DatabaseConnector:
self.mandateId = self._mandateId
self.userId = self._userId
- logger.info(f"DatabaseConnector initialized for directory: {self.dbFolder}")
logger.debug(f"Context: mandateId={self.mandateId}, userId={self.userId}")
def _initializeSystemTable(self):
@@ -286,7 +285,6 @@ class DatabaseConnector:
def getRecordset(self, table: str, fieldFilter: List[str] = None, recordFilter: Dict[str, Any] = None) -> List[Dict[str, Any]]:
"""Returns a list of records from a table, filtered by criteria."""
data = self._loadTable(table)
- logger.debug(f"getRecordset: data volume of {len(data)} records")
# Apply recordFilter if available
if recordFilter:
diff --git a/modules/agentAnalyst.py b/modules/agentAnalyst.py
index 853b3955..5e68c91e 100644
--- a/modules/agentAnalyst.py
+++ b/modules/agentAnalyst.py
@@ -23,6 +23,7 @@ class AgentAnalyst(AgentBase):
"""Initialize the data analysis agent"""
super().__init__()
self.name = "analyst"
+ self.label = "Data Analysis"
self.description = "Analyzes data using AI-powered insights and visualizations, produce diagrams and visualizations"
self.capabilities = [
"dataAnalysis",
diff --git a/modules/agentCoach.py b/modules/agentCoach.py
new file mode 100644
index 00000000..6a0616bf
--- /dev/null
+++ b/modules/agentCoach.py
@@ -0,0 +1,376 @@
+"""
+Coach agent for answering questions and generating structured content.
+Provides direct AI-based responses using extracted data from documents.
+"""
+
+import logging
+from typing import Dict, Any, List
+import json
+from datetime import datetime
+
+from modules.workflowAgentsRegistry import AgentBase
+
+logger = logging.getLogger(__name__)
+
+class AgentCoach(AgentBase):
+ """AI-driven agent for answering questions and generating structured content from extracted data"""
+
+ def __init__(self):
+ """Initialize the coach agent"""
+ super().__init__()
+ self.name = "coach"
+ self.label = "Coach & Assistant"
+ self.description = "Answers questions and generates content directly from extracted data without complex processing"
+ self.capabilities = [
+ "questionAnswering",
+ "contentGeneration",
+ "simpleDataFormatting",
+ "informationSynthesis",
+ "directResponse",
+ "imageInterpretation",
+ "structuredOutput"
+ ]
+
+ def setDependencies(self, mydom=None):
+ """Set external dependencies for the agent."""
+ self.mydom = mydom
+
+ async def processTask(self, task: Dict[str, Any]) -> Dict[str, Any]:
+ """
+ Process a task by directly using AI to provide answers or content based on extracted data.
+
+ Args:
+ task: Task dictionary with prompt, inputDocuments, outputSpecifications
+
+ Returns:
+ Dictionary with feedback and documents
+ """
+ try:
+ # Extract task information
+ prompt = task.get("prompt", "")
+ inputDocuments = task.get("inputDocuments", [])
+ outputSpecs = task.get("outputSpecifications", [])
+
+ # Check AI service
+ if not self.mydom:
+ return {
+ "feedback": "The Coach agent requires an AI service to function.",
+ "documents": []
+ }
+
+ # Collect all extracted data from input documents
+ documentContext = self._collectExtractedData(inputDocuments)
+
+ # Generate task understanding to guide response creation
+ taskUnderstanding = await self._analyzeTask(prompt, documentContext)
+
+ # Generate documents based on output specifications
+ documents = []
+
+ # If no output specs provided, create a default document
+ if not outputSpecs:
+ defaultFormat = taskUnderstanding.get("recommendedFormat", "md")
+ defaultTitle = taskUnderstanding.get("suggestedFilename", "response")
+
+ outputSpecs = [{
+ "label": f"{defaultTitle}.{defaultFormat}",
+ "description": "Response to your request"
+ }]
+
+ # Process each output specification
+ for spec in outputSpecs:
+ outputLabel = spec.get("label", "output.txt")
+ outputDescription = spec.get("description", "")
+
+ # Determine format based on file extension
+ outputFormat = outputLabel.split('.')[-1].lower() if '.' in outputLabel else "txt"
+
+ # Generate document based on format
+ document = await self._generateDocument(
+ prompt,
+ documentContext,
+ outputLabel,
+ outputFormat,
+ outputDescription,
+ taskUnderstanding
+ )
+
+ documents.append(document)
+
+ # Generate feedback
+ feedback = taskUnderstanding.get("feedback", "I've created content based on your request.")
+
+ return {
+ "feedback": feedback,
+ "documents": documents
+ }
+
+ except Exception as e:
+ logger.error(f"Error in coach processing: {str(e)}", exc_info=True)
+ return {
+ "feedback": f"Error while processing your request: {str(e)}",
+ "documents": []
+ }
+
+ def _collectExtractedData(self, documents: List[Dict[str, Any]]) -> str:
+ """
+ Collect extracted data from input documents.
+
+ Args:
+ documents: List of input documents
+
+ Returns:
+ Combined extracted data as text
+ """
+ contextParts = []
+
+ for doc in documents:
+ docName = doc.get("name", "unnamed")
+ if doc.get("ext"):
+ docName = f"{docName}.{doc.get('ext')}"
+
+ contextParts.append(f"\n\n--- {docName} ---\n")
+
+ # Process contents, focusing on dataExtracted field
+ for content in doc.get("contents", []):
+ if content.get("dataExtracted"):
+ contextParts.append(content.get("dataExtracted", ""))
+
+ return "\n".join(contextParts)
+
+ async def _analyzeTask(self, prompt: str, context: str) -> Dict:
+ """
+ Use AI to analyze the task and develop an understanding of what's required.
+
+ Args:
+ prompt: The task prompt
+ context: Extracted document data
+
+ Returns:
+ Task understanding dictionary
+ """
+ analysisPrompt = f"""
+ Analyze this request to determine the best approach for creating a response.
+
+ REQUEST: {prompt}
+
+ EXTRACTED DATA:
+ {context[:1500]}... (truncated if longer)
+
+ Create a task analysis in JSON format with the following structure:
+ {{
+ "requestType": "question|content|data|report|description",
+ "recommendedFormat": "md|txt|html|csv|json",
+ "suggestedFilename": "appropriate_filename_without_extension",
+ "contentFocus": "brief description of what to focus on",
+ "feedback": "brief explanation of how you'll approach this request",
+ "complexity": "simple|moderate|complex"
+ }}
+
+ Only return valid JSON. No preamble or explanations.
+ """
+
+ try:
+ response = await self.mydom.callAi([
+ {"role": "system", "content": "You are a task analysis expert. Respond with valid JSON only."},
+ {"role": "user", "content": analysisPrompt}
+ ])
+
+ # Extract JSON from response
+ jsonStart = response.find('{')
+ jsonEnd = response.rfind('}') + 1
+
+ if jsonStart >= 0 and jsonEnd > jsonStart:
+ taskUnderstanding = json.loads(response[jsonStart:jsonEnd])
+ return taskUnderstanding
+ else:
+ # Fallback if JSON not found
+ return {
+ "requestType": "content",
+ "recommendedFormat": "md",
+ "suggestedFilename": "response",
+ "contentFocus": "Addressing the main request",
+ "feedback": "I've created content based on your request and the provided data.",
+ "complexity": "moderate"
+ }
+
+ except Exception as e:
+ logger.warning(f"Error analyzing task: {str(e)}")
+ return {
+ "requestType": "content",
+ "recommendedFormat": "md",
+ "suggestedFilename": "response",
+ "contentFocus": "Addressing the main request",
+ "feedback": "I've created content based on your request and the provided data.",
+ "complexity": "moderate"
+ }
+
+ async def _generateDocument(self, prompt: str, context: str, outputLabel: str,
+ outputFormat: str, description: str, taskUnderstanding: Dict) -> Dict[str, Any]:
+ """
+ Generate a document based on the request and extracted data.
+
+ Args:
+ prompt: The task prompt
+ context: Extracted document data
+ outputLabel: Output filename
+ outputFormat: Output format (file extension)
+ description: Output description
+ taskUnderstanding: Task understanding from analysis
+
+ Returns:
+ Document object
+ """
+ # Determine content type based on format
+ contentType = self._getContentType(outputFormat)
+
+ # Build prompt based on output format
+ generationPrompt = f"""
+ Create a response to the following request in {outputFormat} format:
+
+ REQUEST: {prompt}
+
+ EXTRACTED DATA:
+ {context}
+
+ OUTPUT REQUIREMENTS:
+ - Filename: {outputLabel}
+ - Format: {outputFormat}
+ - Description: {description}
+ - Focus on: {taskUnderstanding.get("contentFocus", "Addressing the main request")}
+
+ Guidelines:
+ 1. Create content that directly addresses the request
+ 2. Use the extracted data to inform your response
+ 3. Format the output appropriately for {outputFormat}
+ 4. Be comprehensive but focused
+ 5. Include appropriate formatting, structure, and organization
+
+ Your response should be in valid {outputFormat} format without explanations or markdown formatting around it.
+ """
+
+ try:
+ # Build system prompt based on format
+ systemPrompt = f"You create {outputFormat} format content based on requests and extracted data. Provide only the content in valid {outputFormat} format."
+
+ # Generate content with AI
+ content = await self.mydom.callAi([
+ {"role": "system", "content": systemPrompt},
+ {"role": "user", "content": generationPrompt}
+ ])
+
+ # Process content based on format
+ if outputFormat in ["json", "csv"]:
+ # For structured formats, extract from code blocks if present
+ content = self._extractFromCodeBlocks(content, outputFormat)
+
+ # Validate JSON if needed
+ if outputFormat == "json":
+ try:
+ json.loads(content)
+ except:
+ logger.warning("Invalid JSON generated, attempting to fix")
+ # Try to extract just the JSON portion
+ jsonStart = content.find('{')
+ jsonEnd = content.rfind('}') + 1
+ if jsonStart >= 0 and jsonEnd > jsonStart:
+ content = content[jsonStart:jsonEnd]
+
+ # Ensure proper structure for markdown/HTML
+ if outputFormat in ["md", "markdown"] and not content.strip().startswith("#"):
+ title = "Response"
+ content = f"# {title}\n\n{content}"
+ elif outputFormat == "html" and not "{title}{title}
{content}"
+
+ return self.formatAgentDocumentOutput(outputLabel, content, contentType)
+
+ except Exception as e:
+ logger.error(f"Error generating document: {str(e)}")
+
+ # Create error document
+ errorContent = self._createErrorContent(str(e), outputFormat)
+ return self.formatAgentDocumentOutput(outputLabel, errorContent, contentType)
+
+ def _getContentType(self, outputFormat: str) -> str:
+ """
+ Get content type based on format.
+
+ Args:
+ outputFormat: Output format
+
+ Returns:
+ Content type
+ """
+ contentTypeMap = {
+ "md": "text/markdown",
+ "markdown": "text/markdown",
+ "html": "text/html",
+ "txt": "text/plain",
+ "text": "text/plain",
+ "json": "application/json",
+ "csv": "text/csv",
+ "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ }
+
+ return contentTypeMap.get(outputFormat, "text/plain")
+
+ def _extractFromCodeBlocks(self, content: str, format: str) -> str:
+ """
+ Extract content from code blocks if present.
+
+ Args:
+ content: Raw content
+ format: Expected format
+
+ Returns:
+ Extracted content
+ """
+ # Check for code blocks
+ codeBlockStart = f"```{format}"
+ if codeBlockStart in content:
+ start = content.find(codeBlockStart) + len(codeBlockStart)
+ end = content.find("```", start)
+ if end > start:
+ return content[start:end].strip()
+
+ # Check for generic code blocks
+ if "```" in content:
+ start = content.find("```") + 3
+ # Skip format identifier if present
+ if content[start:].strip() and not content[start:start+1].isalnum():
+ start = content.find("\n", start) + 1
+ end = content.find("```", start)
+ if end > start:
+ return content[start:end].strip()
+
+ return content
+
+ def _createErrorContent(self, errorMessage: str, outputFormat: str) -> str:
+ """
+ Create error content in the appropriate format.
+
+ Args:
+ errorMessage: Error message
+ outputFormat: Output format
+
+ Returns:
+ Formatted error content
+ """
+ if outputFormat == "json":
+ return json.dumps({"error": errorMessage})
+ elif outputFormat == "csv":
+ return f"error\n{errorMessage}"
+ elif outputFormat in ["md", "markdown"]:
+ return f"# Error\n\n{errorMessage}"
+ elif outputFormat == "html":
+ return f"Error
{errorMessage}
"
+ else:
+ return f"Error: {errorMessage}"
+
+
+# Factory function for the Coach agent
+def getAgentCoach():
+ """Returns an instance of the Coach agent."""
+ return AgentCoach()
\ No newline at end of file
diff --git a/modules/agentCoder.py b/modules/agentCoder.py
index 0286a157..9af8fd7d 100644
--- a/modules/agentCoder.py
+++ b/modules/agentCoder.py
@@ -24,6 +24,7 @@ class AgentCoder(AgentBase):
"""Initialize the coder agent"""
super().__init__()
self.name = "coder"
+ self.label = "Developer and Code Executor"
self.description = "Develops and executes Python code for data processing and automation"
self.capabilities = [
"code_development",
diff --git a/modules/agentDocumentation.py b/modules/agentDocumentation.py
index daae8a97..38b401d2 100644
--- a/modules/agentDocumentation.py
+++ b/modules/agentDocumentation.py
@@ -18,6 +18,7 @@ class AgentDocumentation(AgentBase):
"""Initialize the documentation agent"""
super().__init__()
self.name = "documentation"
+ self.label = "Documentation"
self.description = "Creates structured documentation, reports, and content using AI with multi-step generation"
self.capabilities = [
"report_generation",
@@ -59,7 +60,8 @@ class AgentDocumentation(AgentBase):
# Create task analysis to understand the requirements
documentationPlan = await self._analyzeTask(prompt, documentContext, outputSpecs)
-
+ logger.debug(f"Documentation plan: {documentationPlan}")
+
# Generate all required output documents
documents = []
@@ -216,7 +218,7 @@ class AgentDocumentation(AgentBase):
else:
# Fallback if JSON not found
return {
- "title": "Documentation",
+ "title": "Documentation (DEFAULT)",
"documentType": "report",
"audience": "general",
"detailedStructure": [
@@ -332,17 +334,17 @@ class AgentDocumentation(AgentBase):
# Fallback structure if none provided
detailedStructure = [
{
- "title": "Introduction",
+ "title": "Introduction (Default)",
"keyPoints": ["Purpose", "Scope"],
"importance": "high"
},
{
- "title": "Main Content",
+ "title": "Main Content (Default)",
"keyPoints": ["Core Information"],
"importance": "high"
},
{
- "title": "Conclusion",
+ "title": "Conclusion (Default)",
"keyPoints": ["Summary", "Next Steps"],
"importance": "medium"
}
@@ -351,25 +353,25 @@ class AgentDocumentation(AgentBase):
try:
# Step 1: Generate document introduction
introPrompt = f"""
- Create the introduction for a {documentType} titled "{title}".
-
- DOCUMENT OVERVIEW:
- - Type: {documentType}
- - Audience: {audience}
- - Tone: {tone}
- - Key Topics: {', '.join(keyTopics)}
- - Format: {formatType}
-
- TASK CONTEXT: {prompt}
-
- This introduction should:
- 1. Clearly state the purpose and scope of the document
- 2. Provide context and background information
- 3. Outline what the reader will find in the document
- 4. Set the appropriate tone for the {audience} audience
-
- The introduction should be professional and engaging, formatted according to {formatType} standards.
- """
+Create the introduction for a {documentType} titled "{title}".
+
+DOCUMENT OVERVIEW:
+- Type: {documentType}
+- Audience: {audience}
+- Tone: {tone}
+- Key Topics: {', '.join(keyTopics)}
+- Format: {formatType}
+
+TASK CONTEXT: {prompt}
+
+This introduction should:
+1. Clearly state the purpose and scope of the document
+2. Provide context and background information
+3. Outline what the reader will find in the document
+4. Set the appropriate tone for the {audience} audience
+
+The introduction should be professional and engaging, but short and precise, formatted according to {formatType} standards. do not add details, which are not requested by the Task Context.
+"""
introduction = await self.mydom.callAi([
{"role": "system", "content": f"You are a documentation expert creating an introduction in {formatType} format."},
@@ -417,35 +419,35 @@ class AgentDocumentation(AgentBase):
detailLevel = "high" if importance == "high" else "medium"
sectionPrompt = f"""
- Create the "{sectionTitle}" section for a {documentType} titled "{title}".
-
- SECTION DETAILS:
- - Title: {sectionTitle}
- - Key Points to Cover: {', '.join(keyPoints)}
- - Subsections: {', '.join(subsections)}
- - Detail Level: {detailLevel}
-
- DOCUMENT CONTEXT:
- - Type: {documentType}
- - Audience: {audience}
- - Tone: {tone}
- - Format: {formatType}
-
- TASK CONTEXT: {prompt}
-
- AVAILABLE INFORMATION:
- {context[:500]}... (truncated)
-
- This section should:
- 1. Be comprehensive and well-structured
- 2. Cover all the key points listed
- 3. Include the specified subsections with appropriate headings
- 4. Maintain a {tone} tone suitable for the {audience} audience
- 5. Be properly formatted according to {formatType} standards
- 6. Include specific examples, data, or evidence where appropriate
-
- Be thorough in your coverage of this section, providing substantive content.
- """
+Create the "{sectionTitle}" section for a {documentType} titled "{title}".
+
+SECTION DETAILS:
+- Title: {sectionTitle}
+- Key Points to Cover: {', '.join(keyPoints)}
+- Subsections: {', '.join(subsections)}
+- Detail Level: {detailLevel}
+
+DOCUMENT CONTEXT:
+- Type: {documentType}
+- Audience: {audience}
+- Tone: {tone}
+- Format: {formatType}
+
+TASK CONTEXT: {prompt}
+
+AVAILABLE INFORMATION:
+{context[:500]}... (truncated)
+
+This section should:
+1. Be comprehensive and well-structured
+2. Cover all the key points listed
+3. Include the specified subsections with appropriate headings
+4. Maintain a {tone} tone suitable for the {audience} audience
+5. Be properly formatted according to {formatType} standards
+6. Include specific examples, data, or evidence where appropriate
+
+Be thorough in your coverage of this section, providing substantive content focussing on the Task content.
+"""
sectionContent = await self.mydom.callAi([
{"role": "system", "content": f"You are a documentation expert creating detailed content for the {sectionTitle} section."},
diff --git a/modules/agentWebcrawler.py b/modules/agentWebcrawler.py
index cc2fe3f7..7f5cad09 100644
--- a/modules/agentWebcrawler.py
+++ b/modules/agentWebcrawler.py
@@ -26,6 +26,7 @@ class AgentWebcrawler(AgentBase):
"""Initialize the webcrawler agent"""
super().__init__()
self.name = "webcrawler"
+ self.label = "Web-Research"
self.description = "Conducts web research and collects information from online sources"
self.capabilities = [
"webSearch",
diff --git a/modules/documentProcessor.py b/modules/documentProcessor.py
index 528109be..d3b637e1 100644
--- a/modules/documentProcessor.py
+++ b/modules/documentProcessor.py
@@ -206,7 +206,7 @@ def extractTextContent(fileName: str, fileContent: bytes, mimeType: str) -> List
"sequenceNr": 1,
"name": "1_text", # Simplified naming
"ext": fileExtension,
- "contentType": "text",
+ "contentType": "text/plain",
"data": textContent,
"base64Encoded": False,
"metadata": {
@@ -225,7 +225,7 @@ def extractTextContent(fileName: str, fileContent: bytes, mimeType: str) -> List
"sequenceNr": 1,
"name": "1_text", # Simplified naming
"ext": fileExtension,
- "contentType": "text",
+ "contentType": "text/plain",
"data": textContent,
"base64Encoded": False,
"metadata": {
@@ -282,7 +282,7 @@ def extractCsvContent(fileName: str, fileContent: bytes) -> List[Dict[str, Any]]
"sequenceNr": 1,
"name": "1_csv", # Simplified naming
"ext": "csv",
- "contentType": "csv",
+ "contentType": "text/csv",
"data": csvContent,
"base64Encoded": False,
"metadata": {
@@ -302,7 +302,7 @@ def extractCsvContent(fileName: str, fileContent: bytes) -> List[Dict[str, Any]]
"sequenceNr": 1,
"name": "1_csv", # Simplified naming
"ext": "csv",
- "contentType": "csv",
+ "contentType": "text/csv",
"data": csvContent,
"base64Encoded": False,
"metadata": {
@@ -519,7 +519,7 @@ def extractImageContent(fileName: str, fileContent: bytes, mimeType: str) -> Lis
"sequenceNr": 1,
"name": "1_image", # Simplified naming
"ext": fileExtension,
- "contentType": "image",
+ "contentType": mimeType,
"data": encoded_data,
"base64Encoded": True,
"metadata": imageMetadata
@@ -531,7 +531,7 @@ def extractImageContent(fileName: str, fileContent: bytes, mimeType: str) -> Lis
"sequenceNr": 2,
"name": "2_text_image_info", # Simplified naming with label
"ext": "txt",
- "contentType": "text",
+ "contentType": "text/plain",
"data": imageDescription,
"base64Encoded": False,
"metadata": {
@@ -604,7 +604,7 @@ def extractPdfContent(fileName: str, fileContent: bytes) -> List[Dict[str, Any]]
"sequenceNr": len(contents) + 1,
"name": f"{len(contents) + 1}_text", # Simplified naming
"ext": "txt",
- "contentType": "text",
+ "contentType": "text/plain",
"data": extractedText,
"base64Encoded": False,
"metadata": {
@@ -743,7 +743,7 @@ def extractWordContent(fileName: str, fileContent: bytes, mimeType: str) -> List
"sequenceNr": 1,
"name": "1_text", # Simplified naming
"ext": "txt",
- "contentType": "text",
+ "contentType": "text/plain",
"data": extractedText,
"base64Encoded": False,
"metadata": {
@@ -845,7 +845,7 @@ def extractExcelContent(fileName: str, fileContent: bytes, mimeType: str) -> Lis
"sequenceNr": len(contents) + 1,
"name": f"{len(contents) + 1}_csv_{sheetSafeName}", # Simplified naming with sheet label
"ext": "csv",
- "contentType": "csv",
+ "contentType": "text/csv",
"data": csvContent,
"base64Encoded": False,
"metadata": {
diff --git a/modules/gatewayInterface.py b/modules/gatewayInterface.py
index 7c7f0aea..94359949 100644
--- a/modules/gatewayInterface.py
+++ b/modules/gatewayInterface.py
@@ -393,7 +393,9 @@ class GatewayInterface:
def authenticateUser(self, username: str, password: str) -> Optional[Dict[str, Any]]:
"""Authenticates a user by username and password."""
- user = self.getUserByUsername(username)
+ # Instead of using UAM filtering, directly get user from database
+ users = self.db.getRecordset("users")
+ user = next((u for u in users if u.get("username") == username), None)
if not user:
return None
@@ -412,6 +414,7 @@ class GatewayInterface:
return authenticatedUser
+
def updateUser(self, userId: int, userData: Dict[str, Any]) -> Dict[str, Any]:
"""Updates a user if current user has permission."""
# Check if the user exists and current user has access
diff --git a/modules/lucydomInterface.py b/modules/lucydomInterface.py
index ac779782..f0f91b2d 100644
--- a/modules/lucydomInterface.py
+++ b/modules/lucydomInterface.py
@@ -263,6 +263,13 @@ class LucyDOMInterface:
else:
return await self.aiService.callApi(messages)
+ async def callAi4Image(self, imageData: Union[str, bytes], mimeType: str = None, prompt: str = "Describe this image") -> str:
+ """Enhanced AI service call with language support."""
+ if not self.aiService:
+ logger.error("AI service not set in LucyDOMInterface")
+ return "Error: AI service not available"
+ return await self.aiService.analyzeImage(imageData, mimeType, prompt)
+
# Utilities
def getInitialId(self, table: str) -> Optional[int]:
@@ -680,7 +687,7 @@ class LucyDOMInterface:
self.createFileData(dbFile["id"], fileContent)
# Debug: Export file to static folder
- # self._exportFileToStatic(fileContent, dbFile["id"], fileName)
+ self._exportFileToStatic(fileContent, dbFile["id"], fileName)
logger.info(f"File upload process completed for: {fileName}")
return dbFile
diff --git a/modules/workflowAgentsRegistry.py b/modules/workflowAgentsRegistry.py
index 8cd7d63d..25d8d2ff 100644
--- a/modules/workflowAgentsRegistry.py
+++ b/modules/workflowAgentsRegistry.py
@@ -28,6 +28,7 @@ class AgentBase:
def __init__(self):
"""Initialize the base agent."""
self.name = "base-agent"
+ self.label = "(Template)"
self.description = "Basic agent functionality"
self.capabilities = []
self.mydom = None
diff --git a/modules/workflowManager.py b/modules/workflowManager.py
index 087cb5a1..5d89d311 100644
--- a/modules/workflowManager.py
+++ b/modules/workflowManager.py
@@ -135,7 +135,7 @@ class WorkflowManager:
try:
# State 3: User Message Processing
self.checkExitCriteria(workflow)
- messageUser = await self.chatMessageToWorkflow("user", "", userInput, workflow)
+ messageUser = await self.chatMessageToWorkflow("user", None, userInput, workflow)
messageUser["status"] = "first" # For first message
# State 4: Project Manager Analysis
@@ -155,7 +155,7 @@ class WorkflowManager:
self.checkExitCriteria(workflow)
responseMessage = {
"role": "assistant",
- "agentName": "project_manager",
+ "agentName": "Project Manager",
"content": objUserResponse,
"status": "step" # As per state machine specification
}
@@ -228,10 +228,8 @@ class WorkflowManager:
"""
currentTime = datetime.now().isoformat()
- logger.debug(f"CHECK DATA0 id'{workflowId}'")
workflowExist=self.mydom.getWorkflow(workflowId)
if workflowId is None or not workflowExist:
- logger.debug(f"CHECK DATA1 id'{workflowId}'")
# Create new workflow
newWorkflowId = str(uuid.uuid4()) if workflowId is None else workflowId
workflow = {
@@ -277,8 +275,8 @@ class WorkflowManager:
workflow["messageIds"] = [msg["id"] for msg in workflow.get("messages", [])]
# Update in database
- self.mydom.updateWorkflow(workflowId, {"messageIds": workflow["messageIds"]})
-
+ self.mydom.updateWorkflow(workflowId, {"messageIds": workflow["messageIds"]})
+
# Update status and increment round counter
workflow["status"] = "running"
workflow["lastActivity"] = currentTime
@@ -467,24 +465,25 @@ JSON_OUTPUT = {{
agentName = task.get("agent")
agentPrompt = task.get("prompt", "")
+ # Get agent from registry
+ agent = self.agentRegistry.getAgent(agentName)
+ if not agent:
+ logger.error(f"Agent '{agentName}' not found")
+ return []
+ agentLabel = agent.label
+
# Log the current step
outputLabels = []
for doc in task.get("outputDocuments", []):
outputLabels.append(doc.get("label", "unknown"))
- stepInfo = f"Agent '{agentName}' to create {', '.join(outputLabels)}."
+ stepInfo = f"Agent {agentLabel} to create {', '.join(outputLabels)}."
self.logAdd(workflow, stepInfo, level="info")
# Check if prompt is empty
if agentPrompt == "":
logger.warning("Empty prompt, no task to do")
return []
-
- # Get agent from registry
- agent = self.agentRegistry.getAgent(agentName)
- if not agent:
- logger.error(f"Agent '{agentName}' not found")
- return []
# Prepare output document specifications
outputSpecs = []
@@ -526,7 +525,7 @@ JSON_OUTPUT = {{
# Log the agent response
self.logAdd(
workflow,
- f"Agent '{agentName}' completed task. Feedback: {agentResults.get('feedback', 'No feedback provided')}",
+ f"Agent {agentLabel} completed task. Feedback: {agentResults.get('feedback', 'No feedback provided')}",
level="info"
)
@@ -537,14 +536,14 @@ JSON_OUTPUT = {{
}
# Create a message in the workflow with the agent's response
- agentMessage = await self.chatMessageToWorkflow("assistant", agentName, agentInputs, workflow)
+ agentMessage = await self.chatMessageToWorkflow("assistant", agent, agentInputs, workflow)
agentMessage["status"] = "step" # As per state machine specification
logger.debug(f"Agent result = {self.parseJson2text(agentMessage)}.")
return agentMessage.get("documents", [])
except Exception as e:
- errorMsg = f"Error executing agent '{agentName}': {str(e)}"
+ errorMsg = f"Error executing agent '{agentLabel}': {str(e)}"
logger.error(errorMsg, exc_info=True) # Add exc_info=True to get full traceback
self.logAdd(workflow, errorMsg, level="error")
return []
@@ -599,7 +598,7 @@ filesDelivered = {self.parseJson2text(matchingDocuments)}
logger.debug(f"FINAL PROMPT = {self.parseJson2text(finalPrompt)}.")
finalMessage = {
"role": "assistant",
- "agentName": "project_manager",
+ "agentName": "Project Manager",
"content": finalPrompt,
"documents": [] # DO NOT include the results documents, already with agents
}
@@ -668,7 +667,7 @@ filesDelivered = {self.parseJson2text(matchingDocuments)}
return f"[{role} {agentName}]: {contentSummary}{docsSummary}"
- async def chatMessageToWorkflow(self, role: str, agentName: str, chatMessage: Dict[str, Any], workflow: Dict[str, Any]) -> Dict[str, Any]:
+ async def chatMessageToWorkflow(self, role: str, agent: Dict[str, Any], chatMessage: Dict[str, Any], workflow: Dict[str, Any]) -> Dict[str, Any]:
"""
Integrates user inputs into a Message object including files with complete contents (State 3: User Message Processing).
@@ -681,6 +680,8 @@ filesDelivered = {self.parseJson2text(matchingDocuments)}
Returns:
Message object with content and documents including contents
"""
+ agentName = "" if agent is None else agent.name
+ agentLabel = "" if agent is None else agent.label
logger.info(f"Message from {role} {agentName} sent with {len(chatMessage.get('listFileId', []))} documents")
logger.debug(f"message = {self.parseJson2text(chatMessage)}.")
@@ -701,7 +702,7 @@ filesDelivered = {self.parseJson2text(matchingDocuments)}
# Create message object
messageObject = {
"role": role,
- "agentName": agentName,
+ "agentName": agentLabel,
"content": messageContent,
"documents": additionalFiles,
"status": chatMessage.get("status", "")
@@ -931,15 +932,8 @@ filesDelivered = {self.parseJson2text(matchingDocuments)}
try:
# For image content, use the specialized image analysis
- if contentType.startswith("image/") or content.get("metadata", {}).get("isImage", False):
- # analyzeImage handles base64 encoded data internally
- return await self.mydom.analyzeImage(data, contentType, image_prompt)
-
- # For binary data (base64Encoded but not an image), provide a generic description
- elif base64Encoded:
- metadata = content.get("metadata", {})
- format_type = metadata.get("format", "unknown")
- return f"Binary {format_type} data ({contentType})"
+ if base64Encoded:
+ return await self.mydom.callAi4Image(data, contentType, image_prompt)
# For text data, use the regular AI processing
else:
diff --git a/routes/routeUsers.py b/routes/routeUsers.py
index ed574274..d149125e 100644
--- a/routes/routeUsers.py
+++ b/routes/routeUsers.py
@@ -64,44 +64,64 @@ async def getUsers(currentUser: Dict[str, Any] = Depends(getCurrentActiveUser)):
else: # sysadmin
return context.interfaceData.getAllUsers()
+
@router.post("/register", response_model=Dict[str, Any])
async def registerUser(userData: dict = Body(...)):
"""Register a new user"""
- # Don't use user context for registration
- gateway = getGatewayInterface()
+ # Add debug logging to see what's coming in
+ import logging
+ logger = logging.getLogger(__name__)
+ logger.info(f"Registration request data: {userData}")
+
+ # Get the initial IDs for mandate and admin user
+ adminGateway = getGatewayInterface()
+
+ # Get ID of the root mandate - we'll use this for new users
+ rootMandateId = adminGateway.getInitialId("mandates")
+ adminUserId = adminGateway.getInitialId("users")
+
+ if not rootMandateId or not adminUserId:
+ raise HTTPException(
+ status_code=500,
+ detail="System is not properly initialized with root mandate and admin user"
+ )
+
+ # Use a gateway with admin context for user creation
+ gateway = getGatewayInterface(rootMandateId, adminUserId)
if "username" not in userData or "password" not in userData:
raise HTTPException(status_code=400, detail="Username and password required")
try:
- # Prepare mandate data
- mandateData = {
- "name": f"Mandate of {userData['username']}",
+ # Create user data - explicitly set fields
+ userCreateData = {
+ "username": userData["username"],
+ "password": userData["password"],
+ "mandateId": rootMandateId, # Use the Root mandate
+ "disabled": False,
+ "privilege": "user",
"language": userData.get("language", "de")
}
- newMandate = gateway.createMandate(**mandateData)
-
- # Filter user attributes from the request
- userCreateData = {}
- for attr in userAttributes:
- if attr in userData and attr != "id": # ID is auto-assigned
- userCreateData[attr] = userData[attr]
-
- # Required fields
- userCreateData["username"] = userData["username"]
- userCreateData["password"] = userData["password"]
- userCreateData["mandateId"] = newMandate["id"]
-
- # Default values for optional fields
- userCreateData.setdefault("disabled", False)
- userCreateData.setdefault("privilege", "user")
- userCreateData.setdefault("language", "de")
-
+ # Explicitly add optional fields - only if they exist and are not empty
+ if "email" in userData and userData["email"]:
+ userCreateData["email"] = userData["email"]
+
+ if "fullName" in userData and userData["fullName"]:
+ userCreateData["fullName"] = userData["fullName"]
newUser = gateway.createUser(**userCreateData)
return newUser
except ValueError as e:
+ logger.error(f"ValueError in registration: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
+ except PermissionError as e:
+ logger.error(f"PermissionError in registration: {str(e)}")
+ raise HTTPException(status_code=403, detail=str(e))
+ except Exception as e:
+ import traceback
+ logger.error(f"Unexpected error in registration: {str(e)}")
+ logger.error(traceback.format_exc())
+ raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}")
@router.get("/{userId}", response_model=Dict[str, Any])
async def getUser(
diff --git a/static/1_LF-Details.png b/static/1_LF-Details.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a2be57d619cb66e70f76081929e723e874ffcad
GIT binary patch
literal 253009
zcmeFZcT`hb_bwb11rbC=R8T=sEFeXS^di{kk=_yMAVqo&C;|#9sB|eoDIpLLAs}7h
zC`t)P??pb8H+bH2e1G@5|9#&WcZ|CSB7~i_*P3gVXFhYTJVL8nr=ww}L7`A|
z@;9!ip-?ndC={g+^=^3OMaiQQ_>aO_?Yb-~n{<2(exbCytb7@T$_?AQX0i)@-{W{g
z*BOP{SB3nesCK}a!5f0DHFR8bl$FFx9qf2a%p7i;^LW@f!rds8gtUjFiK(r*%gNj3
zme%%?XJ<+(&YrY3lRT>}q|B%6C}(bEeZ$MiT-{4W!_>>xRMhOOv=kkUgohZ6U}x@P
za?-=@j=i&(ha~&fz+&(-@-i>Gl!TL+g_zovYrpS+-z3?sTwEN*czNC3-Fe&vcpRK8
zc`u5Jit_UD^YZg^!yVktp7t ^CfMr+yD`#oXD{$=cDy+QI%LGN8$A2UizKc2`$x
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