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") logger.info("Application is starting up")
# Initialize root interface to ensure database is properly set up # Initialize root interface to ensure database is properly set up
from modules.interfaces.serviceAppClass import getRootInterface from modules.interfaces.interfaceAppObjects import getRootInterface
getRootInterface() getRootInterface()
yield yield

View file

@ -17,7 +17,7 @@ def loadConfigData():
"maxTokens": int(APP_CONFIG.get('Connector_AiAnthropic_MAX_TOKENS')) "maxTokens": int(APP_CONFIG.get('Connector_AiAnthropic_MAX_TOKENS'))
} }
class ChatService: class AiAnthropic:
"""Connector for communication with the Anthropic API.""" """Connector for communication with the Anthropic API."""
def __init__(self): def __init__(self):
@ -39,7 +39,7 @@ class ChatService:
logger.info(f"Anthropic Connector initialized with model: {self.modelName}") 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. Calls the Anthropic API with the given messages.
@ -49,15 +49,12 @@ class ChatService:
maxTokens: Maximum number of tokens in the response maxTokens: Maximum number of tokens in the response
Returns: Returns:
The response converted to OpenAI format The response in OpenAI format
Raises: Raises:
HTTPException: For errors in API communication HTTPException: For errors in API communication
""" """
try: try:
# Convert OpenAI format to Anthropic format
formattedMessages = self._convertToAnthropicFormat(messages)
# Use parameters from configuration if none were overridden # Use parameters from configuration if none were overridden
if temperature is None: if temperature is None:
temperature = self.config.get("temperature", 0.2) temperature = self.config.get("temperature", 0.2)
@ -68,7 +65,7 @@ class ChatService:
# Create Anthropic API payload # Create Anthropic API payload
payload = { payload = {
"model": self.modelName, "model": self.modelName,
"messages": formattedMessages, "messages": messages,
"temperature": temperature, "temperature": temperature,
"max_tokens": maxTokens "max_tokens": maxTokens
} }
@ -82,110 +79,44 @@ class ChatService:
logger.error(f"Anthropic API error: {response.status_code} - {response.text}") logger.error(f"Anthropic API error: {response.status_code} - {response.text}")
raise HTTPException(status_code=500, detail="Error communicating with Anthropic API") raise HTTPException(status_code=500, detail="Error communicating with Anthropic API")
# Convert response from Anthropic format to OpenAI format # Parse response
anthropicResponse = response.json() 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: except Exception as e:
logger.error(f"Error calling Anthropic API: {str(e)}") logger.error(f"Error calling Anthropic API: {str(e)}")
raise HTTPException(status_code=500, detail=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]]: async def callAiImage(self, prompt: str, imageData: Union[str, bytes], mimeType: str = None) -> str:
"""
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:
""" """
Analyzes an image using Anthropic's vision capabilities. 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')) "maxTokens": int(APP_CONFIG.get('Connector_AiOpenai_MAX_TOKENS'))
} }
class AiConnector: class AiOpenai:
"""Connector for communication with the OpenAI API.""" """Connector for communication with the OpenAI API."""
def __init__(self): def __init__(self):
@ -85,7 +85,7 @@ class AiConnector:
logger.error(f"Error calling OpenAI API: {str(e)}") logger.error(f"Error calling OpenAI API: {str(e)}")
raise HTTPException(status_code=500, detail=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. 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 import logging
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
from datetime import datetime from datetime import datetime
from modules.interfaces.serviceAppModel import UserPrivilege, Session, User from modules.interfaces.interfaceAppModel import UserPrivilege, Session, User
# Configure logger # Configure logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

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

View file

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

View file

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

View file

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

View file

@ -3,10 +3,19 @@ from typing import Dict, List, Optional, Any, Literal
from datetime import datetime, UTC from datetime import datetime, UTC
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
import logging import logging
from modules.interfaces.serviceChatModel import MethodResult from modules.interfaces.interfaceChatModel import MethodResult
from functools import wraps
logger = logging.getLogger(__name__) 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: class MethodBase:
"""Base class for all methods""" """Base class for all methods"""

View file

@ -3,85 +3,20 @@ import logging
import ast import ast
import re import re
from modules.methods.methodBase import MethodBase, MethodResult from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MethodCoder(MethodBase): class MethodCoder(MethodBase):
"""Coder method implementation for code operations""" """Coder method implementation for code operations"""
def __init__(self): def __init__(self, serviceContainer: Any):
super().__init__() super().__init__(serviceContainer)
self.name = "coder" self.name = "coder"
self.description = "Handle code operations like analysis, generation, and refactoring" 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: @action
"""Execute coder method""" async def analyze(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
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:
"""Analyze code structure and quality""" """Analyze code structure and quality"""
try: try:
code = parameters["code"] code = parameters["code"]
@ -173,7 +108,8 @@ class MethodCoder(MethodBase):
data={"error": f"Analysis failed: {str(e)}"} 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""" """Generate code based on requirements"""
try: try:
requirements = parameters["requirements"] requirements = parameters["requirements"]
@ -216,7 +152,8 @@ class MethodCoder(MethodBase):
data={"error": f"Generation failed: {str(e)}"} 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""" """Refactor code for better quality"""
try: try:
code = parameters["code"] code = parameters["code"]

View file

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

View file

@ -1,202 +1,176 @@
from typing import Dict, Any, Optional """
import logging Outlook method module.
from datetime import datetime, UTC Handles Outlook operations using the Outlook service.
from O365 import Account, MSGraphProtocol """
from modules.methods.methodBase import MethodBase, MethodResult import logging
from modules.models.userConnection import UserConnection 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__) logger = logging.getLogger(__name__)
class MethodOutlook(MethodBase): class MethodOutlook(MethodBase):
"""Outlook method implementation for email operations""" """Outlook method implementation"""
def __init__(self): def __init__(self, serviceContainer):
super().__init__() """Initialize the Outlook method"""
self.name = "outlook" super().__init__(serviceContainer)
self.description = "Handle Outlook email operations like reading and sending emails" 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 Args:
def actions(self) -> Dict[str, Dict[str, Any]]: parameters:
"""Available actions and their parameters""" folder: Folder to read from (default: inbox)
return { query: Search query
"readMails": { maxResults: Maximum number of results
"description": "Read emails from Outlook", includeAttachments: Whether to include attachments
"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"""
try: try:
folder = parameters.get("folder", "inbox") folder = parameters.get("folder", "inbox")
query = parameters.get("query") query = parameters.get("query")
maxResults = parameters.get("maxResults", 10) maxResults = parameters.get("maxResults", 10)
includeAttachments = parameters.get("includeAttachments", False) includeAttachments = parameters.get("includeAttachments", False)
# Create Outlook account # Read emails
account = Account( emails = await self.outlookService.readEmails(
credentials=(userConnection.authToken, userConnection.refreshToken), folder=folder,
protocol=MSGraphProtocol() 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( return self._createResult(
success=True, success=True,
data={ data={
"folder": folder, "folder": folder,
"query": query, "query": query,
"messages": results "emails": emails
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error reading Outlook emails: {e}") logger.error(f"Error reading emails: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Read failed: {str(e)}"} data={"error": str(e)}
) )
async def _sendMail(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: @action
"""Send email through Outlook""" 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: try:
toAddresses = parameters["to"] to = parameters["to"]
subject = parameters["subject"] subject = parameters["subject"]
body = parameters["body"] body = parameters["body"]
ccAddresses = parameters.get("cc", [])
bccAddresses = parameters.get("bcc", [])
attachments = parameters.get("attachments", []) attachments = parameters.get("attachments", [])
# Create Outlook account # Send email
account = Account( messageId = await self.outlookService.sendEmail(
credentials=(userConnection.authToken, userConnection.refreshToken), to=to,
protocol=MSGraphProtocol() 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( return self._createResult(
success=True, success=True,
data={ data={
"to": toAddresses, "messageId": messageId,
"subject": subject, "to": to,
"sent": datetime.now(UTC).isoformat() "subject": subject
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error sending Outlook email: {e}") logger.error(f"Error sending email: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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 PowerPoint method module.
import os Handles PowerPoint operations using the PowerPoint service.
from pathlib import Path """
from modules.methods.methodBase import MethodBase, MethodResult import logging
from modules.models.userConnection import UserConnection from typing import Dict, Any, List, Optional
from modules.models.account import Account from datetime import datetime
from modules.protocols.msGraphProtocol import MSGraphProtocol
from modules.interfaces.interfacePowerpoint import PowerpointService
from modules.methods.methodBase import MethodBase, MethodResult, action
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MethodPowerpoint(MethodBase): class MethodPowerpoint(MethodBase):
"""Powerpoint method implementation for PowerPoint operations""" """PowerPoint method implementation"""
def __init__(self): def __init__(self, serviceContainer):
super().__init__() """Initialize the PowerPoint method"""
self.name = "powerpoint" super().__init__(serviceContainer)
self.description = "Handle PowerPoint operations like reading, writing, and converting presentations" self.powerpointService = PowerpointService(serviceContainer)
@action
async def read(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Read PowerPoint presentation
@property Args:
def actions(self) -> Dict[str, Dict[str, Any]]: parameters:
"""Available actions and their parameters""" fileId: ID of the PowerPoint file
return { includeSlides: Whether to include slide content
"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"""
try: try:
# Validate parameters fileId = parameters["fileId"]
if not await self.validateParameters(action, parameters): includeSlides = parameters.get("includeSlides", True)
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Invalid parameters for {action}"} data={"error": f"File not found: {fileId}"}
) )
# Get UserConnection from auth_data # Read presentation
if not authData or "userConnection" not in authData: presentation = await self.powerpointService.readPresentation(file, includeSlides)
return self._createResult(
success=False,
data={"error": "UserConnection required for PowerPoint operations"}
)
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: except Exception as e:
logger.error(f"Error executing PowerPoint {action}: {e}") logger.error(f"Error reading PowerPoint: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": str(e)} data={"error": str(e)}
) )
async def _read_presentation(self, parameters: Dict[str, Any], authData: Dict[str, Any]) -> MethodResult: @action
"""Read PowerPoint presentation content""" 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: try:
path = Path(parameters["path"]) fileId = parameters["fileId"]
if not path.exists(): slides = parameters["slides"]
# Get file from service
file = await self.service.interfaceComponent.getFile(fileId)
if not file:
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"File not found: {path}"} data={"error": f"File not found: {fileId}"}
) )
# Determine format if not specified # Write presentation
format = parameters.get("format") await self.powerpointService.writePresentation(file, slides)
if not format:
format = path.suffix[1:] if path.suffix else "pptx"
# TODO: Implement PowerPoint reading using Microsoft Graph API
# This is a placeholder implementation
return self._createResult( return self._createResult(
success=True, success=True,
data={ data={
"path": str(path), "fileId": fileId,
"format": format, "slideCount": len(slides)
"slides": [
{
"number": 1,
"title": "Example Slide",
"content": "Example content",
"notes": "Example notes" if parameters.get("includeNotes", False) else None
}
]
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error reading presentation: {e}") logger.error(f"Error writing PowerPoint: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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: @action
"""Write content to PowerPoint presentation""" 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: try:
path = Path(parameters["path"]) fileId = parameters["fileId"]
format = parameters["format"]
# Create directory if it doesn't exist # Get file from service
path.parent.mkdir(parents=True, exist_ok=True) file = await self.service.interfaceComponent.getFile(fileId)
if not file:
# 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():
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Source file not found: {source_path}"} data={"error": f"File not found: {fileId}"}
) )
# Determine formats if not specified # Convert presentation
source_format = parameters.get("sourceFormat") convertedFile = await self.powerpointService.convertPresentation(file, format)
if not source_format:
source_format = source_path.suffix[1:] if source_path.suffix else "pptx"
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( return self._createResult(
success=True, success=True,
data={ data={
"sourcePath": str(source_path), "fileId": fileId,
"targetPath": str(target_path), "format": format,
"sourceFormat": source_format, "convertedFileId": convertedFile.id
"targetFormat": target_format
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error converting presentation: {e}") logger.error(f"Error converting PowerPoint: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Conversion failed: {str(e)}"} data={"error": str(e)}
) )
async def _createPresentation(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: @action
"""Create a new PowerPoint presentation""" 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: try:
title = parameters["title"] fileName = parameters["fileName"]
template = parameters.get("template") template = parameters.get("template")
# Create PowerPoint account
account = Account(
credentials=(userConnection.authToken, userConnection.refreshToken),
protocol=MSGraphProtocol()
)
# Get drive
drive = account.drive()
# Create presentation # Create presentation
if template: file = await self.powerpointService.createPresentation(fileName, 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"
)
return self._createResult( return self._createResult(
success=True, success=True,
data={ data={
"id": newFile.object_id, "fileId": file.id,
"name": newFile.name, "fileName": fileName,
"webUrl": newFile.web_url "template": template
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error creating PowerPoint presentation: {e}") logger.error(f"Error creating PowerPoint: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Create failed: {str(e)}"} data={"error": str(e)}
) )
async def _addSlide(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: @action
"""Add a new slide to presentation""" 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: try:
presentationId = parameters["presentationId"] fileId = parameters["fileId"]
layout = parameters.get("layout", "title") layout = parameters.get("layout", "title")
title = parameters.get("title") content = parameters.get("content", {})
# Create PowerPoint account # Get file from service
account = Account( file = await self.service.interfaceComponent.getFile(fileId)
credentials=(userConnection.authToken, userConnection.refreshToken), if not file:
protocol=MSGraphProtocol() return self._createResult(
) success=False,
data={"error": f"File not found: {fileId}"}
# Get drive )
drive = account.drive()
# Get presentation
presentation = drive.get_item_by_id(presentationId)
# Add slide # Add slide
slide = presentation.add_slide(layout=layout) slideId = await self.powerpointService.addSlide(file, layout, content)
if title:
slide.title = title
return self._createResult( return self._createResult(
success=True, success=True,
data={ data={
"slideId": slide.object_id, "fileId": fileId,
"layout": layout, "slideId": slideId,
"title": title "layout": layout
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error adding PowerPoint slide: {e}") logger.error(f"Error adding slide: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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: @action
"""Add content to a slide""" 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: try:
presentationId = parameters["presentationId"] fileId = parameters["fileId"]
slideId = parameters["slideId"] slideId = parameters["slideId"]
contentType = parameters["contentType"]
content = parameters["content"] content = parameters["content"]
position = parameters.get("position", {"x": 0, "y": 0})
# Create PowerPoint account # Get file from service
account = Account( file = await self.service.interfaceComponent.getFile(fileId)
credentials=(userConnection.authToken, userConnection.refreshToken), if not file:
protocol=MSGraphProtocol() return self._createResult(
) success=False,
data={"error": f"File not found: {fileId}"}
# 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"]
) )
elif contentType == "image":
shape = slide.add_picture( # Add content
image_path=content, await self.powerpointService.addContent(file, slideId, 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}")
return self._createResult( return self._createResult(
success=True, success=True,
data={ data={
"shapeId": shape.object_id, "fileId": fileId,
"contentType": contentType, "slideId": slideId
"position": position
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error adding PowerPoint content: {e}") logger.error(f"Error adding content: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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 SharePoint method module.
from datetime import datetime, UTC Handles SharePoint operations using the SharePoint service.
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
from modules.methods.methodBase import MethodBase, MethodResult import logging
from modules.models.userConnection import UserConnection 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__) logger = logging.getLogger(__name__)
class MethodSharepoint(MethodBase): class MethodSharepoint(MethodBase):
"""SharePoint method implementation for document operations""" """SharePoint method implementation"""
def __init__(self): def __init__(self, serviceContainer):
super().__init__() """Initialize the SharePoint method"""
self.name = "sharepoint" super().__init__(serviceContainer)
self.description = "Handle SharePoint document operations like search, read, and write" self.sharepointService = SharepointService(serviceContainer)
@action
async def search(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""
Search SharePoint content
@property Args:
def actions(self) -> Dict[str, Dict[str, Any]]: parameters:
"""Available actions and their parameters""" query: Search query
return { siteId: Site ID to search in
"search": { contentType: Content type to search for
"description": "Search SharePoint documents", maxResults: Maximum number of results
"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"""
try: 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"] query = parameters["query"]
listName = parameters.get("listName") siteId = parameters.get("siteId")
contentType = parameters.get("contentType")
maxResults = parameters.get("maxResults", 10) maxResults = parameters.get("maxResults", 10)
# Create SharePoint context # Search content
ctx = ClientContext(siteUrl).with_credentials( results = await self.sharepointService.searchContent(
UserCredential(userConnection.authToken, userConnection.refreshToken) 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( return self._createResult(
success=True, success=True,
data={ data={
"query": query, "query": query,
"siteId": siteId,
"contentType": contentType,
"results": results "results": results
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error searching SharePoint documents: {e}") logger.error(f"Error searching SharePoint: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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: @action
"""Read SharePoint document content""" 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: try:
siteUrl = parameters["siteUrl"] itemId = parameters["itemId"]
fileUrl = parameters["fileUrl"] siteId = parameters.get("siteId")
listId = parameters.get("listId")
# Create SharePoint context # Read item
ctx = ClientContext(siteUrl).with_credentials( item = await self.sharepointService.readItem(
UserCredential(userConnection.authToken, userConnection.refreshToken) 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( return self._createResult(
success=True, success=True,
data={ data={
"url": fileUrl, "itemId": itemId,
"content": file_content.content.decode('utf-8'), "siteId": siteId,
"modified": file.properties["TimeLastModified"], "listId": listId,
"size": file.properties["Length"] "item": item
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error reading SharePoint document: {e}") logger.error(f"Error reading SharePoint item: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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: @action
"""Write content to SharePoint document""" 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: try:
siteUrl = parameters["siteUrl"] siteId = parameters["siteId"]
fileUrl = parameters["fileUrl"] listId = parameters["listId"]
content = parameters["content"] item = parameters["item"]
contentType = parameters.get("contentType", "text/plain")
# Create SharePoint context # Write item
ctx = ClientContext(siteUrl).with_credentials( itemId = await self.sharepointService.writeItem(
UserCredential(userConnection.authToken, userConnection.refreshToken) 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( return self._createResult(
success=True, success=True,
data={ data={
"url": fileUrl, "siteId": siteId,
"modified": datetime.now(UTC).isoformat(), "listId": listId,
"size": len(content.encode('utf-8')) "itemId": itemId
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error writing SharePoint document: {e}") logger.error(f"Error writing SharePoint item: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Write failed: {str(e)}"} data={"error": str(e)}
) )
async def _readList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: @action
"""Read items from SharePoint list""" 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: try:
siteUrl = parameters["siteUrl"] listId = parameters["listId"]
listName = parameters["listName"] siteId = parameters.get("siteId")
query = parameters.get("query") query = parameters.get("query")
fields = parameters.get("fields", ["*"]) maxResults = parameters.get("maxResults", 100)
# Create SharePoint account # Read list
account = Account( items = await self.sharepointService.readList(
credentials=(userConnection.authToken, userConnection.refreshToken), listId=listId,
protocol=MSGraphProtocol() 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( return self._createResult(
success=True, success=True,
data={ data={
"siteUrl": siteUrl, "listId": listId,
"listName": listName, "siteId": siteId,
"items": items "items": items
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error reading SharePoint list: {e}") logger.error(f"Error reading SharePoint list: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Read failed: {str(e)}"} data={"error": str(e)}
) )
async def _writeList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: @action
"""Write items to SharePoint list""" 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: try:
siteUrl = parameters["siteUrl"] siteId = parameters["siteId"]
listName = parameters["listName"] listId = parameters["listId"]
items = parameters["items"] items = parameters["items"]
# Create SharePoint account # Write items
account = Account( itemIds = await self.sharepointService.writeList(
credentials=(userConnection.authToken, userConnection.refreshToken), siteId=siteId,
protocol=MSGraphProtocol() 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( return self._createResult(
success=True, success=True,
data={ data={
"siteUrl": siteUrl, "siteId": siteId,
"listName": listName, "listId": listId,
"results": results "itemIds": itemIds
} }
) )
except Exception as e: 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( return self._createResult(
success=False, success=False,
data={"error": f"Write failed: {str(e)}"} data={"error": str(e)}
) )
async def _createList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: @action
"""Create a new SharePoint list""" 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: try:
siteUrl = parameters["siteUrl"] siteId = parameters["siteId"]
listName = parameters["listName"] name = parameters["name"]
description = parameters.get("description") description = parameters.get("description")
template = parameters.get("template", "generic") template = parameters.get("template", "genericList")
fields = parameters.get("fields", []) 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 # Create list
list = site.create_list( listId = await self.sharepointService.createList(
name=listName, siteId=siteId,
name=name,
description=description, 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( return self._createResult(
success=True, success=True,
data={ data={
"siteUrl": siteUrl, "siteId": siteId,
"listName": listName, "listId": listId,
"id": list.id, "name": name
"webUrl": list.web_url
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error creating SharePoint list: {e}") logger.error(f"Error creating SharePoint list: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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 Web method module.
import aiohttp Handles web operations using the web service.
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
from modules.methods.methodBase import MethodBase, MethodResult import logging
from modules.shared.configuration import APP_CONFIG 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__) logger = logging.getLogger(__name__)
class MethodWeb(MethodBase): class MethodWeb(MethodBase):
"""Web method implementation for web operations""" """Web method implementation"""
def __init__(self): def __init__(self, serviceContainer):
super().__init__() """Initialize the web method"""
self.name = "web" super().__init__(serviceContainer)
self.description = "Handle web operations like search, crawl, and content extraction" 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 Args:
self.srcApikey = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_APIKEY", "") parameters:
self.srcEngine = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_ENGINE", "google") query: Search query
self.srcCountry = APP_CONFIG.get("Agent_Webcrawler_SERPAPI_COUNTRY", "auto") engine: Search engine to use (google, bing)
self.maxResults = int(APP_CONFIG.get("Agent_Webcrawler_SERPAPI_MAX_SEARCH_RESULTS", "5")) maxResults: Maximum number of results
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"""
try: try:
query = parameters["query"] query = parameters["query"]
engine = parameters.get("engine", "google")
maxResults = parameters.get("maxResults", 10) maxResults = parameters.get("maxResults", 10)
filters = parameters.get("filters", {})
searchEngine = parameters.get("searchEngine", "google")
# Implement search using different engines # Search web
if searchEngine.lower() == "google": results = await self.webService.searchContent(
# Use Google Custom Search API query=query,
# TODO: Implement Google Custom Search API integration engine=engine,
results = await self._google_search(query, maxResults, filters) maxResults=maxResults
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}"}
)
return self._createResult( return self._createResult(
success=True, success=True,
data={ data={
"query": query, "query": query,
"engine": searchEngine, "engine": engine,
"results": results "results": results
} }
) )
except Exception as e: except Exception as e:
logger.error(f"Error searching web: {e}") logger.error(f"Error searching web: {str(e)}")
return self._createResult( return self._createResult(
success=False, 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: @action
"""Search using Google Custom Search API""" async def crawl(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
# TODO: Implement Google Custom Search API """
# This is a placeholder implementation Crawl web page
return [
{ Args:
"title": "Example Result", parameters:
"url": "https://example.com", url: URL to crawl
"snippet": "Example search result snippet", depth: Crawl depth
"source": "google" followLinks: Whether to follow links
} extractContent: Whether to extract content
] """
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"""
try: try:
url = parameters["url"] url = parameters["url"]
depth = parameters.get("depth", 1) depth = parameters.get("depth", 1)
followLinks = parameters.get("followLinks", False) followLinks = parameters.get("followLinks", False)
includeImages = parameters.get("includeImages", False) extractContent = parameters.get("extractContent", True)
respectRobots = parameters.get("respectRobots", True)
# Check robots.txt if required # Crawl page
if respectRobots: results = await self.webService.crawlPage(
if not await self._check_robots_txt(url): url=url,
return self._createResult( depth=depth,
success=False, followLinks=followLinks,
data={"error": "Crawling not allowed by robots.txt"} 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: except Exception as e:
logger.error(f"Error crawling page: {e}") logger.error(f"Error crawling web page: {str(e)}")
return self._createResult( return self._createResult(
success=False, success=False,
data={"error": f"Crawl failed: {str(e)}"} data={"error": str(e)}
) )
def _get_meta_description(self, soup: BeautifulSoup) -> Optional[str]: @action
"""Extract meta description from HTML""" async def extract(self, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
metaDesc = soup.find('meta', attrs={'name': 'description'}) """
if metaDesc: Extract content from web page
return metaDesc.get('content')
return None Args:
parameters:
def _is_valid_url(self, url: str) -> bool: url: URL to extract from
"""Check if URL is valid""" selectors: CSS selectors to extract
format: Output format (text, html, json)
"""
try: try:
result = urlparse(url) url = parameters["url"]
return all([result.scheme, result.netloc]) selectors = parameters.get("selectors", ["body"])
except: format = parameters.get("format", "text")
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"
async with aiohttp.ClientSession() as session: # Extract content
async with session.get(robotsUrl, headers={"User-Agent": self.userAgent}, timeout=self.timeout) as response: content = await self.webService.extractContent(
if response.status == 200: url=url,
robotsContent = await response.text() selectors=selectors,
format=format
# 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')
# Try to get language from meta tag return self._createResult(
metaLang = soup.find('meta', attrs={'http-equiv': 'content-language'}) success=True,
if metaLang: data={
return metaLang.get('content', 'en') "url": url,
"format": format,
# Try to get language from meta charset "content": content
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
except Exception as e: except Exception as e:
logger.warning(f"Error detecting language: {str(e)}") logger.error(f"Error extracting web content: {str(e)}")
return 'en' # Default to English on error 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.shared.configuration import APP_CONFIG
from modules.security.auth import limiter, getCurrentUser from modules.security.auth import limiter, getCurrentUser
from modules.interfaces.serviceAppModel import User from modules.interfaces.interfaceAppModel import User
router = APIRouter( router = APIRouter(
prefix="", prefix="",

View file

@ -11,7 +11,7 @@ import logging
from modules.security.auth import limiter, getCurrentUser from modules.security.auth import limiter, getCurrentUser
# Import the attribute definition and helper functions # 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 from modules.shared.attributeUtils import getModelClasses, getModelAttributeDefinitions, AttributeResponse, AttributeDefinition
# Configure logger # Configure logger

View file

@ -10,9 +10,9 @@ from datetime import datetime
import logging import logging
import json 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.security.auth import getCurrentUser, limiter
from modules.interfaces.serviceAppClass import getInterface, getRootInterface from modules.interfaces.interfaceAppObjects import getInterface, getRootInterface
# Configure logger # Configure logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -13,8 +13,8 @@ from slowapi import Limiter
from slowapi.util import get_remote_address from slowapi.util import get_remote_address
from modules.shared.configuration import APP_CONFIG from modules.shared.configuration import APP_CONFIG
from modules.interfaces.serviceAppClass import getRootInterface from modules.interfaces.interfaceAppObjects import getRootInterface
from modules.interfaces.serviceAppModel import Session, AuthEvent, UserPrivilege, User from modules.interfaces.interfaceAppModel import Session, AuthEvent, UserPrivilege, User
# Get Config Data # Get Config Data
SECRET_KEY = APP_CONFIG.get("APP_JWT_SECRET_SECRET") 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 logging
import uuid import uuid
from modules.interfaces.serviceChatModel import ( from modules.interfaces.interfaceChatModel import (
ChatDocument, ChatDocument,
ExtractedContent ExtractedContent
) )

View file

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

View file

@ -9,7 +9,7 @@ from pathlib import Path
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from modules.interfaces.serviceChatModel import ( from modules.interfaces.interfaceChatModel import (
ChatDocument, ChatDocument,
ExtractedContent, ExtractedContent,
ContentItem, ContentItem,
@ -578,7 +578,7 @@ class DocumentProcessor:
""" """
# Get AI response # 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": "system", "content": "You are an expert at extracting relevant information from documents."},
{"role": "user", "content": aiPrompt} {"role": "user", "content": aiPrompt}
]) ])
@ -813,69 +813,4 @@ class DocumentProcessor:
except Exception: except Exception:
return [content] 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 pkgutil
import inspect import inspect
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
from modules.interfaces.serviceAppClass import User from modules.interfaces.interfaceAppModel import User, UserConnection
from modules.interfaces.serviceChatModel import ( from modules.interfaces.interfaceChatModel import (
TaskStatus, ChatDocument, TaskItem, TaskAction, TaskResult, TaskStatus, ChatDocument, TaskItem, TaskAction, TaskResult,
ChatStat, ChatLog, ChatMessage, ChatWorkflow, UserConnection ChatStat, ChatLog, ChatMessage, ChatWorkflow
) )
from modules.interfaces.interfaceAi import interfaceAi from modules.interfaces.interfaceAiCalls import interfaceAiCalls
from modules.interfaces.serviceChatClass import getInterface as getChatInterface from modules.interfaces.interfaceChatObjects import getInterface as getChatObjects
from modules.interfaces.serviceManagementClass import getInterface as getFileInterface from modules.interfaces.interfaceComponentObjects import getInterface as getComponentObjects
from modules.workflow.managerDocument import DocumentManager from modules.workflow.managerDocument import DocumentManager
from modules.methods.methodBase import MethodBase from modules.methods.methodBase import MethodBase
import uuid import uuid
@ -30,18 +30,18 @@ class ServiceContainer:
self.currentTask = None # Initialize current task as None self.currentTask = None # Initialize current task as None
# Initialize managers # Initialize managers
self.interfaceChat = getChatInterface(currentUser) self.interfaceChat = getChatObjects(currentUser)
self.interfaceFiles = getFileInterface(currentUser) self.interfaceComponent = getComponentObjects(currentUser)
self.interfaceAi = interfaceAi() self.interfaceAiCalls = interfaceAiCalls()
self.documentManager = DocumentManager(self) self.documentManager = DocumentManager(self)
# Initialize methods catalog # Initialize methods catalog
self.methods = None self.methods = {}
# Discover additional methods # Discover additional methods
self._discoverMethods() self._discoverMethods()
def _discoverMethods(self): 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: try:
# Import the methods package # Import the methods package
methodsPackage = importlib.import_module('modules.methods') methodsPackage = importlib.import_module('modules.methods')
@ -58,10 +58,41 @@ class ServiceContainer:
if (inspect.isclass(item) and if (inspect.isclass(item) and
issubclass(item, MethodBase) and issubclass(item, MethodBase) and
item != MethodBase): item != MethodBase):
# Instantiate the method and add to service # Instantiate the method
methodInstance = item() methodInstance = item(self)
self.methods[methodInstance.name] = methodInstance
logger.info(f"Discovered method: {methodInstance.name}") # 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: except Exception as e:
logger.error(f"Error loading method module {name}: {str(e)}") logger.error(f"Error loading method module {name}: {str(e)}")
@ -76,23 +107,35 @@ class ServiceContainer:
return self.documentManager.extractContent(prompt, document) return self.documentManager.extractContent(prompt, document)
def getMethodsCatalog(self) -> Dict[str, Any]: def getMethodsCatalog(self) -> Dict[str, Any]:
"""Get catalog of available methods""" """Get catalog of available methods and their actions"""
return self.methods 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]: def getMethodsList(self) -> List[str]:
"""Get list of available methods with their signatures""" """Get list of available methods with their signatures"""
methodList = [] methodList = []
for methodName, method in self.methods.items(): for methodName, method in self.methods.items():
for actionName, action in method.actions.items(): for actionName, action in method['actions'].items():
# Get parameter types and return type from action signature # Get parameter types from action signature
paramTypes = [] paramTypes = []
for paramName, param in action.parameters.items(): for paramName, param in action['parameters'].items():
paramTypes.append(f"{paramName}:{param.type}") paramTypes.append(f"{paramName}:{param['type']}")
# Format: method.action([param1:type, param2:type])->resultLabel:type # description # Format: method.action([param1:type, param2:type]) # description
signature = f"{methodName}.{actionName}([{', '.join(paramTypes)}])->{action.resultLabel}:{action.resultType}" signature = f"{methodName}.{actionName}([{', '.join(paramTypes)}])"
if action.description: if action['description']:
signature += f" # {action.description}" signature += f" # {action['description']}"
methodList.append(signature) methodList.append(signature)
return methodList return methodList
@ -205,35 +248,137 @@ class ServiceContainer:
return [] return []
def getConnectionReferenceList(self) -> List[Dict[str, str]]: def getConnectionReferenceList(self) -> List[Dict[str, str]]:
"""Get list of connection references sorted by authority""" """Get list of all UserConnection objects as references"""
return self._getConnectionReferences() 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: def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str:
"""Get connection reference from UserConnection""" """Get connection reference from UserConnection"""
return f"connection_{connection.id}_{connection.authority}" return f"connection_{connection.id}_{connection.authority}"
def getUserConnectionFromConnectionReference(self, reference: str) -> UserConnection: def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]:
"""Get UserConnection from connection reference""" """Get UserConnection from reference string"""
return self._getUserConnectionByReference(reference) try:
# Parse reference format: connection_{id}_{authority}
def getMessageSummary(self, message: ChatMessage) -> Dict[str, List[Dict[str, Any]]]: parts = connectionReference.split('_')
"""Get message summary""" if len(parts) != 3 or parts[0] != "connection":
return { return None
"chat": self._getChatMessageSummaries(),
"history": self._getHistoryMessageSummaries() 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: def getFileData(self, fileId: str) -> bytes:
"""Get file data by ID""" """Get file data by ID"""
return self.interfaceFiles.getFileData(fileId) return self.interfaceComponent.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)
def createFile(self, fileName: str, mimeType: str, content: str, base64encoded: bool = False) -> str: def createFile(self, fileName: str, mimeType: str, content: str, base64encoded: bool = False) -> str:
"""Create new file and return its ID""" """Create new file and return its ID"""
@ -244,14 +389,14 @@ class ServiceContainer:
content_bytes = content.encode('utf-8') content_bytes = content.encode('utf-8')
# First create the file metadata # First create the file metadata
file_item = self.interfaceFiles.createFile( file_item = self.interfaceComponent.createFile(
name=fileName, name=fileName,
mimeType=mimeType, mimeType=mimeType,
size=len(content_bytes) size=len(content_bytes)
) )
# Then store the file data # 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 return file_item.id
@ -261,7 +406,7 @@ class ServiceContainer:
file_id = self.createFile(fileName, mimeType, content, base64encoded) file_id = self.createFile(fileName, mimeType, content, base64encoded)
# Get file info for metadata # Get file info for metadata
file_info = self.interfaceFiles.getFile(file_id) file_info = self.interfaceComponent.getFile(file_id)
# Create document with file reference # Create document with file reference
return ChatDocument( return ChatDocument(
@ -271,85 +416,25 @@ class ServiceContainer:
fileSize=file_info.fileSize, fileSize=file_info.fileSize,
mimeType=mimeType mimeType=mimeType
) )
def getFileInfo(self, fileId: str) -> Dict[str, Any]: async def executeMethod(self, methodName: str, actionName: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult:
"""Get file information""" """Execute a method action"""
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"""
try: try:
# Process each document if methodName not in self.methods:
results = [] raise ValueError(f"Unknown method: {methodName}")
for doc in documents:
content = self.extractContent(prompt, doc) method = self.methods[methodName]
results.append(content) if actionName not in method['actions']:
return results 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: except Exception as e:
logger.error(f"Error executing AI call: {str(e)}") logger.error(f"Error executing method {methodName}.{actionName}: {str(e)}")
return [] raise
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 []
# Create singleton instance # Create singleton instance
serviceObject = None serviceObject = None

View file

@ -1,84 +1,8 @@
TODO 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 - neutralizer to put back placeholders to the returned data
- prompt for task definition to fix - referenceHandling and authentication for connections in the method actions
- implement all functions from service object correctly - check methods
- result parsing: execute task actions by using data references stepwise, all documents in TaskResults to save as ChatDocuments, to be available for next action. - test for workflow backend with userdata
- 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
******************** ********************

View file

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

View file

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