import logging from typing import Dict, Any, List, Optional, Tuple, Union from modules.datamodels.datamodelChat import PromptPlaceholder from modules.datamodels.datamodelChat import ChatDocument from modules.services.serviceExtraction.mainServiceExtraction import ExtractionService from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, ModelCapabilities, OperationType, Priority from modules.datamodels.datamodelExtraction import ChunkResult, ContentExtracted from modules.datamodels.datamodelWeb import ( WebResearchRequest, WebResearchActionResult, WebResearchDocumentData, WebResearchActionDocument, WebSearchResultItem, ) from modules.interfaces.interfaceAiObjects import AiObjects from modules.shared.configuration import APP_CONFIG from modules.services.serviceAi.subCoreAi import SubCoreAi from modules.services.serviceAi.subDocumentProcessing import SubDocumentProcessing from modules.services.serviceAi.subWebResearch import SubWebResearch from modules.services.serviceAi.subDocumentGeneration import SubDocumentGeneration from modules.services.serviceAi.subUtilities import SubUtilities logger = logging.getLogger(__name__) class AiService: """Lightweight AI service orchestrator that delegates to specialized sub-modules. Manager delegates to specialized sub-modules: - SubCoreAi: Core AI operations (readImage, generateImage, callAi, planning, text calls) - SubDocumentProcessing: Document chunking, processing, and merging logic - SubWebResearch: Web research and crawling functionality - SubDocumentGeneration: Single-file and multi-file document generation - SubUtilities: Helper functions, text processing, and debugging utilities The main service acts as a coordinator: 1. Manages lazy initialization of sub-modules 2. Delegates operations to appropriate sub-modules 3. Maintains the same public API for backward compatibility """ def __init__(self, serviceCenter=None) -> None: """Initialize AI service with service center access. Args: serviceCenter: Service center instance for accessing other services """ self.services = serviceCenter # Only depend on interfaces self.aiObjects = None # Will be initialized in create() self._extractionService = None # Lazy initialization self._coreAi = None # Lazy initialization self._documentProcessor = None # Lazy initialization self._webResearch = None # Lazy initialization self._documentGenerator = None # Lazy initialization self._utilities = None # Lazy initialization @property def extractionService(self): """Lazy initialization of extraction service.""" if self._extractionService is None: logger.info("Lazy initializing ExtractionService...") self._extractionService = ExtractionService(self.services) return self._extractionService @property def coreAi(self): """Lazy initialization of core AI service.""" if self._coreAi is None: logger.info("Lazy initializing SubCoreAi...") self._coreAi = SubCoreAi(self.services, self.aiObjects) return self._coreAi @property def documentProcessor(self): """Lazy initialization of document processing service.""" if self._documentProcessor is None: logger.info("Lazy initializing SubDocumentProcessing...") self._documentProcessor = SubDocumentProcessing(self.services, self.aiObjects) return self._documentProcessor @property def webResearchService(self): """Lazy initialization of web research service.""" if self._webResearch is None: logger.info("Lazy initializing SubWebResearch...") self._webResearch = SubWebResearch(self.services, self.aiObjects) return self._webResearch @property def documentGenerator(self): """Lazy initialization of document generation service.""" if self._documentGenerator is None: logger.info("Lazy initializing SubDocumentGeneration...") self._documentGenerator = SubDocumentGeneration(self.services, self.aiObjects, self.documentProcessor) return self._documentGenerator @property def utilities(self): """Lazy initialization of utilities service.""" if self._utilities is None: logger.info("Lazy initializing SubUtilities...") self._utilities = SubUtilities(self.services) return self._utilities async def _ensureAiObjectsInitialized(self): """Ensure aiObjects is initialized.""" if self.aiObjects is None: logger.info("Lazy initializing AiObjects...") self.aiObjects = await AiObjects.create() logger.info("AiObjects initialization completed") @classmethod async def create(cls, serviceCenter=None) -> "AiService": """Create AiService instance with all connectors initialized.""" logger.info("AiService.create() called") instance = cls(serviceCenter) logger.info("AiService created, about to call AiObjects.create()...") instance.aiObjects = await AiObjects.create() logger.info("AiObjects.create() completed") return instance # AI Image Analysis async def readImage( self, prompt: str, imageData: Union[str, bytes], mimeType: str = None, options: Optional[AiCallOptions] = None, ) -> str: """Call AI for image analysis using interface.callImage().""" await self._ensureAiObjectsInitialized() return await self.coreAi.readImage(prompt, imageData, mimeType, options) # AI Image Generation async def generateImage( self, prompt: str, size: str = "1024x1024", quality: str = "standard", style: str = "vivid", options: Optional[AiCallOptions] = None, ) -> Dict[str, Any]: """Generate an image using AI using interface.generateImage().""" await self._ensureAiObjectsInitialized() return await self.coreAi.generateImage(prompt, size, quality, style, options) # Web Research async def webResearch(self, request: WebResearchRequest) -> WebResearchActionResult: """Perform web research using interface functions.""" await self._ensureAiObjectsInitialized() return await self.webResearchService.webResearch(request) # Master AI Call (process user prompt with optional unlimited count of input documents delivering one or many output documents, no size limitations) async def callAi( self, prompt: str, documents: Optional[List[ChatDocument]] = None, placeholders: Optional[List[PromptPlaceholder]] = None, options: Optional[AiCallOptions] = None, outputFormat: Optional[str] = None, title: Optional[str] = None ) -> Union[str, Dict[str, Any]]: """ Unified AI call interface that automatically routes to appropriate handler. Args: prompt: The main prompt for the AI call documents: Optional list of documents to process placeholders: Optional list of placeholder replacements for planning calls options: AI call configuration options outputFormat: Optional output format (html, pdf, docx, txt, md, json, csv, xlsx) for document generation title: Optional title for generated documents Returns: AI response as string, or dict with documents if outputFormat is specified Raises: Exception: If all available models fail """ await self._ensureAiObjectsInitialized() # Get document processor and generator documentProcessor = self.documentProcessor documentGenerator = self.documentGenerator return await self.coreAi.callAi( prompt, documents, placeholders, options, outputFormat, title, documentProcessor, documentGenerator )