methods done

This commit is contained in:
ValueOn AG 2025-06-13 00:41:51 +02:00
parent eebd995d64
commit 03c17d7506
43 changed files with 1761 additions and 2522 deletions

2
app.py
View file

@ -105,7 +105,7 @@ async def lifespan(app: FastAPI):
logger.info("Application is starting up")
# Initialize root interface to ensure database is properly set up
from modules.interfaces.serviceAppClass import getRootInterface
from modules.interfaces.interfaceAppObjects import getRootInterface
getRootInterface()
yield

View file

@ -17,7 +17,7 @@ def loadConfigData():
"maxTokens": int(APP_CONFIG.get('Connector_AiAnthropic_MAX_TOKENS'))
}
class ChatService:
class AiAnthropic:
"""Connector for communication with the Anthropic API."""
def __init__(self):
@ -39,7 +39,7 @@ class ChatService:
logger.info(f"Anthropic Connector initialized with model: {self.modelName}")
async def callApi(self, messages: List[Dict[str, Any]], temperature: float = None, maxTokens: int = None) -> Dict[str, Any]:
async def callAiBasic(self, messages: List[Dict[str, Any]], temperature: float = None, maxTokens: int = None) -> Dict[str, Any]:
"""
Calls the Anthropic API with the given messages.
@ -49,15 +49,12 @@ class ChatService:
maxTokens: Maximum number of tokens in the response
Returns:
The response converted to OpenAI format
The response in OpenAI format
Raises:
HTTPException: For errors in API communication
"""
try:
# Convert OpenAI format to Anthropic format
formattedMessages = self._convertToAnthropicFormat(messages)
# Use parameters from configuration if none were overridden
if temperature is None:
temperature = self.config.get("temperature", 0.2)
@ -68,7 +65,7 @@ class ChatService:
# Create Anthropic API payload
payload = {
"model": self.modelName,
"messages": formattedMessages,
"messages": messages,
"temperature": temperature,
"max_tokens": maxTokens
}
@ -82,110 +79,44 @@ class ChatService:
logger.error(f"Anthropic API error: {response.status_code} - {response.text}")
raise HTTPException(status_code=500, detail="Error communicating with Anthropic API")
# Convert response from Anthropic format to OpenAI format
# Parse response
anthropicResponse = response.json()
openaiFormattedResponse = self._convertToOpenaiFormat(anthropicResponse)
return openaiFormattedResponse
# Extract content from response
content = ""
if "content" in anthropicResponse:
if isinstance(anthropicResponse["content"], list):
# Content is a list of parts (in newer API versions)
for part in anthropicResponse["content"]:
if part.get("type") == "text":
content += part.get("text", "")
else:
# Direct content as string (in older API versions)
content = anthropicResponse["content"]
# Return in OpenAI format
return {
"id": anthropicResponse.get("id", ""),
"object": "chat.completion",
"created": anthropicResponse.get("created", 0),
"model": anthropicResponse.get("model", self.modelName),
"choices": [
{
"message": {
"role": "assistant",
"content": content
},
"index": 0,
"finish_reason": "stop"
}
]
}
except Exception as e:
logger.error(f"Error calling Anthropic API: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error calling Anthropic API: {str(e)}")
def _convertToAnthropicFormat(self, openaiMessages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Converts messages from OpenAI format to Anthropic format.
OpenAI uses:
[{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}]
Anthropic uses:
[{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}]
Note: Anthropic has no direct system message equivalent,
so we add system messages to the first user message.
"""
anthropicMessages = []
systemContent = ""
# First extract all system messages
for msg in openaiMessages:
if msg.get("role") == "system":
systemContent += msg.get("content", "") + "\n\n"
# Convert the remaining messages
for msg in openaiMessages:
role = msg.get("role")
content = msg.get("content", "")
# Skip system messages (already extracted)
if role == "system":
continue
# For the first user message: prepend system content if available
if role == "user" and systemContent and not any(m.get("role") == "user" for m in anthropicMessages):
if isinstance(content, str):
content = systemContent + content
elif isinstance(content, list):
# If content is an array (for multimodal messages)
textParts = []
for part in content:
if part.get("type") == "text":
textParts.append(part)
if textParts:
# Create a new text part with combined content
textParts[0] = {
"type": "text",
"text": systemContent + textParts[0].get("text", "")
}
# Anthropic only supports "user" and "assistant" roles
if role not in ["user", "assistant"]:
role = "user"
anthropicMessages.append({"role": role, "content": content})
return anthropicMessages
def _convertToOpenaiFormat(self, anthropicResponse: Dict[str, Any]) -> Dict[str, Any]:
"""
Converts a response from Anthropic format to OpenAI format.
"""
# Extract content from Anthropic response
content = ""
if "content" in anthropicResponse:
if isinstance(anthropicResponse["content"], list):
# Content is a list of parts (in newer API versions)
for part in anthropicResponse["content"]:
if part.get("type") == "text":
content += part.get("text", "")
else:
# Direct content as string (in older API versions)
content = anthropicResponse["content"]
# Create OpenAI-formatted response
return {
"id": anthropicResponse.get("id", ""),
"object": "chat.completion",
"created": anthropicResponse.get("created", 0),
"model": anthropicResponse.get("model", self.modelName),
"choices": [
{
"message": {
"role": "assistant",
"content": content
},
"index": 0,
"finish_reason": "stop"
}
]
}
async def analyzeImage(self, imageData: Union[str, bytes], mimeType: str = None, prompt: str = "Describe this image") -> str:
async def callAiImage(self, prompt: str, imageData: Union[str, bytes], mimeType: str = None) -> str:
"""
Analyzes an image using Anthropic's vision capabilities.

View file

@ -18,7 +18,7 @@ def loadConfigData():
"maxTokens": int(APP_CONFIG.get('Connector_AiOpenai_MAX_TOKENS'))
}
class AiConnector:
class AiOpenai:
"""Connector for communication with the OpenAI API."""
def __init__(self):
@ -85,7 +85,7 @@ class AiConnector:
logger.error(f"Error calling OpenAI API: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error calling OpenAI API: {str(e)}")
async def callAiImage(self, imageData: Union[str, bytes], mimeType: str = None, prompt: str = "Describe this image") -> str:
async def callAiImage(self, prompt: str, imageData: Union[str, bytes], mimeType: str = None) -> str:
"""
Analyzes an image with the OpenAI Vision API.

View file

@ -1,38 +0,0 @@
import logging
from typing import Dict, Any, List, Union
from modules.connectors.connectorAiOpenai import AiConnector
logger = logging.getLogger(__name__)
class AiInterface:
"""Interface for AI service interactions"""
def __init__(self):
self.service = AiConnector()
async def callAiBasic(self, messages: List[Dict[str, str]], produceUserAnswer: bool = False, temperature: float = None) -> str:
"""Enhanced AI service call with language support."""
# Add language instruction for user-facing responses
if produceUserAnswer and hasattr(self, 'userLanguage') and self.userLanguage:
ltext = f"Please respond in '{self.userLanguage}' language."
if messages and messages[0]["role"] == "system":
if "language" not in messages[0]["content"].lower():
messages[0]["content"] = f"{ltext} {messages[0]['content']}"
else:
# Insert a system message with language instruction
messages.insert(0, {
"role": "system",
"content": ltext
})
# Call the AI service
return await self.service.callAiBasic(messages, temperature=temperature)
async def callAiImage(self, imageData: Union[str, bytes], mimeType: str = None, prompt: str = "Describe this image") -> str:
"""Enhanced AI service call with language support."""
if not self.service:
logger.error("AI service not set in AiInterface")
return "Error: AI service not available"
return await self.service.callAiImage(imageData, mimeType, prompt)

View file

@ -0,0 +1,141 @@
import logging
from typing import Dict, Any, List, Union, Optional
from modules.connectors.connectorAiOpenai import AiOpenai
from modules.connectors.connectorAiAnthropic import AiAnthropic
logger = logging.getLogger(__name__)
class AiCalls:
"""Interface for AI service interactions"""
def __init__(self):
self.openaiService = AiOpenai()
self.anthropicService = AiAnthropic()
async def callAiTextBasic(self, prompt: str, context: Optional[str] = None) -> str:
"""
Basic text processing using OpenAI.
Args:
prompt: The user prompt to process
context: Optional system context/prompt
Returns:
The AI response as text
"""
# Prepare messages in OpenAI format
messages = []
# Add system message if context provided
if context:
messages.append({
"role": "system",
"content": context
})
# Add user message
messages.append({
"role": "user",
"content": prompt
})
# Add language instruction for user-facing responses
if hasattr(self, 'userLanguage') and self.userLanguage:
ltext = f"Please respond in '{self.userLanguage}' language."
if messages and messages[0]["role"] == "system":
if "language" not in messages[0]["content"].lower():
messages[0]["content"] = f"{ltext} {messages[0]['content']}"
else:
messages.insert(0, {
"role": "system",
"content": ltext
})
try:
return await self.openaiService.callAiBasic(messages)
except Exception as e:
logger.error(f"Error in OpenAI call: {str(e)}")
return f"Error: {str(e)}"
async def callAiTextAdvanced(self, prompt: str, context: Optional[str] = None) -> str:
"""
Advanced text processing using Anthropic.
Args:
prompt: The user prompt to process
context: Optional system context/prompt
Returns:
The AI response as text
"""
# Prepare messages in OpenAI format
messages = []
# Add system message if context provided
if context:
messages.append({
"role": "system",
"content": context
})
# Add user message
messages.append({
"role": "user",
"content": prompt
})
# Add language instruction for user-facing responses
if hasattr(self, 'userLanguage') and self.userLanguage:
ltext = f"Please respond in '{self.userLanguage}' language."
if messages and messages[0]["role"] == "system":
if "language" not in messages[0]["content"].lower():
messages[0]["content"] = f"{ltext} {messages[0]['content']}"
else:
messages.insert(0, {
"role": "system",
"content": ltext
})
try:
response = await self.anthropicService.callAiBasic(messages)
return response["choices"][0]["message"]["content"]
except Exception as e:
logger.error(f"Error in Anthropic call: {str(e)}")
return f"Error: {str(e)}"
async def callAiImageBasic(self, prompt: str, imageData: Union[str, bytes], mimeType: str = None) -> str:
"""
Basic image processing using OpenAI.
Args:
prompt: The prompt for image analysis
imageData: The image data (file path or bytes)
mimeType: Optional MIME type of the image
Returns:
The AI response as text
"""
try:
return await self.openaiService.callAiImage(imageData, mimeType, prompt)
except Exception as e:
logger.error(f"Error in OpenAI image call: {str(e)}")
return f"Error: {str(e)}"
async def callAiImageAdvanced(self, prompt: str, imageData: Union[str, bytes], mimeType: str = None) -> str:
"""
Advanced image processing using Anthropic.
Args:
prompt: The prompt for image analysis
imageData: The image data (file path or bytes)
mimeType: Optional MIME type of the image
Returns:
The AI response as text
"""
try:
return await self.anthropicService.callAiImage(prompt, imageData, mimeType)
except Exception as e:
logger.error(f"Error in Anthropic image call: {str(e)}")
return f"Error: {str(e)}"

View file

@ -5,7 +5,7 @@ Access control for the Application.
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.serviceAppModel import UserPrivilege, Session, User
from modules.interfaces.interfaceAppModel import UserPrivilege, Session, User
# Configure logger
logger = logging.getLogger(__name__)

View file

@ -14,8 +14,8 @@ import uuid
from modules.connectors.connectorDbJson import DatabaseConnector
from modules.shared.configuration import APP_CONFIG
from modules.interfaces.serviceAppAccess import AppAccess
from modules.interfaces.serviceAppModel import (
from modules.interfaces.interfaceAppAccess import AppAccess
from modules.interfaces.interfaceAppModel import (
User, Mandate, UserInDB, UserConnection,
Session, AuthEvent, AuthAuthority, UserPrivilege,
ConnectionStatus, Token, LocalToken, GoogleToken, MsftToken
@ -24,16 +24,16 @@ from modules.shared.attributeUtils import ModelMixin
logger = logging.getLogger(__name__)
# Singleton factory for GatewayInterface instances per context
# Singleton factory for AppObjects instances per context
_gatewayInterfaces = {}
# Root interface instance
_rootGatewayInterface = None
_rootAppObjects = None
# Password-Hashing
pwdContext = CryptContext(schemes=["argon2"], deprecated="auto")
class GatewayInterface:
class AppObjects:
"""
Interface to the Gateway system.
Manages users and mandates.
@ -793,9 +793,9 @@ class GatewayInterface:
# Public Methods
def getInterface(currentUser: User) -> GatewayInterface:
def getInterface(currentUser: User) -> AppObjects:
"""
Returns a GatewayInterface instance for the current user.
Returns a AppObjects instance for the current user.
Handles initialization of database and records.
"""
if not currentUser:
@ -806,7 +806,7 @@ def getInterface(currentUser: User) -> GatewayInterface:
# Create new instance if not exists
if contextKey not in _gatewayInterfaces:
_gatewayInterfaces[contextKey] = GatewayInterface(currentUser)
_gatewayInterfaces[contextKey] = AppObjects(currentUser)
return _gatewayInterfaces[contextKey]
@ -817,7 +817,7 @@ def getRootUser() -> User:
"""
try:
# Create a temporary interface without user context
tempInterface = GatewayInterface()
tempInterface = AppObjects()
# Get the initial user directly
initialUserId = tempInterface.db.getInitialId("users")
@ -835,15 +835,15 @@ def getRootUser() -> User:
logger.error(f"Error getting root user: {str(e)}")
raise ValueError(f"Failed to get root user: {str(e)}")
def getRootInterface() -> GatewayInterface:
def getRootInterface() -> AppObjects:
"""
Returns a GatewayInterface instance with root privileges.
Returns a AppObjects instance with root privileges.
This is used for initial setup and user creation.
"""
global _rootGatewayInterface
global _rootAppObjects
if _rootGatewayInterface is None:
if _rootAppObjects is None:
rootUser = getRootUser()
_rootGatewayInterface = GatewayInterface(rootUser)
_rootAppObjects = AppObjects(rootUser)
return _rootGatewayInterface
return _rootAppObjects

View file

@ -4,7 +4,7 @@ Handles user access management and permission checks.
"""
from typing import Dict, Any, List, Optional
from modules.interfaces.serviceAppModel import User, UserPrivilege
from modules.interfaces.interfaceAppModel import User, UserPrivilege
class ChatAccess:
"""

View file

@ -12,11 +12,11 @@ from typing import Dict, Any, List, Optional, Union
import hashlib
import asyncio
from modules.interfaces.serviceChatAccess import ChatAccess
from modules.interfaces.serviceChatModel import (
from modules.interfaces.interfaceChatAccess import ChatAccess
from modules.interfaces.interfaceChatModel import (
TaskStatus, UserInputRequest, ChatDocument, TaskItem, ChatStat, ChatLog, ChatMessage, ChatWorkflow, TaskAction
)
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
# DYNAMIC PART: Connectors to the Interface
from modules.connectors.connectorDbJson import DatabaseConnector
@ -28,7 +28,7 @@ logger = logging.getLogger(__name__)
# Singleton factory for Chat instances
_chatInterfaces = {}
class ChatInterface:
class ChatObjects:
"""
Interface to Chat database and AI Connectors.
Uses the JSON connector for data access with added language support.
@ -1025,9 +1025,9 @@ class ChatInterface:
logger.error(f"Error deleting task: {str(e)}")
return False
def getInterface(currentUser: Optional[User] = None) -> 'ChatInterface':
def getInterface(currentUser: Optional[User] = None) -> 'ChatObjects':
"""
Returns a ChatInterface instance for the current user.
Returns a ChatObjects instance for the current user.
Handles initialization of database and records.
"""
if not currentUser:
@ -1038,6 +1038,6 @@ def getInterface(currentUser: Optional[User] = None) -> 'ChatInterface':
# Create new instance if not exists
if contextKey not in _chatInterfaces:
_chatInterfaces[contextKey] = ChatInterface(currentUser)
_chatInterfaces[contextKey] = ChatObjects(currentUser)
return _chatInterfaces[contextKey]

View file

@ -5,12 +5,12 @@ Handles user access management and permission checks.
import logging
from typing import Dict, Any, List, Optional
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
# Configure logger
logger = logging.getLogger(__name__)
class ManagementAccess:
class ComponentAccess:
"""
Access control class for Management interface.
Handles user access management and permission checks.

View file

@ -11,11 +11,11 @@ from typing import Dict, Any, List, Optional, Union
import hashlib
from modules.interfaces.serviceManagementAccess import ManagementAccess
from modules.interfaces.serviceManagementModel import (
from modules.interfaces.interfaceComponentAccess import ComponentAccess
from modules.interfaces.interfaceComponentModel import (
FilePreview, Prompt, FileItem, FileData
)
from modules.interfaces.serviceAppModel import User, Mandate, UserPrivilege
from modules.interfaces.interfaceAppModel import User, Mandate, UserPrivilege
# DYNAMIC PART: Connectors to the Interface
from modules.connectors.connectorDbJson import DatabaseConnector
@ -49,7 +49,7 @@ class FileDeletionError(FileError):
"""Exception raised when there's an error deleting a file."""
pass
class ServiceManagement:
class ComponentObjects:
"""
Interface to Management database and AI Connectors.
Uses the JSON connector for data access with added language support.
@ -60,7 +60,7 @@ class ServiceManagement:
# Initialize variables first
self.currentUser: Optional[User] = None
self.userId: Optional[str] = None
self.access: Optional[ManagementAccess] = None # Will be set when user context is provided
self.access: Optional[ComponentAccess] = None # Will be set when user context is provided
self.aiService: Optional[ChatService] = None # Will be set when user context is provided
# Initialize database
@ -85,7 +85,7 @@ class ServiceManagement:
self.userLanguage = currentUser.language # Default user language
# Initialize access control with user context
self.access = ManagementAccess(self.currentUser, self.db)
self.access = ComponentAccess(self.currentUser, self.db)
# Initialize AI service
self.aiService = ChatService()
@ -143,7 +143,7 @@ class ServiceManagement:
return
# Get the root interface to access the initial mandate ID
from modules.interfaces.serviceAppClass import getRootInterface
from modules.interfaces.interfaceAppObjects import getRootInterface
rootInterface = getRootInterface()
# Get initial mandate ID through the root interface
@ -887,15 +887,15 @@ class ServiceManagement:
raise FileError(f"Error downloading file: {str(e)}")
def getInterface(currentUser: Optional[User] = None) -> 'ServiceManagement':
def getInterface(currentUser: Optional[User] = None) -> 'ComponentObjects':
"""
Returns a ServiceManagement instance.
Returns a ComponentObjects instance.
If currentUser is provided, initializes with user context.
Otherwise, returns an instance with only database access.
"""
# Create new instance if not exists
if "default" not in _instancesManagement:
_instancesManagement["default"] = ServiceManagement()
_instancesManagement["default"] = ComponentObjects()
interface = _instancesManagement["default"]

View file

@ -3,10 +3,19 @@ from typing import Dict, List, Optional, Any, Literal
from datetime import datetime, UTC
from pydantic import BaseModel, Field
import logging
from modules.interfaces.serviceChatModel import MethodResult
from modules.interfaces.interfaceChatModel import MethodResult
from functools import wraps
logger = logging.getLogger(__name__)
def action(func):
"""Decorator to mark a method as an available action"""
@wraps(func)
async def wrapper(self, *args, **kwargs):
return await func(self, *args, **kwargs)
wrapper.is_action = True
return wrapper
class MethodBase:
"""Base class for all methods"""

View file

@ -3,85 +3,20 @@ import logging
import ast
import re
from modules.methods.methodBase import MethodBase, MethodResult
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
class MethodCoder(MethodBase):
"""Coder method implementation for code operations"""
def __init__(self):
super().__init__()
def __init__(self, serviceContainer: Any):
super().__init__(serviceContainer)
self.name = "coder"
self.description = "Handle code operations like analysis, generation, and refactoring"
@property
def actions(self) -> Dict[str, Dict[str, Any]]:
"""Available actions and their parameters"""
return {
"analyze": {
"description": "Analyze code structure and quality",
"retryMax": 2,
"timeout": 30,
"parameters": {
"code": {"type": "string", "required": True},
"language": {"type": "string", "required": False},
"metrics": {"type": "array", "items": "string", "required": False}
}
},
"generate": {
"description": "Generate code based on requirements",
"retryMax": 2,
"timeout": 60,
"parameters": {
"requirements": {"type": "string", "required": True},
"language": {"type": "string", "required": False},
"style": {"type": "string", "required": False}
}
},
"refactor": {
"description": "Refactor code for better quality",
"retryMax": 2,
"timeout": 60,
"parameters": {
"code": {"type": "string", "required": True},
"language": {"type": "string", "required": False},
"improvements": {"type": "array", "items": "string", "required": False}
}
}
}
async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Execute coder method"""
try:
# Validate parameters
if not await self.validateParameters(action, parameters):
return self._createResult(
success=False,
data={"error": f"Invalid parameters for {action}"}
)
# Execute action
if action == "analyze":
return await self._analyzeCode(parameters)
elif action == "generate":
return await self._generateCode(parameters)
elif action == "refactor":
return await self._refactorCode(parameters)
else:
return self._createResult(
success=False,
data={"error": f"Unknown action: {action}"}
)
except Exception as e:
logger.error(f"Error executing coder {action}: {e}")
return self._createResult(
success=False,
data={"error": str(e)}
)
async def _analyzeCode(self, parameters: Dict[str, Any]) -> MethodResult:
@action
async def analyze(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Analyze code structure and quality"""
try:
code = parameters["code"]
@ -173,7 +108,8 @@ class MethodCoder(MethodBase):
data={"error": f"Analysis failed: {str(e)}"}
)
async def _generateCode(self, parameters: Dict[str, Any]) -> MethodResult:
@action
async def generate(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Generate code based on requirements"""
try:
requirements = parameters["requirements"]
@ -216,7 +152,8 @@ class MethodCoder(MethodBase):
data={"error": f"Generation failed: {str(e)}"}
)
async def _refactorCode(self, parameters: Dict[str, Any]) -> MethodResult:
@action
async def refactor(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Refactor code for better quality"""
try:
code = parameters["code"]

View file

@ -7,14 +7,14 @@ import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.serviceChatModel import (
from modules.interfaces.interfaceChatModel import (
ChatDocument,
TaskDocument,
ExtractedContent,
ContentItem
)
from modules.workflow.managerDocument import DocumentManager
from modules.methods.methodBase import MethodBase
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
@ -25,100 +25,80 @@ class MethodDocument(MethodBase):
"""Initialize the document method"""
super().__init__(serviceContainer)
self.documentManager = DocumentManager(serviceContainer)
async def process(self, action: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
@action
async def extract(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Process document operations
Extract content from document
Args:
action: The action to perform
parameters: Action parameters
Returns:
Dictionary containing the operation result
Raises:
ValueError: If action is not supported
parameters:
documentId: ID of the document to extract from
documentType: Type of document
extractionType: Type of extraction to perform
"""
try:
if action == "extract":
return await self._extractContent(parameters)
elif action == "analyze":
return await self._analyzeDocument(parameters)
elif action == "summarize":
return await self._summarizeDocument(parameters)
documentId = parameters["documentId"]
documentType = parameters.get("documentType", "text")
extractionType = parameters.get("extractionType", "full")
# Get document from service
document = await self.service.interfaceComponent.getDocument(documentId)
if not document:
return self._createResult(
success=False,
data={"error": f"Document not found: {documentId}"}
)
# Extract content based on type
if documentType == "text":
content = await self.documentManager.extractTextContent(document, extractionType)
elif documentType == "table":
content = await self.documentManager.extractTableContent(document, extractionType)
elif documentType == "image":
content = await self.documentManager.extractImageContent(document, extractionType)
else:
raise ValueError(f"Unsupported action: {action}")
except Exception as e:
logger.error(f"Error processing document action {action}: {str(e)}")
raise
async def _extractContent(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract content from a document
Args:
parameters: Dictionary containing:
- documentId: ID of the document to process
- documentType: Type of document ('ChatDocument' or 'TaskDocument')
Returns:
Dictionary containing extracted content
"""
try:
documentId = parameters.get("documentId")
documentType = parameters.get("documentType", "ChatDocument")
return self._createResult(
success=False,
data={"error": f"Unsupported document type: {documentType}"}
)
if not documentId:
raise ValueError("documentId is required")
# Get document from database
if documentType == "ChatDocument":
document = await self._getChatDocument(documentId)
if not document:
raise ValueError(f"ChatDocument {documentId} not found")
extracted = await self.documentManager.extractFromChatDocument(document)
else:
document = await self._getTaskDocument(documentId)
if not document:
raise ValueError(f"TaskDocument {documentId} not found")
extracted = await self.documentManager.extractFromTaskDocument(document)
return {
"success": True,
"content": extracted.dict(),
"metadata": await self.documentManager.getDocumentMetadata(document)
}
return self._createResult(
success=True,
data={
"documentId": documentId,
"type": documentType,
"content": content
}
)
except Exception as e:
logger.error(f"Error extracting content: {str(e)}")
return {
"success": False,
"error": str(e)
}
async def _analyzeDocument(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def analyze(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Analyze document content
Args:
parameters: Dictionary containing:
- documentId: ID of the document to analyze
- documentType: Type of document
- analysisType: Type of analysis to perform
Returns:
Dictionary containing analysis results
parameters:
documentId: ID of the document to analyze
documentType: Type of document
analysisType: Type of analysis to perform
"""
try:
# Extract content first
contentResult = await self._extractContent(parameters)
if not contentResult["success"]:
contentResult = await self.extract(parameters)
if not contentResult.success:
return contentResult
# Perform analysis based on type
analysisType = parameters.get("analysisType", "basic")
content = ExtractedContent(**contentResult["content"])
content = ExtractedContent(**contentResult.data["content"])
if analysisType == "basic":
# Basic analysis: count items, calculate statistics
@ -134,64 +114,71 @@ class MethodDocument(MethodBase):
stats["itemTypes"][itemType] = 0
stats["itemTypes"][itemType] += 1
return {
"success": True,
"analysis": stats
}
return self._createResult(
success=True,
data={
"documentId": parameters["documentId"],
"analysis": stats
}
)
else:
raise ValueError(f"Unsupported analysis type: {analysisType}")
return self._createResult(
success=False,
data={"error": f"Unsupported analysis type: {analysisType}"}
)
except Exception as e:
logger.error(f"Error analyzing document: {str(e)}")
return {
"success": False,
"error": str(e)
}
async def _summarizeDocument(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def summarize(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Generate document summary
Summarize document content
Args:
parameters: Dictionary containing:
- documentId: ID of the document to summarize
- documentType: Type of document
- summaryType: Type of summary to generate
Returns:
Dictionary containing summary
parameters:
documentId: ID of the document to summarize
documentType: Type of document
summaryType: Type of summary to generate
"""
try:
# Extract content first
contentResult = await self._extractContent(parameters)
if not contentResult["success"]:
contentResult = await self.extract(parameters)
if not contentResult.success:
return contentResult
# Generate summary based on type
summaryType = parameters.get("summaryType", "basic")
content = ExtractedContent(**contentResult["content"])
content = ExtractedContent(**contentResult.data["content"])
if summaryType == "basic":
# Basic summary: concatenate all text content
summary = "\n".join(
item.data for item in content.contents
if item.label == "main"
)
summary = "\n".join(item.content for item in content.contents if item.content)
return {
"success": True,
"summary": summary
}
return self._createResult(
success=True,
data={
"documentId": parameters["documentId"],
"summary": summary
}
)
else:
raise ValueError(f"Unsupported summary type: {summaryType}")
return self._createResult(
success=False,
data={"error": f"Unsupported summary type: {summaryType}"}
)
except Exception as e:
logger.error(f"Error summarizing document: {str(e)}")
return {
"success": False,
"error": str(e)
}
return self._createResult(
success=False,
data={"error": str(e)}
)
async def _getChatDocument(self, documentId: str) -> Optional[ChatDocument]:
"""Get ChatDocument from database"""
try:

View file

@ -0,0 +1,188 @@
"""
Excel method module.
Handles Excel operations using the Excel service.
"""
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.interfaceExcel import ExcelService
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
class MethodExcel(MethodBase):
"""Excel method implementation"""
def __init__(self, serviceContainer):
"""Initialize the Excel method"""
super().__init__(serviceContainer)
self.excelService = ExcelService(serviceContainer)
@action
async def read(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Read data from Excel file
Args:
parameters:
fileId: ID of the Excel file
sheetName: Name of the sheet to read
range: Cell range to read (e.g. "A1:B10")
"""
try:
fileId = parameters["fileId"]
sheetName = parameters.get("sheetName", "Sheet1")
range = parameters.get("range")
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"File not found: {fileId}"}
)
# Read data from Excel
data = await self.excelService.readData(file, sheetName, range)
return self._createResult(
success=True,
data={
"fileId": fileId,
"sheetName": sheetName,
"range": range,
"data": data
}
)
except Exception as e:
logger.error(f"Error reading Excel file: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def write(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Write data to Excel file
Args:
parameters:
fileId: ID of the Excel file
sheetName: Name of the sheet to write to
range: Cell range to write to (e.g. "A1:B10")
data: Data to write
"""
try:
fileId = parameters["fileId"]
sheetName = parameters.get("sheetName", "Sheet1")
range = parameters.get("range")
data = parameters["data"]
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"File not found: {fileId}"}
)
# Write data to Excel
await self.excelService.writeData(file, sheetName, range, data)
return self._createResult(
success=True,
data={
"fileId": fileId,
"sheetName": sheetName,
"range": range
}
)
except Exception as e:
logger.error(f"Error writing to Excel file: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def create(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Create new Excel file
Args:
parameters:
fileName: Name of the new file
sheets: List of sheet configurations
"""
try:
fileName = parameters["fileName"]
sheets = parameters.get("sheets", [{"name": "Sheet1"}])
# Create new Excel file
file = await self.excelService.createFile(fileName, sheets)
return self._createResult(
success=True,
data={
"fileId": file.id,
"fileName": fileName,
"sheets": sheets
}
)
except Exception as e:
logger.error(f"Error creating Excel file: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def format(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Format Excel cells
Args:
parameters:
fileId: ID of the Excel file
sheetName: Name of the sheet to format
range: Cell range to format (e.g. "A1:B10")
format: Format configuration
"""
try:
fileId = parameters["fileId"]
sheetName = parameters.get("sheetName", "Sheet1")
range = parameters.get("range")
format = parameters["format"]
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"File not found: {fileId}"}
)
# Apply formatting
await self.excelService.formatCells(file, sheetName, range, format)
return self._createResult(
success=True,
data={
"fileId": fileId,
"sheetName": sheetName,
"range": range
}
)
except Exception as e:
logger.error(f"Error formatting Excel cells: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)

View file

@ -2,7 +2,7 @@ from typing import Dict, List, Any, Optional
from datetime import datetime, UTC
import logging
from .methodBase import MethodBase
from modules.interfaces.serviceChatModel import MethodResult
from modules.interfaces.interfaceChatModel import MethodResult
logger = logging.getLogger(__name__)
@ -159,7 +159,7 @@ class MethodOperator(MethodBase):
full_prompt += f"\nDocument: {content['document']}\n{content['content']}\n"
# Call AI service
response = await self.service.callAiBasic(full_prompt)
response = await self.service.callAiTextBasic(full_prompt)
return self._createResult(
success=True,

View file

@ -1,202 +1,176 @@
from typing import Dict, Any, Optional
import logging
from datetime import datetime, UTC
from O365 import Account, MSGraphProtocol
"""
Outlook method module.
Handles Outlook operations using the Outlook service.
"""
from modules.methods.methodBase import MethodBase, MethodResult
from modules.models.userConnection import UserConnection
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.interfaceOutlook import OutlookService
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
class MethodOutlook(MethodBase):
"""Outlook method implementation for email operations"""
"""Outlook method implementation"""
def __init__(self):
super().__init__()
self.name = "outlook"
self.description = "Handle Outlook email operations like reading and sending emails"
def __init__(self, serviceContainer):
"""Initialize the Outlook method"""
super().__init__(serviceContainer)
self.outlookService = OutlookService(serviceContainer)
@action
async def readMails(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Read emails from Outlook
@property
def actions(self) -> Dict[str, Dict[str, Any]]:
"""Available actions and their parameters"""
return {
"readMails": {
"description": "Read emails from Outlook",
"retryMax": 2,
"timeout": 30,
"parameters": {
"folder": {"type": "string", "required": False},
"query": {"type": "string", "required": False},
"maxResults": {"type": "number", "required": False},
"includeAttachments": {"type": "boolean", "required": False}
}
},
"sendMail": {
"description": "Send email through Outlook",
"retryMax": 2,
"timeout": 30,
"parameters": {
"to": {"type": "array", "items": "string", "required": True},
"subject": {"type": "string", "required": True},
"body": {"type": "string", "required": True},
"cc": {"type": "array", "items": "string", "required": False},
"bcc": {"type": "array", "items": "string", "required": False},
"attachments": {"type": "array", "items": "string", "required": False}
}
}
}
async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Execute Outlook method"""
try:
# Validate parameters
if not await self.validateParameters(action, parameters):
return self._createResult(
success=False,
data={"error": f"Invalid parameters for {action}"}
)
# Get UserConnection from auth_data
if not authData or "userConnection" not in authData:
return self._createResult(
success=False,
data={"error": "UserConnection required for Outlook operations"}
)
userConnection: UserConnection = authData["userConnection"]
# Execute action
if action == "readMails":
return await self._readMails(parameters, userConnection)
elif action == "sendMail":
return await self._sendMail(parameters, userConnection)
else:
return self._createResult(
success=False,
data={"error": f"Unknown action: {action}"}
)
except Exception as e:
logger.error(f"Error executing Outlook {action}: {e}")
return self._createResult(
success=False,
data={"error": str(e)}
)
async def _readMails(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Read emails from Outlook"""
Args:
parameters:
folder: Folder to read from (default: inbox)
query: Search query
maxResults: Maximum number of results
includeAttachments: Whether to include attachments
"""
try:
folder = parameters.get("folder", "inbox")
query = parameters.get("query")
maxResults = parameters.get("maxResults", 10)
includeAttachments = parameters.get("includeAttachments", False)
# Create Outlook account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
# Read emails
emails = await self.outlookService.readEmails(
folder=folder,
query=query,
maxResults=maxResults,
includeAttachments=includeAttachments
)
# Get mailbox
mailbox = account.mailbox()
# Get folder
targetFolder = mailbox.folder(folder_name=folder)
# Get messages
if query:
messages = targetFolder.get_messages(query=query, limit=maxResults)
else:
messages = targetFolder.get_messages(limit=maxResults)
# Process messages
results = []
for message in messages:
msgData = {
"id": message.object_id,
"subject": message.subject,
"from": message.sender.address,
"to": [to.address for to in message.to],
"cc": [cc.address for cc in message.cc],
"received": message.received.strftime("%Y-%m-%d %H:%M:%S"),
"body": message.body,
"hasAttachments": message.has_attachments
}
if includeAttachments and message.has_attachments:
attachments = []
for attachment in message.attachments:
attachments.append({
"name": attachment.name,
"contentType": attachment.content_type,
"size": attachment.size
})
msgData["attachments"] = attachments
results.append(msgData)
return self._createResult(
success=True,
data={
"folder": folder,
"query": query,
"messages": results
"emails": emails
}
)
except Exception as e:
logger.error(f"Error reading Outlook emails: {e}")
logger.error(f"Error reading emails: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Read failed: {str(e)}"}
data={"error": str(e)}
)
async def _sendMail(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Send email through Outlook"""
@action
async def sendMail(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Send email using Outlook
Args:
parameters:
to: List of recipient email addresses
subject: Email subject
body: Email body
attachments: List of attachment file IDs
"""
try:
toAddresses = parameters["to"]
to = parameters["to"]
subject = parameters["subject"]
body = parameters["body"]
ccAddresses = parameters.get("cc", [])
bccAddresses = parameters.get("bcc", [])
attachments = parameters.get("attachments", [])
# Create Outlook account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
# Send email
messageId = await self.outlookService.sendEmail(
to=to,
subject=subject,
body=body,
attachments=attachments
)
# Get mailbox
mailbox = account.mailbox()
# Create new message
message = mailbox.new_message()
message.to.add(toAddresses)
if ccAddresses:
message.cc.add(ccAddresses)
if bccAddresses:
message.bcc.add(bccAddresses)
message.subject = subject
message.body = body
# Add attachments
for attachmentPath in attachments:
message.attachments.add(attachmentPath)
# Send message
message.send()
return self._createResult(
success=True,
data={
"to": toAddresses,
"subject": subject,
"sent": datetime.now(UTC).isoformat()
"messageId": messageId,
"to": to,
"subject": subject
}
)
except Exception as e:
logger.error(f"Error sending Outlook email: {e}")
logger.error(f"Error sending email: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Send failed: {str(e)}"}
data={"error": str(e)}
)
@action
async def createFolder(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Create folder in Outlook
Args:
parameters:
name: Folder name
parentFolder: Parent folder ID (optional)
"""
try:
name = parameters["name"]
parentFolder = parameters.get("parentFolder")
# Create folder
folderId = await self.outlookService.createFolder(
name=name,
parentFolder=parentFolder
)
return self._createResult(
success=True,
data={
"folderId": folderId,
"name": name,
"parentFolder": parentFolder
}
)
except Exception as e:
logger.error(f"Error creating folder: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def moveMail(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Move email to different folder
Args:
parameters:
messageId: ID of the message to move
targetFolder: ID of the target folder
"""
try:
messageId = parameters["messageId"]
targetFolder = parameters["targetFolder"]
# Move email
await self.outlookService.moveEmail(
messageId=messageId,
targetFolder=targetFolder
)
return self._createResult(
success=True,
data={
"messageId": messageId,
"targetFolder": targetFolder
}
)
except Exception as e:
logger.error(f"Error moving email: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)

View file

@ -1,375 +1,260 @@
from typing import Dict, Any, Optional
import logging
import os
from pathlib import Path
"""
PowerPoint method module.
Handles PowerPoint operations using the PowerPoint service.
"""
from modules.methods.methodBase import MethodBase, MethodResult
from modules.models.userConnection import UserConnection
from modules.models.account import Account
from modules.protocols.msGraphProtocol import MSGraphProtocol
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.interfacePowerpoint import PowerpointService
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
class MethodPowerpoint(MethodBase):
"""Powerpoint method implementation for PowerPoint operations"""
"""PowerPoint method implementation"""
def __init__(self):
super().__init__()
self.name = "powerpoint"
self.description = "Handle PowerPoint operations like reading, writing, and converting presentations"
def __init__(self, serviceContainer):
"""Initialize the PowerPoint method"""
super().__init__(serviceContainer)
self.powerpointService = PowerpointService(serviceContainer)
@action
async def read(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Read PowerPoint presentation
@property
def actions(self) -> Dict[str, Dict[str, Any]]:
"""Available actions and their parameters"""
return {
"read": {
"description": "Read PowerPoint presentation content",
"retryMax": 2,
"timeout": 30,
"parameters": {
"path": {"type": "string", "required": True},
"format": {"type": "string", "required": False},
"includeNotes": {"type": "boolean", "required": False}
}
},
"write": {
"description": "Write content to PowerPoint presentation",
"retryMax": 2,
"timeout": 60,
"parameters": {
"path": {"type": "string", "required": True},
"content": {"type": "object", "required": True},
"template": {"type": "string", "required": False}
}
},
"convert": {
"description": "Convert PowerPoint presentation between formats",
"retryMax": 2,
"timeout": 60,
"parameters": {
"sourcePath": {"type": "string", "required": True},
"targetPath": {"type": "string", "required": True},
"sourceFormat": {"type": "string", "required": False},
"targetFormat": {"type": "string", "required": False}
}
},
"createPresentation": {
"description": "Create a new PowerPoint presentation",
"retryMax": 2,
"timeout": 60,
"parameters": {
"title": {"type": "string", "required": True},
"template": {"type": "string", "required": False}
}
},
"addSlide": {
"description": "Add a new slide to presentation",
"retryMax": 2,
"timeout": 60,
"parameters": {
"presentationId": {"type": "string", "required": True},
"layout": {"type": "string", "required": False},
"title": {"type": "string", "required": False}
}
},
"addContent": {
"description": "Add content to a slide",
"retryMax": 2,
"timeout": 60,
"parameters": {
"presentationId": {"type": "string", "required": True},
"slideId": {"type": "string", "required": True},
"contentType": {"type": "string", "required": True},
"content": {"type": "object", "required": True},
"position": {"type": "object", "required": False}
}
}
}
async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Execute PowerPoint method"""
Args:
parameters:
fileId: ID of the PowerPoint file
includeSlides: Whether to include slide content
"""
try:
# Validate parameters
if not await self.validateParameters(action, parameters):
fileId = parameters["fileId"]
includeSlides = parameters.get("includeSlides", True)
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"Invalid parameters for {action}"}
data={"error": f"File not found: {fileId}"}
)
# Get UserConnection from auth_data
if not authData or "userConnection" not in authData:
return self._createResult(
success=False,
data={"error": "UserConnection required for PowerPoint operations"}
)
# Read presentation
presentation = await self.powerpointService.readPresentation(file, includeSlides)
userConnection: UserConnection = authData["userConnection"]
return self._createResult(
success=True,
data={
"fileId": fileId,
"presentation": presentation
}
)
# Execute action
if action == "createPresentation":
return await self._createPresentation(parameters, userConnection)
elif action == "addSlide":
return await self._addSlide(parameters, userConnection)
elif action == "addContent":
return await self._addContent(parameters, userConnection)
else:
return self._createResult(
success=False,
data={"error": f"Unknown action: {action}"}
)
except Exception as e:
logger.error(f"Error executing PowerPoint {action}: {e}")
logger.error(f"Error reading PowerPoint: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)
async def _read_presentation(self, parameters: Dict[str, Any], authData: Dict[str, Any]) -> MethodResult:
"""Read PowerPoint presentation content"""
@action
async def write(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Write PowerPoint presentation
Args:
parameters:
fileId: ID of the PowerPoint file
slides: List of slide configurations
"""
try:
path = Path(parameters["path"])
if not path.exists():
fileId = parameters["fileId"]
slides = parameters["slides"]
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"File not found: {path}"}
data={"error": f"File not found: {fileId}"}
)
# Determine format if not specified
format = parameters.get("format")
if not format:
format = path.suffix[1:] if path.suffix else "pptx"
# Write presentation
await self.powerpointService.writePresentation(file, slides)
# TODO: Implement PowerPoint reading using Microsoft Graph API
# This is a placeholder implementation
return self._createResult(
success=True,
data={
"path": str(path),
"format": format,
"slides": [
{
"number": 1,
"title": "Example Slide",
"content": "Example content",
"notes": "Example notes" if parameters.get("includeNotes", False) else None
}
]
"fileId": fileId,
"slideCount": len(slides)
}
)
except Exception as e:
logger.error(f"Error reading presentation: {e}")
logger.error(f"Error writing PowerPoint: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Read failed: {str(e)}"}
data={"error": str(e)}
)
async def _write_presentation(self, parameters: Dict[str, Any], authData: Dict[str, Any]) -> MethodResult:
"""Write content to PowerPoint presentation"""
@action
async def convert(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Convert PowerPoint to other format
Args:
parameters:
fileId: ID of the PowerPoint file
format: Target format (pdf, png, etc.)
"""
try:
path = Path(parameters["path"])
fileId = parameters["fileId"]
format = parameters["format"]
# Create directory if it doesn't exist
path.parent.mkdir(parents=True, exist_ok=True)
# Determine format if not specified
format = parameters.get("format")
if not format:
format = path.suffix[1:] if path.suffix else "pptx"
# TODO: Implement PowerPoint writing using Microsoft Graph API
# This is a placeholder implementation
return self._createResult(
success=True,
data={
"path": str(path),
"format": format,
"slides": len(parameters["content"].get("slides", []))
}
)
except Exception as e:
logger.error(f"Error writing presentation: {e}")
return self._createResult(
success=False,
data={"error": f"Write failed: {str(e)}"}
)
async def _convert_presentation(self, parameters: Dict[str, Any], authData: Dict[str, Any]) -> MethodResult:
"""Convert PowerPoint presentation between formats"""
try:
source_path = Path(parameters["sourcePath"])
target_path = Path(parameters["targetPath"])
if not source_path.exists():
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"Source file not found: {source_path}"}
data={"error": f"File not found: {fileId}"}
)
# Determine formats if not specified
source_format = parameters.get("sourceFormat")
if not source_format:
source_format = source_path.suffix[1:] if source_path.suffix else "pptx"
# Convert presentation
convertedFile = await self.powerpointService.convertPresentation(file, format)
target_format = parameters.get("targetFormat")
if not target_format:
target_format = target_path.suffix[1:] if target_path.suffix else "pptx"
# TODO: Implement PowerPoint conversion using Microsoft Graph API
# This is a placeholder implementation
return self._createResult(
success=True,
data={
"sourcePath": str(source_path),
"targetPath": str(target_path),
"sourceFormat": source_format,
"targetFormat": target_format
"fileId": fileId,
"format": format,
"convertedFileId": convertedFile.id
}
)
except Exception as e:
logger.error(f"Error converting presentation: {e}")
logger.error(f"Error converting PowerPoint: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Conversion failed: {str(e)}"}
data={"error": str(e)}
)
async def _createPresentation(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Create a new PowerPoint presentation"""
@action
async def createPresentation(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Create new PowerPoint presentation
Args:
parameters:
fileName: Name of the new file
template: Template ID (optional)
"""
try:
title = parameters["title"]
fileName = parameters["fileName"]
template = parameters.get("template")
# Create PowerPoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
)
# Get drive
drive = account.drive()
# Create presentation
if template:
# Copy template
templateFile = drive.get_item_by_path(template)
newFile = templateFile.copy(f"{title}.pptx")
else:
# Create blank presentation
newFile = drive.create_file(
name=f"{title}.pptx",
content_type="application/vnd.openxmlformats-officedocument.presentationml.presentation"
)
file = await self.powerpointService.createPresentation(fileName, template)
return self._createResult(
success=True,
data={
"id": newFile.object_id,
"name": newFile.name,
"webUrl": newFile.web_url
"fileId": file.id,
"fileName": fileName,
"template": template
}
)
except Exception as e:
logger.error(f"Error creating PowerPoint presentation: {e}")
logger.error(f"Error creating PowerPoint: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Create failed: {str(e)}"}
data={"error": str(e)}
)
async def _addSlide(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Add a new slide to presentation"""
@action
async def addSlide(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Add slide to presentation
Args:
parameters:
fileId: ID of the PowerPoint file
layout: Slide layout
content: Slide content
"""
try:
presentationId = parameters["presentationId"]
fileId = parameters["fileId"]
layout = parameters.get("layout", "title")
title = parameters.get("title")
content = parameters.get("content", {})
# Create PowerPoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
)
# Get drive
drive = account.drive()
# Get presentation
presentation = drive.get_item_by_id(presentationId)
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"File not found: {fileId}"}
)
# Add slide
slide = presentation.add_slide(layout=layout)
if title:
slide.title = title
slideId = await self.powerpointService.addSlide(file, layout, content)
return self._createResult(
success=True,
data={
"slideId": slide.object_id,
"layout": layout,
"title": title
"fileId": fileId,
"slideId": slideId,
"layout": layout
}
)
except Exception as e:
logger.error(f"Error adding PowerPoint slide: {e}")
logger.error(f"Error adding slide: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Add slide failed: {str(e)}"}
data={"error": str(e)}
)
async def _addContent(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Add content to a slide"""
@action
async def addContent(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Add content to slide
Args:
parameters:
fileId: ID of the PowerPoint file
slideId: ID of the slide
content: Content to add
"""
try:
presentationId = parameters["presentationId"]
fileId = parameters["fileId"]
slideId = parameters["slideId"]
contentType = parameters["contentType"]
content = parameters["content"]
position = parameters.get("position", {"x": 0, "y": 0})
# Create PowerPoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
)
# Get drive
drive = account.drive()
# Get presentation and slide
presentation = drive.get_item_by_id(presentationId)
slide = presentation.get_slide(slideId)
# Add content based on type
if contentType == "text":
shape = slide.add_text_box(
text=content,
left=position["x"],
top=position["y"]
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult(
success=False,
data={"error": f"File not found: {fileId}"}
)
elif contentType == "image":
shape = slide.add_picture(
image_path=content,
left=position["x"],
top=position["y"]
)
elif contentType == "table":
shape = slide.add_table(
rows=content["rows"],
cols=content["cols"],
left=position["x"],
top=position["y"]
)
else:
raise ValueError(f"Unsupported content type: {contentType}")
# Add content
await self.powerpointService.addContent(file, slideId, content)
return self._createResult(
success=True,
data={
"shapeId": shape.object_id,
"contentType": contentType,
"position": position
"fileId": fileId,
"slideId": slideId
}
)
except Exception as e:
logger.error(f"Error adding PowerPoint content: {e}")
logger.error(f"Error adding content: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Add content failed: {str(e)}"}
data={"error": str(e)}
)

View file

@ -1,389 +1,269 @@
from typing import Dict, Any, Optional
import logging
from datetime import datetime, UTC
from office365.runtime.auth.user_credential import UserCredential
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.files.file import File
from office365.sharepoint.lists.list import List
from office365.sharepoint.lists.list_creation_information import ListCreationInformation
"""
SharePoint method module.
Handles SharePoint operations using the SharePoint service.
"""
from modules.methods.methodBase import MethodBase, MethodResult
from modules.models.userConnection import UserConnection
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.interfaceSharepoint import SharepointService
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
class MethodSharepoint(MethodBase):
"""SharePoint method implementation for document operations"""
"""SharePoint method implementation"""
def __init__(self):
super().__init__()
self.name = "sharepoint"
self.description = "Handle SharePoint document operations like search, read, and write"
def __init__(self, serviceContainer):
"""Initialize the SharePoint method"""
super().__init__(serviceContainer)
self.sharepointService = SharepointService(serviceContainer)
@action
async def search(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Search SharePoint content
@property
def actions(self) -> Dict[str, Dict[str, Any]]:
"""Available actions and their parameters"""
return {
"search": {
"description": "Search SharePoint documents",
"retryMax": 3,
"timeout": 30,
"parameters": {
"query": {"type": "string", "required": True},
"siteUrl": {"type": "string", "required": True},
"listName": {"type": "string", "required": False},
"maxResults": {"type": "number", "required": False}
}
},
"read": {
"description": "Read SharePoint document content",
"retryMax": 2,
"timeout": 30,
"parameters": {
"fileUrl": {"type": "string", "required": True},
"siteUrl": {"type": "string", "required": True}
}
},
"write": {
"description": "Write content to SharePoint document",
"retryMax": 2,
"timeout": 30,
"parameters": {
"fileUrl": {"type": "string", "required": True},
"siteUrl": {"type": "string", "required": True},
"content": {"type": "string", "required": True},
"contentType": {"type": "string", "required": False}
}
},
"readList": {
"description": "Read items from SharePoint list",
"retryMax": 2,
"timeout": 30,
"parameters": {
"siteUrl": {"type": "string", "required": True},
"listName": {"type": "string", "required": True},
"query": {"type": "string", "required": False},
"fields": {"type": "array", "required": False}
}
},
"writeList": {
"description": "Write items to SharePoint list",
"retryMax": 2,
"timeout": 30,
"parameters": {
"siteUrl": {"type": "string", "required": True},
"listName": {"type": "string", "required": True},
"items": {"type": "array", "required": True}
}
},
"createList": {
"description": "Create a new SharePoint list",
"retryMax": 2,
"timeout": 30,
"parameters": {
"siteUrl": {"type": "string", "required": True},
"listName": {"type": "string", "required": True},
"description": {"type": "string", "required": False},
"template": {"type": "string", "required": False},
"fields": {"type": "array", "required": False}
}
}
}
async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Execute SharePoint method"""
Args:
parameters:
query: Search query
siteId: Site ID to search in
contentType: Content type to search for
maxResults: Maximum number of results
"""
try:
# Validate parameters
if not await self.validateParameters(action, parameters):
return self._createResult(
success=False,
data={"error": f"Invalid parameters for {action}"}
)
# Get UserConnection from auth_data
if not authData or "userConnection" not in authData:
return self._createResult(
success=False,
data={"error": "UserConnection required for SharePoint operations"}
)
userConnection: UserConnection = authData["userConnection"]
# Execute action
if action == "search":
return await self._search_documents(parameters, userConnection)
elif action == "read":
return await self._read_document(parameters, userConnection)
elif action == "write":
return await self._write_document(parameters, userConnection)
elif action == "readList":
return await self._readList(parameters, userConnection)
elif action == "writeList":
return await self._writeList(parameters, userConnection)
elif action == "createList":
return await self._createList(parameters, userConnection)
else:
return self._createResult(
success=False,
data={"error": f"Unknown action: {action}"}
)
except Exception as e:
logger.error(f"Error executing SharePoint {action}: {e}")
return self._createResult(
success=False,
data={"error": str(e)}
)
async def _search_documents(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Search SharePoint documents"""
try:
siteUrl = parameters["siteUrl"]
query = parameters["query"]
listName = parameters.get("listName")
siteId = parameters.get("siteId")
contentType = parameters.get("contentType")
maxResults = parameters.get("maxResults", 10)
# Create SharePoint context
ctx = ClientContext(siteUrl).with_credentials(
UserCredential(userConnection.authToken, userConnection.refreshToken)
# Search content
results = await self.sharepointService.searchContent(
query=query,
siteId=siteId,
contentType=contentType,
maxResults=maxResults
)
# Search in specific list or entire site
if listName:
targetList = ctx.web.lists.get_by_title(listName)
items = targetList.items.filter(f"Title eq '{query}'").top(maxResults).get().execute_query()
results = [{
"title": item.properties["Title"],
"url": item.properties["FileRef"],
"modified": item.properties["Modified"],
"created": item.properties["Created"]
} for item in items]
else:
# Search entire site
search_results = ctx.search(query).execute_query()
results = [{
"title": result.properties["Title"],
"url": result.properties["Path"],
"modified": result.properties["LastModifiedTime"],
"created": result.properties["Created"]
} for result in search_results[:maxResults]]
return self._createResult(
success=True,
data={
"query": query,
"siteId": siteId,
"contentType": contentType,
"results": results
}
)
except Exception as e:
logger.error(f"Error searching SharePoint documents: {e}")
logger.error(f"Error searching SharePoint: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Search failed: {str(e)}"}
data={"error": str(e)}
)
async def _read_document(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Read SharePoint document content"""
@action
async def read(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Read SharePoint item
Args:
parameters:
itemId: ID of the item to read
siteId: Site ID containing the item
listId: List ID containing the item
"""
try:
siteUrl = parameters["siteUrl"]
fileUrl = parameters["fileUrl"]
itemId = parameters["itemId"]
siteId = parameters.get("siteId")
listId = parameters.get("listId")
# Create SharePoint context
ctx = ClientContext(siteUrl).with_credentials(
UserCredential(userConnection.authToken, userConnection.refreshToken)
# Read item
item = await self.sharepointService.readItem(
itemId=itemId,
siteId=siteId,
listId=listId
)
# Get file
file = ctx.web.get_file_by_server_relative_url(fileUrl)
file_content = file.read().execute_query()
return self._createResult(
success=True,
data={
"url": fileUrl,
"content": file_content.content.decode('utf-8'),
"modified": file.properties["TimeLastModified"],
"size": file.properties["Length"]
"itemId": itemId,
"siteId": siteId,
"listId": listId,
"item": item
}
)
except Exception as e:
logger.error(f"Error reading SharePoint document: {e}")
logger.error(f"Error reading SharePoint item: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Read failed: {str(e)}"}
data={"error": str(e)}
)
async def _write_document(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Write content to SharePoint document"""
@action
async def write(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Write SharePoint item
Args:
parameters:
siteId: Site ID to write to
listId: List ID to write to
item: Item data to write
"""
try:
siteUrl = parameters["siteUrl"]
fileUrl = parameters["fileUrl"]
content = parameters["content"]
contentType = parameters.get("contentType", "text/plain")
siteId = parameters["siteId"]
listId = parameters["listId"]
item = parameters["item"]
# Create SharePoint context
ctx = ClientContext(siteUrl).with_credentials(
UserCredential(userConnection.authToken, userConnection.refreshToken)
# Write item
itemId = await self.sharepointService.writeItem(
siteId=siteId,
listId=listId,
item=item
)
# Get or create file
try:
file = ctx.web.get_file_by_server_relative_url(fileUrl)
except:
# Create new file
folderUrl = "/".join(fileUrl.split("/")[:-1])
fileName = fileUrl.split("/")[-1]
folder = ctx.web.get_folder_by_server_relative_url(folderUrl)
file = folder.upload_file(fileName, content.encode('utf-8')).execute_query()
# Update file content
file.write(content.encode('utf-8')).execute_query()
return self._createResult(
success=True,
data={
"url": fileUrl,
"modified": datetime.now(UTC).isoformat(),
"size": len(content.encode('utf-8'))
"siteId": siteId,
"listId": listId,
"itemId": itemId
}
)
except Exception as e:
logger.error(f"Error writing SharePoint document: {e}")
logger.error(f"Error writing SharePoint item: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Write failed: {str(e)}"}
data={"error": str(e)}
)
async def _readList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Read items from SharePoint list"""
@action
async def readList(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Read SharePoint list
Args:
parameters:
listId: ID of the list to read
siteId: Site ID containing the list
query: Query to filter items
maxResults: Maximum number of results
"""
try:
siteUrl = parameters["siteUrl"]
listName = parameters["listName"]
listId = parameters["listId"]
siteId = parameters.get("siteId")
query = parameters.get("query")
fields = parameters.get("fields", ["*"])
maxResults = parameters.get("maxResults", 100)
# Create SharePoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
# Read list
items = await self.sharepointService.readList(
listId=listId,
siteId=siteId,
query=query,
maxResults=maxResults
)
# Get site
site = account.get_site(siteUrl)
# Get list
list = site.get_list(listName)
# Get items
if query:
items = list.get_items(query=query, fields=fields)
else:
items = list.get_items(fields=fields)
return self._createResult(
success=True,
data={
"siteUrl": siteUrl,
"listName": listName,
"listId": listId,
"siteId": siteId,
"items": items
}
)
except Exception as e:
logger.error(f"Error reading SharePoint list: {e}")
logger.error(f"Error reading SharePoint list: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Read failed: {str(e)}"}
data={"error": str(e)}
)
async def _writeList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Write items to SharePoint list"""
@action
async def writeList(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Write multiple items to SharePoint list
Args:
parameters:
siteId: Site ID to write to
listId: List ID to write to
items: List of item data to write
"""
try:
siteUrl = parameters["siteUrl"]
listName = parameters["listName"]
siteId = parameters["siteId"]
listId = parameters["listId"]
items = parameters["items"]
# Create SharePoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
# Write items
itemIds = await self.sharepointService.writeList(
siteId=siteId,
listId=listId,
items=items
)
# Get site
site = account.get_site(siteUrl)
# Get list
list = site.get_list(listName)
# Add items
results = []
for item in items:
result = list.add_item(item)
results.append({
"id": result.id,
"status": "success"
})
return self._createResult(
success=True,
data={
"siteUrl": siteUrl,
"listName": listName,
"results": results
"siteId": siteId,
"listId": listId,
"itemIds": itemIds
}
)
except Exception as e:
logger.error(f"Error writing to SharePoint list: {e}")
logger.error(f"Error writing SharePoint list: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Write failed: {str(e)}"}
data={"error": str(e)}
)
async def _createList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult:
"""Create a new SharePoint list"""
@action
async def createList(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Create SharePoint list
Args:
parameters:
siteId: Site ID to create list in
name: Name of the list
description: List description
template: List template
fields: List field definitions
"""
try:
siteUrl = parameters["siteUrl"]
listName = parameters["listName"]
siteId = parameters["siteId"]
name = parameters["name"]
description = parameters.get("description")
template = parameters.get("template", "generic")
template = parameters.get("template", "genericList")
fields = parameters.get("fields", [])
# Create SharePoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
)
# Get site
site = account.get_site(siteUrl)
# Create list
list = site.create_list(
name=listName,
listId = await self.sharepointService.createList(
siteId=siteId,
name=name,
description=description,
template=template
template=template,
fields=fields
)
# Add fields
for field in fields:
list.add_field(
name=field["name"],
field_type=field["type"],
required=field.get("required", False),
description=field.get("description")
)
return self._createResult(
success=True,
data={
"siteUrl": siteUrl,
"listName": listName,
"id": list.id,
"webUrl": list.web_url
"siteId": siteId,
"listId": listId,
"name": name
}
)
except Exception as e:
logger.error(f"Error creating SharePoint list: {e}")
logger.error(f"Error creating SharePoint list: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Create failed: {str(e)}"}
data={"error": str(e)}
)

View file

@ -1,468 +1,177 @@
from typing import Dict, Any, Optional
import logging
import aiohttp
import asyncio
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import re
from datetime import datetime, UTC
import requests
import time
import json
"""
Web method module.
Handles web operations using the web service.
"""
from modules.methods.methodBase import MethodBase, MethodResult
from modules.shared.configuration import APP_CONFIG
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from modules.interfaces.interfaceWeb import WebService
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__)
class MethodWeb(MethodBase):
"""Web method implementation for web operations"""
"""Web method implementation"""
def __init__(self):
super().__init__()
self.name = "web"
self.description = "Handle web operations like search, crawl, and content extraction"
def __init__(self, serviceContainer):
"""Initialize the web method"""
super().__init__(serviceContainer)
self.webService = WebService(serviceContainer)
@action
async def search(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Search web content
# Web crawling configuration from agentWebcrawler
self.srcApikey = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_APIKEY", "")
self.srcEngine = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_ENGINE", "google")
self.srcCountry = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_COUNTRY", "auto")
self.maxResults = int(APP_CONFIG.get("Agent_Webcrawler_SERPAPI_MAX_SEARCH_RESULTS", "5"))
self.timeout = int(APP_CONFIG.get("Agent_Webcrawler_SERPAPI_TIMEOUT", "30"))
self.userAgent = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_USER_AGENT", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
if not self.srcApikey:
logger.error("SerpAPI key not configured")
@property
def actions(self) -> Dict[str, Dict[str, Any]]:
"""Available actions and their parameters"""
return {
"search": {
"description": "Search web content",
"retryMax": 3,
"timeout": 30,
"parameters": {
"query": {"type": "string", "required": True},
"maxResults": {"type": "number", "required": False},
"filters": {"type": "object", "required": False},
"searchEngine": {"type": "string", "required": False}
}
},
"crawl": {
"description": "Crawl web pages",
"retryMax": 2,
"timeout": 60,
"parameters": {
"url": {"type": "string", "required": True},
"depth": {"type": "number", "required": False},
"followLinks": {"type": "boolean", "required": False},
"includeImages": {"type": "boolean", "required": False},
"respectRobots": {"type": "boolean", "required": False}
}
},
"extract": {
"description": "Extract content from web page",
"retryMax": 2,
"timeout": 30,
"parameters": {
"url": {"type": "string", "required": True},
"selectors": {"type": "array", "items": "string", "required": False},
"format": {"type": "string", "required": False},
"includeMetadata": {"type": "boolean", "required": False}
}
}
}
async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Execute web method"""
try:
# Validate parameters
if not await self.validateParameters(action, parameters):
return self._createResult(
success=False,
data={"error": f"Invalid parameters for {action}"}
)
# Execute action
if action == "fetchUrl":
return await self._fetchUrl(parameters)
elif action == "parseContent":
return await self._parseContent(parameters)
elif action == "extractData":
return await self._extractData(parameters)
else:
return self._createResult(
success=False,
data={"error": f"Unknown action: {action}"}
)
except Exception as e:
logger.error(f"Error executing web {action}: {e}")
return self._createResult(
success=False,
data={"error": str(e)}
)
async def _fetchUrl(self, parameters: Dict[str, Any]) -> MethodResult:
"""Fetch content from URL"""
try:
url = parameters["url"]
method = parameters.get("method", "GET")
headers = parameters.get("headers", {})
data = parameters.get("data")
timeout = parameters.get("timeout", 30)
async with aiohttp.ClientSession() as session:
async with session.request(
method=method,
url=url,
headers=headers,
data=data,
timeout=timeout
) as response:
content = await response.text()
return self._createResult(
success=True,
data={
"url": url,
"status": response.status,
"headers": dict(response.headers),
"content": content
}
)
except Exception as e:
logger.error(f"Error fetching URL: {e}")
return self._createResult(
success=False,
data={"error": f"Fetch failed: {str(e)}"}
)
async def _parseContent(self, parameters: Dict[str, Any]) -> MethodResult:
"""Parse web content"""
try:
content = parameters["content"]
contentType = parameters.get("contentType", "html")
if contentType == "html":
soup = BeautifulSoup(content, "html.parser")
return self._createResult(
success=True,
data={
"type": "html",
"title": soup.title.string if soup.title else None,
"text": soup.get_text(),
"links": [a.get("href") for a in soup.find_all("a", href=True)],
"images": [img.get("src") for img in soup.find_all("img", src=True)]
}
)
elif contentType == "json":
data = json.loads(content)
return self._createResult(
success=True,
data={
"type": "json",
"data": data
}
)
else:
raise ValueError(f"Unsupported content type: {contentType}")
except Exception as e:
logger.error(f"Error parsing content: {e}")
return self._createResult(
success=False,
data={"error": f"Parse failed: {str(e)}"}
)
async def _extractData(self, parameters: Dict[str, Any]) -> MethodResult:
"""Extract data from web content"""
try:
content = parameters["content"]
contentType = parameters.get("contentType", "html")
selectors = parameters["selectors"]
if contentType == "html":
soup = BeautifulSoup(content, "html.parser")
results = {}
for key, selector in selectors.items():
elements = soup.select(selector)
if len(elements) == 1:
results[key] = elements[0].get_text().strip()
else:
results[key] = [el.get_text().strip() for el in elements]
return self._createResult(
success=True,
data={
"type": "html",
"results": results
}
)
elif contentType == "json":
data = json.loads(content)
results = {}
for key, path in selectors.items():
value = data
for part in path.split("."):
if isinstance(value, dict):
value = value.get(part)
elif isinstance(value, list) and part.isdigit():
value = value[int(part)]
else:
value = None
break
results[key] = value
return self._createResult(
success=True,
data={
"type": "json",
"results": results
}
)
else:
raise ValueError(f"Unsupported content type: {contentType}")
except Exception as e:
logger.error(f"Error extracting data: {e}")
return self._createResult(
success=False,
data={"error": f"Extract failed: {str(e)}"}
)
async def _search_web(self, parameters: Dict[str, Any]) -> MethodResult:
"""Search web content"""
Args:
parameters:
query: Search query
engine: Search engine to use (google, bing)
maxResults: Maximum number of results
"""
try:
query = parameters["query"]
engine = parameters.get("engine", "google")
maxResults = parameters.get("maxResults", 10)
filters = parameters.get("filters", {})
searchEngine = parameters.get("searchEngine", "google")
# Implement search using different engines
if searchEngine.lower() == "google":
# Use Google Custom Search API
# TODO: Implement Google Custom Search API integration
results = await self._google_search(query, maxResults, filters)
elif searchEngine.lower() == "bing":
# Use Bing Web Search API
# TODO: Implement Bing Web Search API integration
results = await self._bing_search(query, maxResults, filters)
else:
return self._createResult(
success=False,
data={"error": f"Unsupported search engine: {searchEngine}"}
)
# Search web
results = await self.webService.searchContent(
query=query,
engine=engine,
maxResults=maxResults
)
return self._createResult(
success=True,
data={
"query": query,
"engine": searchEngine,
"engine": engine,
"results": results
}
)
except Exception as e:
logger.error(f"Error searching web: {e}")
logger.error(f"Error searching web: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Search failed: {str(e)}"}
data={"error": str(e)}
)
async def _google_search(self, query: str, max_results: int, filters: Dict[str, Any]) -> list:
"""Search using Google Custom Search API"""
# TODO: Implement Google Custom Search API
# This is a placeholder implementation
return [
{
"title": "Example Result",
"url": "https://example.com",
"snippet": "Example search result snippet",
"source": "google"
}
]
async def _bing_search(self, query: str, max_results: int, filters: Dict[str, Any]) -> list:
"""Search using Bing Web Search API"""
# TODO: Implement Bing Web Search API
# This is a placeholder implementation
return [
{
"title": "Example Result",
"url": "https://example.com",
"snippet": "Example search result snippet",
"source": "bing"
}
]
async def _crawl_page(self, parameters: Dict[str, Any]) -> MethodResult:
"""Crawl web pages"""
@action
async def crawl(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Crawl web page
Args:
parameters:
url: URL to crawl
depth: Crawl depth
followLinks: Whether to follow links
extractContent: Whether to extract content
"""
try:
url = parameters["url"]
depth = parameters.get("depth", 1)
followLinks = parameters.get("followLinks", False)
includeImages = parameters.get("includeImages", False)
respectRobots = parameters.get("respectRobots", True)
extractContent = parameters.get("extractContent", True)
# Check robots.txt if required
if respectRobots:
if not await self._check_robots_txt(url):
return self._createResult(
success=False,
data={"error": "Crawling not allowed by robots.txt"}
)
# Crawl page
results = await self.webService.crawlPage(
url=url,
depth=depth,
followLinks=followLinks,
extractContent=extractContent
)
return self._createResult(
success=True,
data={
"url": url,
"depth": depth,
"results": results
}
)
# Crawl the page
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
html = await response.text()
soup = BeautifulSoup(html, 'html.parser')
# Extract basic information
result = {
"url": url,
"title": soup.title.string if soup.title else None,
"description": self._get_meta_description(soup),
"links": [],
"images": [] if includeImages else None,
"text": soup.get_text(strip=True),
"crawled": datetime.now(UTC).isoformat()
}
# Extract links if followLinks is True
if followLinks:
baseUrl = url
for link in soup.find_all('a'):
href = link.get('href')
if href:
absoluteUrl = urljoin(baseUrl, href)
if self._is_valid_url(absoluteUrl):
result["links"].append({
"url": absoluteUrl,
"text": link.get_text(strip=True)
})
# Extract images if includeImages is True
if includeImages:
for img in soup.find_all('img'):
src = img.get('src')
if src:
absoluteSrc = urljoin(url, src)
result["images"].append({
"url": absoluteSrc,
"alt": img.get('alt', ''),
"title": img.get('title', '')
})
return self._createResult(
success=True,
data=result
)
else:
return self._createResult(
success=False,
data={"error": f"Failed to fetch URL: {response.status}"}
)
except Exception as e:
logger.error(f"Error crawling page: {e}")
logger.error(f"Error crawling web page: {str(e)}")
return self._createResult(
success=False,
data={"error": f"Crawl failed: {str(e)}"}
data={"error": str(e)}
)
def _get_meta_description(self, soup: BeautifulSoup) -> Optional[str]:
"""Extract meta description from HTML"""
metaDesc = soup.find('meta', attrs={'name': 'description'})
if metaDesc:
return metaDesc.get('content')
return None
def _is_valid_url(self, url: str) -> bool:
"""Check if URL is valid"""
@action
async def extract(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Extract content from web page
Args:
parameters:
url: URL to extract from
selectors: CSS selectors to extract
format: Output format (text, html, json)
"""
try:
result = urlparse(url)
return all([result.scheme, result.netloc])
except:
return False
async def _check_robots_txt(self, url: str) -> bool:
"""Check if URL is allowed by robots.txt"""
try:
parsedUrl = urlparse(url)
robotsUrl = f"{parsedUrl.scheme}://{parsedUrl.netloc}/robots.txt"
url = parameters["url"]
selectors = parameters.get("selectors", ["body"])
format = parameters.get("format", "text")
async with aiohttp.ClientSession() as session:
async with session.get(robotsUrl, headers={"User-Agent": self.userAgent}, timeout=self.timeout) as response:
if response.status == 200:
robotsContent = await response.text()
# Parse robots.txt content
userAgent = "*" # Default to all user agents
disallowPaths = []
for line in robotsContent.splitlines():
line = line.strip().lower()
if line.startswith("user-agent:"):
userAgent = line[11:].strip()
elif line.startswith("disallow:") and userAgent in ["*", self.userAgent.lower()]:
path = line[9:].strip()
if path:
disallowPaths.append(path)
# Check if URL path is disallowed
urlPath = parsedUrl.path
for disallowPath in disallowPaths:
if urlPath.startswith(disallowPath):
return False
return True
else:
# If robots.txt doesn't exist, assume crawling is allowed
return True
except Exception as e:
logger.warning(f"Error checking robots.txt for {url}: {str(e)}")
# If there's an error, assume crawling is allowed
return True
def _detect_language(self, soup: BeautifulSoup) -> str:
"""Detect page language"""
try:
# Try to get language from HTML lang attribute
if soup.html and soup.html.get('lang'):
return soup.html.get('lang')
# Extract content
content = await self.webService.extractContent(
url=url,
selectors=selectors,
format=format
)
# Try to get language from meta tag
metaLang = soup.find('meta', attrs={'http-equiv': 'content-language'})
if metaLang:
return metaLang.get('content', 'en')
# Try to get language from meta charset
metaCharset = soup.find('meta', attrs={'charset': True})
if metaCharset:
charset = metaCharset.get('charset', '').lower()
if 'utf-8' in charset:
return 'en' # Default to English for UTF-8
# Try to detect language from content
# This is a simple heuristic based on common words
text = soup.get_text().lower()
commonWords = {
'en': ['the', 'and', 'of', 'to', 'in', 'is', 'that', 'for', 'it', 'with'],
'es': ['el', 'la', 'los', 'las', 'de', 'y', 'en', 'que', 'por', 'con'],
'fr': ['le', 'la', 'les', 'de', 'et', 'en', 'que', 'pour', 'avec', 'dans'],
'de': ['der', 'die', 'das', 'und', 'in', 'den', 'von', 'zu', 'für', 'mit']
}
wordCounts = {lang: sum(1 for word in words if f' {word} ' in f' {text} ')
for lang, words in commonWords.items()}
if wordCounts:
return max(wordCounts.items(), key=lambda x: x[1])[0]
return 'en' # Default to English if no language detected
return self._createResult(
success=True,
data={
"url": url,
"format": format,
"content": content
}
)
except Exception as e:
logger.warning(f"Error detecting language: {str(e)}")
return 'en' # Default to English on error
logger.error(f"Error extracting web content: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)
@action
async def validate(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Validate web page
Args:
parameters:
url: URL to validate
checks: List of checks to perform
"""
try:
url = parameters["url"]
checks = parameters.get("checks", ["accessibility", "seo", "performance"])
# Validate page
results = await self.webService.validatePage(
url=url,
checks=checks
)
return self._createResult(
success=True,
data={
"url": url,
"checks": checks,
"results": results
}
)
except Exception as e:
logger.error(f"Error validating web page: {str(e)}")
return self._createResult(
success=False,
data={"error": str(e)}
)

View file

@ -9,7 +9,7 @@ from fastapi import HTTPException, status
from modules.shared.configuration import APP_CONFIG
from modules.security.auth import limiter, getCurrentUser
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
router = APIRouter(
prefix="",

View file

@ -11,7 +11,7 @@ import logging
from modules.security.auth import limiter, getCurrentUser
# Import the attribute definition and helper functions
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
from modules.shared.attributeUtils import getModelClasses, getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
# Configure logger

View file

@ -10,9 +10,9 @@ from datetime import datetime
import logging
import json
from modules.interfaces.serviceAppModel import User, UserConnection, AuthAuthority, ConnectionStatus
from modules.interfaces.interfaceAppModel import User, UserConnection, AuthAuthority, ConnectionStatus
from modules.security.auth import getCurrentUser, limiter
from modules.interfaces.serviceAppClass import getInterface, getRootInterface
from modules.interfaces.interfaceAppObjects import getInterface, getRootInterface
# Configure logger
logger = logging.getLogger(__name__)

View file

@ -14,10 +14,10 @@ from pydantic import BaseModel
from modules.security.auth import limiter, getCurrentUser
# Import interfaces
import modules.interfaces.serviceManagementClass as serviceManagementClass
from modules.interfaces.serviceManagementModel import FileItem, FilePreview
import modules.interfaces.interfaceComponentObjects as interfaceComponentObjects
from modules.interfaces.interfaceComponentModel import FileItem, FilePreview
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
# Configure logger
logger = logging.getLogger(__name__)
@ -46,7 +46,7 @@ async def get_files(
) -> List[FileItem]:
"""Get all files"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get all files generically - only metadata, no binary data
files = managementInterface.getAllFiles()
@ -70,17 +70,17 @@ async def upload_file(
) -> JSONResponse:
"""Upload a file"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Read file
fileContent = await file.read()
# Check size limits
maxSize = int(serviceManagementClass.APP_CONFIG.get("File_Management_MAX_UPLOAD_SIZE_MB")) * 1024 * 1024 # in bytes
maxSize = int(interfaceComponentObjects.APP_CONFIG.get("File_Management_MAX_UPLOAD_SIZE_MB")) * 1024 * 1024 # in bytes
if len(fileContent) > maxSize:
raise HTTPException(
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
detail=f"File too large. Maximum size: {serviceManagementClass.APP_CONFIG.get('File_Management_MAX_UPLOAD_SIZE_MB')}MB"
detail=f"File too large. Maximum size: {interfaceComponentObjects.APP_CONFIG.get('File_Management_MAX_UPLOAD_SIZE_MB')}MB"
)
# Save file via LucyDOM interface in the database
@ -101,7 +101,7 @@ async def upload_file(
"file": fileMeta
})
except serviceManagementClass.FileStorageError as e:
except interfaceComponentObjects.FileStorageError as e:
logger.error(f"Error during file upload (storage): {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
@ -123,7 +123,7 @@ async def get_file(
) -> FileItem:
"""Get a file"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get file via LucyDOM interface from the database
fileData = managementInterface.getFile(fileId)
@ -135,19 +135,19 @@ async def get_file(
return FileItem(**fileData)
except serviceManagementClass.FileNotFoundError as e:
except interfaceComponentObjects.FileNotFoundError as e:
logger.warning(f"File not found: {str(e)}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e)
)
except serviceManagementClass.FilePermissionError as e:
except interfaceComponentObjects.FilePermissionError as e:
logger.warning(f"No permission for file: {str(e)}")
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=str(e)
)
except serviceManagementClass.FileError as e:
except interfaceComponentObjects.FileError as e:
logger.error(f"Error retrieving file: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
@ -170,7 +170,7 @@ async def update_file(
) -> FileItem:
"""Update file info"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get the file from the database
file = managementInterface.getFile(fileId)
@ -216,7 +216,7 @@ async def delete_file(
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
"""Delete a file"""
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Check if the file exists
existingFile = managementInterface.getFile(fileId)
@ -243,7 +243,7 @@ async def get_file_stats(
) -> Dict[str, Any]:
"""Returns statistics about the stored files"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get all files - metadata only
allFiles = managementInterface.getAllFiles()
@ -282,7 +282,7 @@ async def download_file(
) -> Response:
"""Download a file"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get file data
fileData = managementInterface.getFile(fileId)
@ -326,7 +326,7 @@ async def preview_file(
) -> FilePreview:
"""Preview a file's content"""
try:
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get file preview
preview = managementInterface.getFilePreview(fileId)

View file

@ -17,11 +17,11 @@ from pydantic import BaseModel
from modules.security.auth import limiter, getCurrentUser
# Import interfaces
import modules.interfaces.serviceAppClass as serviceAppClass
import modules.interfaces.interfaceAppObjects as interfaceAppObjects
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
# Import the model classes
from modules.interfaces.serviceAppModel import Mandate, User
from modules.interfaces.interfaceAppModel import Mandate, User
# Configure logger
logger = logging.getLogger(__name__)
@ -44,7 +44,7 @@ async def get_mandates(
) -> List[Mandate]:
"""Get all mandates"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
mandates = appInterface.getAllMandates()
return mandates
except Exception as e:
@ -63,7 +63,7 @@ async def get_mandate(
) -> Mandate:
"""Get a specific mandate by ID"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
mandate = appInterface.getMandate(mandateId)
if not mandate:
@ -91,7 +91,7 @@ async def create_mandate(
) -> Mandate:
"""Create a new mandate"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Create mandate
newMandate = appInterface.createMandate(
@ -125,7 +125,7 @@ async def update_mandate(
) -> Mandate:
"""Update an existing mandate"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Check if mandate exists
existingMandate = appInterface.getMandate(mandateId)
@ -163,7 +163,7 @@ async def delete_mandate(
) -> Dict[str, Any]:
"""Delete a mandate"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Check if mandate exists
existingMandate = appInterface.getMandate(mandateId)

View file

@ -12,10 +12,10 @@ from pydantic import BaseModel
from modules.security.auth import limiter, getCurrentUser
# Import interfaces
import modules.interfaces.serviceManagementClass as serviceManagementClass
from modules.interfaces.serviceManagementModel import Prompt
import modules.interfaces.interfaceComponentObjects as interfaceComponentObjects
from modules.interfaces.interfaceComponentModel import Prompt
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
# Configure logger
logger = logging.getLogger(__name__)
@ -34,7 +34,7 @@ async def get_prompts(
currentUser: User = Depends(getCurrentUser)
) -> List[Prompt]:
"""Get all prompts"""
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
prompts = managementInterface.getAllPrompts()
return prompts
@ -46,7 +46,7 @@ async def create_prompt(
currentUser: User = Depends(getCurrentUser)
) -> Prompt:
"""Create a new prompt"""
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Convert Prompt to dict for interface
prompt_data = prompt.dict()
@ -64,7 +64,7 @@ async def get_prompt(
currentUser: User = Depends(getCurrentUser)
) -> Prompt:
"""Get a specific prompt"""
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Get prompt
prompt = managementInterface.getPrompt(promptId)
@ -85,7 +85,7 @@ async def update_prompt(
currentUser: User = Depends(getCurrentUser)
) -> Prompt:
"""Update an existing prompt"""
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Check if the prompt exists
existingPrompt = managementInterface.getPrompt(promptId)
@ -117,7 +117,7 @@ async def delete_prompt(
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
"""Delete a prompt"""
managementInterface = serviceManagementClass.getInterface(currentUser)
managementInterface = interfaceComponentObjects.getInterface(currentUser)
# Check if the prompt exists
existingPrompt = managementInterface.getPrompt(promptId)

View file

@ -14,11 +14,11 @@ import os
from pydantic import BaseModel
# Import interfaces and models
import modules.interfaces.serviceAppClass as serviceAppClass
import modules.interfaces.interfaceAppObjects as interfaceAppObjects
from modules.security.auth import getCurrentUser, limiter, getCurrentUser
# Import the attribute definition and helper functions
from modules.interfaces.serviceAppModel import User, AttributeDefinition
from modules.interfaces.interfaceAppModel import User, AttributeDefinition
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse
# Configure logger
@ -39,7 +39,7 @@ async def get_users(
) -> List[User]:
"""Get all users in the current mandate"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# If mandateId is provided, use it, otherwise use the current user's mandate
targetMandateId = mandateId or currentUser.mandateId
# Get all users without filtering by enabled status
@ -61,7 +61,7 @@ async def get_user(
) -> User:
"""Get a specific user by ID"""
try:
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Get user without filtering by enabled status
user = appInterface.getUser(userId)
@ -89,7 +89,7 @@ async def create_user(
currentUser: User = Depends(getCurrentUser)
) -> User:
"""Create a new user"""
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Convert User to dict for interface
user_dict = user_data.dict()
@ -108,7 +108,7 @@ async def update_user(
currentUser: User = Depends(getCurrentUser)
) -> User:
"""Update an existing user"""
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Check if the user exists
existingUser = appInterface.getUser(userId)
@ -140,7 +140,7 @@ async def delete_user(
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
"""Delete a user"""
appInterface = serviceAppClass.getInterface(currentUser)
appInterface = interfaceAppObjects.getInterface(currentUser)
# Check if the user exists
existingUser = appInterface.getUser(userId)

View file

@ -14,8 +14,8 @@ from google.auth.transport.requests import Request as GoogleRequest
from googleapiclient.discovery import build
from modules.shared.configuration import APP_CONFIG
from modules.interfaces.serviceAppClass import getInterface, getRootInterface
from modules.interfaces.serviceAppModel import AuthAuthority, User, Token, ConnectionStatus, UserConnection
from modules.interfaces.interfaceAppObjects import getInterface, getRootInterface
from modules.interfaces.interfaceAppModel import AuthAuthority, User, Token, ConnectionStatus, UserConnection
from modules.security.auth import getCurrentUser, limiter
from modules.shared.attributeUtils import ModelMixin

View file

@ -12,8 +12,8 @@ from pydantic import BaseModel
# Import auth modules
from modules.security.auth import createAccessToken, getCurrentUser, limiter
from modules.interfaces.serviceAppClass import getInterface, getRootInterface
from modules.interfaces.serviceAppModel import User, UserInDB, AuthAuthority, UserPrivilege, Token
from modules.interfaces.interfaceAppObjects import getInterface, getRootInterface
from modules.interfaces.interfaceAppModel import User, UserInDB, AuthAuthority, UserPrivilege, Token
from modules.shared.attributeUtils import ModelMixin
# Configure logger

View file

@ -12,8 +12,8 @@ import msal
import httpx
from modules.shared.configuration import APP_CONFIG
from modules.interfaces.serviceAppClass import getInterface, getRootInterface
from modules.interfaces.serviceAppModel import AuthAuthority, User, Token, ConnectionStatus, UserConnection
from modules.interfaces.interfaceAppObjects import getInterface, getRootInterface
from modules.interfaces.interfaceAppModel import AuthAuthority, User, Token, ConnectionStatus, UserConnection
from modules.security.auth import getCurrentUser, limiter, createAccessToken
from modules.shared.attributeUtils import ModelMixin

View file

@ -15,11 +15,11 @@ from datetime import datetime, timedelta
from modules.security.auth import limiter, getCurrentUser
# Import interfaces
import modules.interfaces.serviceChatClass as serviceChatClass
from modules.interfaces.serviceChatClass import getInterface
import modules.interfaces.interfaceChatObjects as interfaceChatObjects
from modules.interfaces.interfaceChatObjects import getInterface
# Import models
from modules.interfaces.serviceChatModel import (
from modules.interfaces.interfaceChatModel import (
ChatWorkflow,
ChatMessage,
ChatLog,
@ -28,7 +28,7 @@ from modules.interfaces.serviceChatModel import (
UserInputRequest
)
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse
from modules.interfaces.serviceAppModel import User
from modules.interfaces.interfaceAppModel import User
# Configure logger
logger = logging.getLogger(__name__)
@ -44,7 +44,7 @@ router = APIRouter(
)
def getServiceChat(currentUser: User):
return serviceChatClass.getInterface(currentUser)
return interfaceChatObjects.getInterface(currentUser)
# Consolidated endpoint for getting all workflows
@router.get("/", response_model=List[ChatWorkflow])
@ -104,10 +104,10 @@ async def get_workflow_status(
"""Get the current status of a workflow."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Retrieve workflow
workflow = serviceChat.getWorkflow(workflowId)
workflow = interfaceChat.getWorkflow(workflowId)
if not workflow:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
@ -136,10 +136,10 @@ async def get_workflow_logs(
"""Get logs for a workflow with support for selective data transfer."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Verify workflow exists
workflow = serviceChat.getWorkflow(workflowId)
workflow = interfaceChat.getWorkflow(workflowId)
if not workflow:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
@ -147,7 +147,7 @@ async def get_workflow_logs(
)
# Get all logs
allLogs = serviceChat.getWorkflowLogs(workflowId)
allLogs = interfaceChat.getWorkflowLogs(workflowId)
# Apply selective data transfer if logId is provided
if logId:
@ -179,10 +179,10 @@ async def get_workflow_messages(
"""Get messages for a workflow with support for selective data transfer."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Verify workflow exists
workflow = serviceChat.getWorkflow(workflowId)
workflow = interfaceChat.getWorkflow(workflowId)
if not workflow:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
@ -190,7 +190,7 @@ async def get_workflow_messages(
)
# Get all messages
allMessages = serviceChat.getWorkflowMessages(workflowId)
allMessages = interfaceChat.getWorkflowMessages(workflowId)
# Apply selective data transfer if messageId is provided
if messageId:
@ -225,10 +225,10 @@ async def start_workflow(
"""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Start or continue workflow using ChatInterface
workflow = await serviceChat.workflowStart(currentUser, userInput, workflowId)
# Start or continue workflow using ChatObjects
workflow = await interfaceChat.workflowStart(currentUser, userInput, workflowId)
return ChatWorkflow(**workflow)
@ -250,10 +250,10 @@ async def stop_workflow(
"""Stops a running workflow."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Stop workflow using ChatInterface
workflow = await serviceChat.workflowStop(workflowId)
# Stop workflow using ChatObjects
workflow = await interfaceChat.workflowStop(workflowId)
return ChatWorkflow(**workflow)
@ -275,10 +275,10 @@ async def delete_workflow(
"""Deletes a workflow and its associated data."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Verify workflow exists
workflow = serviceChat.getWorkflow(workflowId)
workflow = interfaceChat.getWorkflow(workflowId)
if not workflow:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
@ -293,7 +293,7 @@ async def delete_workflow(
)
# Delete workflow
success = serviceChat.deleteWorkflow(workflowId)
success = interfaceChat.deleteWorkflow(workflowId)
if not success:
raise HTTPException(
@ -328,10 +328,10 @@ async def delete_workflow_message(
"""Delete a message from a workflow."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Verify workflow exists
workflow = serviceChat.getWorkflow(workflowId)
workflow = interfaceChat.getWorkflow(workflowId)
if not workflow:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
@ -339,7 +339,7 @@ async def delete_workflow_message(
)
# Delete the message
success = serviceChat.deleteWorkflowMessage(workflowId, messageId)
success = interfaceChat.deleteWorkflowMessage(workflowId, messageId)
if not success:
raise HTTPException(
@ -351,7 +351,7 @@ async def delete_workflow_message(
messageIds = workflow.get("messageIds", [])
if messageId in messageIds:
messageIds.remove(messageId)
serviceChat.updateWorkflow(workflowId, {"messageIds": messageIds})
interfaceChat.updateWorkflow(workflowId, {"messageIds": messageIds})
return {
"workflowId": workflowId,
@ -379,10 +379,10 @@ async def delete_file_from_message(
"""Delete a file reference from a message in a workflow."""
try:
# Get service container
serviceChat = getServiceChat(currentUser)
interfaceChat = getServiceChat(currentUser)
# Verify workflow exists
workflow = serviceChat.getWorkflow(workflowId)
workflow = interfaceChat.getWorkflow(workflowId)
if not workflow:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
@ -390,7 +390,7 @@ async def delete_file_from_message(
)
# Delete file reference from message
success = serviceChat.deleteFileFromMessage(workflowId, messageId, fileId)
success = interfaceChat.deleteFileFromMessage(workflowId, messageId, fileId)
if not success:
raise HTTPException(

View file

@ -13,8 +13,8 @@ from slowapi import Limiter
from slowapi.util import get_remote_address
from modules.shared.configuration import APP_CONFIG
from modules.interfaces.serviceAppClass import getRootInterface
from modules.interfaces.serviceAppModel import Session, AuthEvent, UserPrivilege, User
from modules.interfaces.interfaceAppObjects import getRootInterface
from modules.interfaces.interfaceAppModel import Session, AuthEvent, UserPrivilege, User
# Get Config Data
SECRET_KEY = APP_CONFIG.get("APP_JWT_SECRET_SECRET")

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ import base64
import logging
import uuid
from modules.interfaces.serviceChatModel import (
from modules.interfaces.interfaceChatModel import (
ChatDocument,
ExtractedContent
)

View file

@ -3,10 +3,10 @@ import logging
from datetime import datetime, UTC
import uuid
from modules.interfaces.serviceAppClass import User
from modules.interfaces.interfaceAppObjects import User
from modules.interfaces.serviceChatModel import (UserInputRequest, ChatMessage, ChatWorkflow)
from modules.interfaces.serviceChatClass import ChatInterface
from modules.interfaces.interfaceChatModel import (UserInputRequest, ChatMessage, ChatWorkflow)
from modules.interfaces.interfaceChatObjects import ChatObjects
from modules.workflow.managerChat import ChatManager
logger = logging.getLogger(__name__)
@ -18,7 +18,7 @@ class WorkflowStoppedException(Exception):
class WorkflowManager:
"""Manager for workflow processing and coordination"""
def __init__(self, chatInterface: ChatInterface, currentUser: User):
def __init__(self, chatInterface: ChatObjects, currentUser: User):
self.chatInterface = chatInterface
self.chatManager = ChatManager(currentUser)
self.currentUser = currentUser

View file

@ -9,7 +9,7 @@ from pathlib import Path
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup
from modules.interfaces.serviceChatModel import (
from modules.interfaces.interfaceChatModel import (
ChatDocument,
ExtractedContent,
ContentItem,
@ -578,7 +578,7 @@ class DocumentProcessor:
"""
# Get AI response
response = await self.serviceManagement.callAi([
response = await self.interfaceComponent.callAi([
{"role": "system", "content": "You are an expert at extracting relevant information from documents."},
{"role": "user", "content": aiPrompt}
])
@ -813,69 +813,4 @@ class DocumentProcessor:
except Exception:
return [content]
async def _extractText(self, content: bytes, mimeType: str) -> str:
"""Extract text content from various text formats"""
try:
textContent = content.decode('utf-8')
return textContent
except UnicodeDecodeError:
logger.warning(f"Failed to decode text content as UTF-8 for {mimeType}")
return ""
async def _extractImage(self, content: bytes, mimeType: str) -> str:
"""Extract text from image content using OCR"""
try:
imageContent = Image.open(io.BytesIO(content))
text = pytesseract.image_to_string(imageContent)
return text
except Exception as e:
logger.error(f"Error extracting text from image: {str(e)}")
return ""
async def _extractVideo(self, content: bytes, mimeType: str) -> str:
"""Extract text from video content"""
try:
videoContent = io.BytesIO(content)
# TODO: Implement video text extraction
return "Video content extraction not implemented"
except Exception as e:
logger.error(f"Error extracting text from video: {str(e)}")
return ""
async def _extractAudio(self, content: bytes, mimeType: str) -> str:
"""Extract text from audio content using speech recognition"""
try:
audioContent = io.BytesIO(content)
# TODO: Implement audio text extraction
return "Audio content extraction not implemented"
except Exception as e:
logger.error(f"Error extracting text from audio: {str(e)}")
return ""
async def _extractJson(self, content: bytes, mimeType: str) -> str:
"""Extract text from JSON content"""
try:
jsonContent = json.loads(content.decode('utf-8'))
return json.dumps(jsonContent, indent=2)
except Exception as e:
logger.error(f"Error extracting text from JSON: {str(e)}")
return ""
async def _extractXml(self, content: bytes, mimeType: str) -> str:
"""Extract text from XML content"""
try:
xmlContent = content.decode('utf-8')
return xmlContent
except Exception as e:
logger.error(f"Error extracting text from XML: {str(e)}")
return ""
async def _extractCsv(self, content: bytes, mimeType: str) -> str:
"""Extract text from CSV content"""
try:
csvContent = content.decode('utf-8')
return csvContent
except Exception as e:
logger.error(f"Error extracting text from CSV: {str(e)}")
return ""

View file

@ -3,14 +3,14 @@ import importlib
import pkgutil
import inspect
from typing import Dict, Any, List, Optional
from modules.interfaces.serviceAppClass import User
from modules.interfaces.serviceChatModel import (
from modules.interfaces.interfaceAppModel import User, UserConnection
from modules.interfaces.interfaceChatModel import (
TaskStatus, ChatDocument, TaskItem, TaskAction, TaskResult,
ChatStat, ChatLog, ChatMessage, ChatWorkflow, UserConnection
ChatStat, ChatLog, ChatMessage, ChatWorkflow
)
from modules.interfaces.interfaceAi import interfaceAi
from modules.interfaces.serviceChatClass import getInterface as getChatInterface
from modules.interfaces.serviceManagementClass import getInterface as getFileInterface
from modules.interfaces.interfaceAiCalls import interfaceAiCalls
from modules.interfaces.interfaceChatObjects import getInterface as getChatObjects
from modules.interfaces.interfaceComponentObjects import getInterface as getComponentObjects
from modules.workflow.managerDocument import DocumentManager
from modules.methods.methodBase import MethodBase
import uuid
@ -30,18 +30,18 @@ class ServiceContainer:
self.currentTask = None # Initialize current task as None
# Initialize managers
self.interfaceChat = getChatInterface(currentUser)
self.interfaceFiles = getFileInterface(currentUser)
self.interfaceAi = interfaceAi()
self.interfaceChat = getChatObjects(currentUser)
self.interfaceComponent = getComponentObjects(currentUser)
self.interfaceAiCalls = interfaceAiCalls()
self.documentManager = DocumentManager(self)
# Initialize methods catalog
self.methods = None
self.methods = {}
# Discover additional methods
self._discoverMethods()
def _discoverMethods(self):
"""Dynamically discover all method classes in modules.methods package"""
"""Dynamically discover all method classes and their actions in modules.methods package"""
try:
# Import the methods package
methodsPackage = importlib.import_module('modules.methods')
@ -58,10 +58,41 @@ class ServiceContainer:
if (inspect.isclass(item) and
issubclass(item, MethodBase) and
item != MethodBase):
# Instantiate the method and add to service
methodInstance = item()
self.methods[methodInstance.name] = methodInstance
logger.info(f"Discovered method: {methodInstance.name}")
# Instantiate the method
methodInstance = item(self)
# Discover actions from public methods
actions = {}
for methodName, method in inspect.getmembers(methodInstance, predicate=inspect.isfunction):
# Skip private methods and inherited methods
if not methodName.startswith('_') and methodName not in ['execute', 'actions', 'validateParameters']:
# Get method signature
sig = inspect.signature(method)
params = {}
# Convert parameters to action definition
for paramName, param in sig.parameters.items():
if paramName not in ['self', 'authData']:
params[paramName] = {
'type': param.annotation if param.annotation != param.empty else Any,
'required': param.default == param.empty,
'description': param.default.__doc__ if hasattr(param.default, '__doc__') else None
}
# Add action definition
actions[methodName] = {
'description': method.__doc__ or '',
'parameters': params,
'method': method
}
# Add method instance with discovered actions
self.methods[methodInstance.name] = {
'instance': methodInstance,
'description': methodInstance.description,
'actions': actions
}
logger.info(f"Discovered method: {methodInstance.name} with {len(actions)} actions")
except Exception as e:
logger.error(f"Error loading method module {name}: {str(e)}")
@ -76,23 +107,35 @@ class ServiceContainer:
return self.documentManager.extractContent(prompt, document)
def getMethodsCatalog(self) -> Dict[str, Any]:
"""Get catalog of available methods"""
return self.methods
"""Get catalog of available methods and their actions"""
catalog = {}
for methodName, method in self.methods.items():
catalog[methodName] = {
'description': method['description'],
'actions': {
actionName: {
'description': action['description'],
'parameters': action['parameters']
}
for actionName, action in method['actions'].items()
}
}
return catalog
def getMethodsList(self) -> List[str]:
"""Get list of available methods with their signatures"""
methodList = []
for methodName, method in self.methods.items():
for actionName, action in method.actions.items():
# Get parameter types and return type from action signature
for actionName, action in method['actions'].items():
# Get parameter types from action signature
paramTypes = []
for paramName, param in action.parameters.items():
paramTypes.append(f"{paramName}:{param.type}")
for paramName, param in action['parameters'].items():
paramTypes.append(f"{paramName}:{param['type']}")
# Format: method.action([param1:type, param2:type])->resultLabel:type # description
signature = f"{methodName}.{actionName}([{', '.join(paramTypes)}])->{action.resultLabel}:{action.resultType}"
if action.description:
signature += f" # {action.description}"
# Format: method.action([param1:type, param2:type]) # description
signature = f"{methodName}.{actionName}([{', '.join(paramTypes)}])"
if action['description']:
signature += f" # {action['description']}"
methodList.append(signature)
return methodList
@ -205,35 +248,137 @@ class ServiceContainer:
return []
def getConnectionReferenceList(self) -> List[Dict[str, str]]:
"""Get list of connection references sorted by authority"""
return self._getConnectionReferences()
"""Get list of all UserConnection objects as references"""
connections = []
for conn in self.user.connections:
connections.append({
"connectionReference": f"connection_{conn.id}_{conn.authority}",
"authority": conn.authority
})
# Sort by authority
return sorted(connections, key=lambda x: x["authority"])
def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str:
"""Get connection reference from UserConnection"""
return f"connection_{connection.id}_{connection.authority}"
def getUserConnectionFromConnectionReference(self, reference: str) -> UserConnection:
"""Get UserConnection from connection reference"""
return self._getUserConnectionByReference(reference)
def getMessageSummary(self, message: ChatMessage) -> Dict[str, List[Dict[str, Any]]]:
"""Get message summary"""
return {
"chat": self._getChatMessageSummaries(),
"history": self._getHistoryMessageSummaries()
}
def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]:
"""Get UserConnection from reference string"""
try:
# Parse reference format: connection_{id}_{authority}
parts = connectionReference.split('_')
if len(parts) != 3 or parts[0] != "connection":
return None
conn_id = parts[1]
authority = parts[2]
# Find matching connection
for conn in self.user.connections:
if str(conn.id) == conn_id and conn.authority == authority:
return conn
return None
except Exception as e:
logger.error(f"Error parsing connection reference: {str(e)}")
return None
async def summarizeChat(self, messages: List[ChatMessage]) -> str:
"""
Summarize chat messages from last to first message with status="first"
Args:
messages: List of chat messages to summarize
Returns:
str: Summary of the chat in user's language
"""
try:
# Get messages from last to first, stopping at first message with status="first"
relevantMessages = []
for msg in reversed(messages):
relevantMessages.append(msg)
if msg.status == "first":
break
# Create prompt for AI
prompt = f"""You are an AI assistant providing a summary of a chat conversation.
Please respond in '{self.user.language}' language.
Chat History:
{chr(10).join(f"- {msg.message}" for msg in reversed(relevantMessages))}
Instructions:
1. Summarize the conversation's key points and outcomes
2. Be concise but informative
3. Use a professional but friendly tone
4. Focus on important decisions and next steps if any
Please provide a comprehensive summary of this conversation."""
# Get summary using AI
return await self.interfaceAiCalls.callAiTextBasic(prompt)
except Exception as e:
logger.error(f"Error summarizing chat: {str(e)}")
return f"Error summarizing chat: {str(e)}"
async def summarizeMessage(self, message: ChatMessage) -> str:
"""
Summarize a single chat message
Args:
message: Chat message to summarize
Returns:
str: Summary of the message in user's language
"""
try:
# Create prompt for AI
prompt = f"""You are an AI assistant providing a summary of a chat message.
Please respond in '{self.user.language}' language.
Message:
{message.message}
Instructions:
1. Summarize the key points of this message
2. Be concise but informative
3. Use a professional but friendly tone
4. Focus on important information and any actions needed
Please provide a clear summary of this message."""
# Get summary using AI
return await self.interfaceAiCalls.callAiTextBasic(prompt)
except Exception as e:
logger.error(f"Error summarizing message: {str(e)}")
return f"Error summarizing message: {str(e)}"
def callAiTextBasic(self, prompt: str, context: str = None) -> str:
"""Basic text processing using OpenAI"""
return self.interfaceAiCalls.callAiTextBasic(prompt, context)
def callAiTextAdvanced(self, prompt: str, context: str = None) -> str:
"""Advanced text processing using Anthropic"""
return self.interfaceAiCalls.callAiTextAdvanced(prompt, context)
def callAiImageBasic(self, prompt: str, imageData: bytes, mimeType: str) -> str:
"""Basic image processing using OpenAI"""
return self.interfaceAiCalls.callAiImageBasic(prompt, imageData, mimeType)
def callAiImageAdvanced(self, prompt: str, imageData: bytes, mimeType: str) -> str:
"""Advanced image processing using Anthropic"""
return self.interfaceAiCalls.callAiImageAdvanced(prompt, imageData, mimeType)
def getFileInfo(self, fileId: str) -> Dict[str, Any]:
"""Get file information"""
return self.interfaceComponent.getFileInfo(fileId)
def getFileData(self, fileId: str) -> bytes:
"""Get file data by ID"""
return self.interfaceFiles.getFileData(fileId)
def callAiBasic(self, prompt: str, context: str = None, complexityFlag: bool = False) -> str:
"""Call basic AI service"""
return self.interfaceAi.callAiBasic(prompt, context, complexityFlag)
def callAiImage(self, imageData: bytes, mimeType: str, prompt: str) -> str:
"""Call AI image service"""
return self.interfaceAi.callAiImage(imageData, mimeType, prompt)
return self.interfaceComponent.getFileData(fileId)
def createFile(self, fileName: str, mimeType: str, content: str, base64encoded: bool = False) -> str:
"""Create new file and return its ID"""
@ -244,14 +389,14 @@ class ServiceContainer:
content_bytes = content.encode('utf-8')
# First create the file metadata
file_item = self.interfaceFiles.createFile(
file_item = self.interfaceComponent.createFile(
name=fileName,
mimeType=mimeType,
size=len(content_bytes)
)
# Then store the file data
self.interfaceFiles.createFileData(file_item.id, content_bytes)
self.interfaceComponent.createFileData(file_item.id, content_bytes)
return file_item.id
@ -261,7 +406,7 @@ class ServiceContainer:
file_id = self.createFile(fileName, mimeType, content, base64encoded)
# Get file info for metadata
file_info = self.interfaceFiles.getFile(file_id)
file_info = self.interfaceComponent.getFile(file_id)
# Create document with file reference
return ChatDocument(
@ -271,85 +416,25 @@ class ServiceContainer:
fileSize=file_info.fileSize,
mimeType=mimeType
)
def getFileInfo(self, fileId: str) -> Dict[str, Any]:
"""Get file information"""
return self.interfaceFiles.getFileInfo(fileId)
# ===== Private Methods =====
def _executeMethodAction(self, parameters: Dict[str, Any]) -> Any:
"""Execute method action with parameters"""
method = parameters.get('method')
action = parameters.get('action')
if method in self.methods and action in self.methods[method]:
return self.methods[method][action](**parameters.get('parameters', {}))
raise ValueError(f"Unknown method or action: {method}.{action}")
def _executeForEach(self, items: List[Any], action: callable) -> List[Any]:
"""Execute forEach operation"""
results = []
for item in items:
try:
result = action(item)
results.append(result)
except Exception as e:
logger.error(f"Error executing forEach action: {str(e)}")
results.append(None)
return results
def _executeAiCall(self, prompt: str, documents: List[Dict[str, Any]]) -> List[Any]:
"""Execute AI call with documents"""
async def executeMethod(self, methodName: str, actionName: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Execute a method action"""
try:
# Process each document
results = []
for doc in documents:
content = self.extractContent(prompt, doc)
results.append(content)
return results
if methodName not in self.methods:
raise ValueError(f"Unknown method: {methodName}")
method = self.methods[methodName]
if actionName not in method['actions']:
raise ValueError(f"Unknown action: {actionName} for method {methodName}")
action = method['actions'][actionName]
# Execute the action
return await action['method'](parameters, authData)
except Exception as e:
logger.error(f"Error executing AI call: {str(e)}")
return []
def _executeSharePointQuery(self, connection: str, site_query: str, file_query: str, content_query: str) -> List[Dict[str, str]]:
"""Execute SharePoint query"""
# TODO: Implement SharePoint query
return []
def _executeSharePointDownload(self, connection: str, filepath: str) -> str:
"""Execute SharePoint download"""
# TODO: Implement SharePoint download
return ""
def _getChatDocumentReferences(self) -> List[Dict[str, str]]:
"""Get chat document references"""
# TODO: Implement chat document references
return []
def _getHistoryDocumentReferences(self) -> List[Dict[str, str]]:
"""Get history document references"""
# TODO: Implement history document references
return []
def _getConnectionReferences(self) -> List[Dict[str, str]]:
"""Get connection references"""
# TODO: Implement connection references
return []
def _getUserConnectionByReference(self, reference: str) -> UserConnection:
"""Get user connection by reference"""
# TODO: Implement user connection lookup
pass
def _getChatMessageSummaries(self) -> List[Dict[str, Any]]:
"""Get chat message summaries"""
# TODO: Implement chat message summaries
return []
def _getHistoryMessageSummaries(self) -> List[Dict[str, Any]]:
"""Get history message summaries"""
# TODO: Implement history message summaries
return []
logger.error(f"Error executing method {methodName}.{actionName}: {str(e)}")
raise
# Create singleton instance
serviceObject = None

View file

@ -1,84 +1,8 @@
TODO
- documenthandling to review with document-file-filedata --> redundant and too complex --> to have ChatDocument with label and reference to fileid with function to get metadate directly from file object
- prompt for task definition to fix
- implement all functions from service object correctly
- result parsing: execute task actions by using data references stepwise, all documents in TaskResults to save as ChatDocuments, to be available for next action.
- action execution: to use conversion functions, user DocumentReference and ConnectionReference
ADAPT:
can you analyse the following specification:
- clear what is required?
- clear what to do?
- questions?
please give a summarized feedback before implementing
How does the task process need to work:
- specification for task-prompt below ensures, that a json with actions is produced, where references are clear, always having uuid integrated, only valid references
- ai call result to be validated against json specification before proceeding, proper error handling for retry or abort depending on error (e.g. is ai down then to abort, if ai error to proceed with error to potentially fix it, etc.)
- then to process the action list.
Resulting specification Prompt for task definition:
- original user input (summary)
- prompt for task to do
- method list
- workflow's history (list of messageSummary)
- available documents (list of documentReference):
- available connections (list of connectionReference):
- instructions:
- rules for result: status (enum), feedback (to tell what is done and what needed next. Only to to the tasks, which are possible with the available methods, referencing and data, rest to do in next round)
- available data to use: list of documentReference, list of connectionReference
- methods usage:
- syntax: method.action([parameter:type])->resultLabel:type (reference note: resultLabel to be stored in TaskAction.execResultLabel for later use)
- resultLabel to be in format documentList_<generated uuid>_<generated label>
- sequence of method.action to be the sequence ot processing
- as parameter only to use available items of documentReference or connectionReference or resultLabel from a previous method.action
- required result format: json, nothing else
FINALIZE:
service (ServiceContainer)
user <-- currentUser
workflow <- workflow
tasks <- workflow.tasks
statusEnums <- serviceChatModel.TaskStatus
chatDatabase.* <-- serviceChatClass(user)
methods
method.action([parameter:type])->resultLabel:type
operator.forEach([items:List[item], action:method.action])->resultList:List[]
operator.aiCall([prompt:str, extractedDocumentContent:List[{"document":documentReference,"promptForContentExtraction":str}]])->resultList:List[]
sharepoint.query([connectionReference:str, site_query:str, file_query:str, content_query:str])->resultList:List[{"filepath":str,"extractedContent":str}]
sharepoint.download([connectionReference:str, filepath:str])->documentReference:str
functions
ok extractContent(prompt,documentReference):str <- managerDocument.extractContent(prompt,FilePreview)
ok getMethodsCatalog():{...} <- to import dynamically all methods from the files "method*.py" in folder modules/methods
ok getMethodsList():[str] <- to transform result from getMethodsCatalog into a list with the method items in format "method.action([parameter:type])->resultLabel:type # description"
ok getDocumentReferenceList():{"chat":[{"documentReference":str,"datetime":str}],"history":[{"documentReference":str,"datetime":str}]} sorted by datetime desc
getDocumentReferenceFromChatDocument(ChatDocument):"document_"+ChatDocument.id+"_"+ChatDocument.filename
getDocumentReferenceFromTaskResult(TaskResult):"documentList_"+TaskResult.id+"_"+TaskResult.documentsLabel
getChatDocumentsFromDocumentReference(documentReference):List[ChatDocuments]
getConnectionReferenceList():List[{"connectionReference":str,"authority":str}] sorted by authority
getConnectionReferenceFromUserConnection(UserConnection):"connection_"+UserConnection.id+"_"+UserConnection.authority
getUserConnectionFromConnectionReference(documentReference):UserConnection
getMessageSummary(ChatMessage):{"chat":[{"messageSummary":str,"role":str,"success":bool,"sequenceNr":int}],"history":[{"messageSummary":str,"role":str,"success":bool,"sequenceNr":int}]} sorted by datetime desc
getFileData(fileId) <- seriveManagementClass.getFileData(fileid) --> used by ManagerData
callAiBasic(prompt, context, complexityFlag) <- interfaceAi.callAiBasic --> used by managerChat
callAiImage(...) <- interfaceAi.callAiImage --> used by processorDocument
createFile(fileName, mimeType, content, base64encoded):FileItem <- seriveManagementClass.createFile, then seriveManagementClass.createFileData --> used by managerChat
getFileInfo(id):FileItem <- serviceManagementClass.getFile(id) --> used by managerChat
- neutralizer to put back placeholders to the returned data
- referenceHandling and authentication for connections in the method actions
- check methods
- test for workflow backend with userdata
********************

View file

@ -94,7 +94,7 @@ class DocumentService:
## Implementation Steps
1. **Model Cleanup**
- Create new model classes in `serviceChatModel.py`
- Create new model classes in `interfaceChatModel.py`
- Remove deprecated models:
- DocumentExtraction
- DocumentContext
@ -113,7 +113,7 @@ class DocumentService:
3. **UserInput Processing**
- Update `UserInputRequest` processing to use `ChatMessage`
- Implement `processFileIds` in `serviceChatClass`
- Implement `processFileIds` in `interfaceChatObjects`
- Update all references to use new model structure
4. **Method Module Updates**
@ -130,14 +130,14 @@ class DocumentService:
## Files to be Removed/Modified
### To be Removed
1. `DocumentExtraction` class from serviceChatModel.py
2. `DocumentContext` class from serviceChatModel.py
3. `ProcessedDocument` class from serviceChatModel.py
4. `ChatContent` class from serviceChatModel.py
1. `DocumentExtraction` class from interfaceChatModel.py
2. `DocumentContext` class from interfaceChatModel.py
3. `ProcessedDocument` class from interfaceChatModel.py
4. `ChatContent` class from interfaceChatModel.py
5. Direct file access methods from method*.py modules
### To be Modified
1. `serviceChatModel.py`
1. `interfaceChatModel.py`
- Add new model classes
- Remove deprecated classes
- Update existing classes
@ -152,7 +152,7 @@ class DocumentService:
- Remove direct file access
- Update error handling
4. `serviceChatClass.py`
4. `interfaceChatObjects.py`
- Implement processFileIds
- Update document handling

View file

@ -881,8 +881,8 @@ gateway/
│ │ └── methodPowerpoint.py # PowerPoint operations
│ │
│ └── interfaces/
│ ├── serviceChatModel.py # Chat system models and enums
│ └── serviceAppModel.py # Application models including UserConnection
│ ├── interfaceChatModel.py # Chat system models and enums
│ └── interfaceAppModel.py # Application models including UserConnection
```
### 6.2 Implementation Plan
@ -894,8 +894,8 @@ gateway/
- Create new `methods` directory
2. **Model Updates**
- Update `serviceChatModel.py` with new enums and models
- Integrate `UserConnection` from `serviceAppModel.py`
- Update `interfaceChatModel.py` with new enums and models
- Integrate `UserConnection` from `interfaceAppModel.py`
- Update validation logic in respective modules
#### Phase 2: Method Migration