# Copyright (c) 2025 Patrick Motsch # All rights reserved. import logging from datetime import datetime, UTC from modules.workflows.methods.methodBase import MethodBase from modules.datamodels.datamodelWorkflowActions import WorkflowActionDefinition, WorkflowActionParameter from modules.shared.frontendTypes import FrontendType # Import helpers from .helpers.csvProcessing import CsvProcessingHelper # Import actions from .actions.process import process from .actions.webResearch import webResearch from .actions.summarizeDocument import summarizeDocument from .actions.translateDocument import translateDocument from .actions.convertDocument import convertDocument from .actions.generateDocument import generateDocument from .actions.generateCode import generateCode logger = logging.getLogger(__name__) class MethodAi(MethodBase): """AI processing methods.""" def __init__(self, services): super().__init__(services) self.name = "ai" self.description = "AI processing methods" # Initialize helper modules self.csvProcessing = CsvProcessingHelper(self) # RBAC-Integration: Action-Definitionen mit actionId self._actions = { "process": WorkflowActionDefinition( actionId="ai.process", description="Universal AI document processing action - accepts multiple input documents in any format and processes them together with a prompt", dynamicMode=True, parameters={ "aiPrompt": WorkflowActionParameter( name="aiPrompt", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="Instruction for the AI describing what processing to perform" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=False, description="Document reference(s) in any format to use as input/context" ), "resultType": WorkflowActionParameter( name="resultType", type="str", frontendType=FrontendType.SELECT, frontendOptions=["txt", "json", "md", "csv", "xml", "html", "pdf", "docx", "xlsx", "pptx", "png", "jpg"], required=False, default="txt", description="Output file extension. All output documents will use this format" ), "generationIntent": WorkflowActionParameter( name="generationIntent", type="str", frontendType=FrontendType.SELECT, frontendOptions=["document", "code", "image"], required=False, description="Explicit generation intent (\"document\" | \"code\" | \"image\"). For DATA_GENERATE operations, if not provided, defaults based on resultType: document formats (xlsx, docx, pdf, etc.) → \"document\", code formats (py, js, ts, etc.) → \"code\". For IMAGE_GENERATE operations, this parameter is ignored. Best practice: Use qualified actions (ai.generateDocument, ai.generateCode) instead of ai.process." ) }, execute=process.__get__(self, self.__class__) ), "webResearch": WorkflowActionDefinition( actionId="ai.webResearch", description="Web research with two-step process: search for URLs, then crawl content", dynamicMode=True, parameters={ "prompt": WorkflowActionParameter( name="prompt", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="Natural language research instruction" ), "urlList": WorkflowActionParameter( name="urlList", type="List[str]", frontendType=FrontendType.MULTISELECT, required=False, description="Specific URLs to crawl, if needed" ), "country": WorkflowActionParameter( name="country", type="str", frontendType=FrontendType.TEXT, required=False, description="Two-digit country code (lowercase, e.g., ch, us, de)" ), "language": WorkflowActionParameter( name="language", type="str", frontendType=FrontendType.SELECT, frontendOptions=["de", "en", "fr", "it", "es"], required=False, description="Language code (lowercase, e.g., de, en, fr)" ), "researchDepth": WorkflowActionParameter( name="researchDepth", type="str", frontendType=FrontendType.SELECT, frontendOptions=["fast", "general", "deep"], required=False, default="general", description="Research depth" ) }, execute=webResearch.__get__(self, self.__class__) ), "summarizeDocument": WorkflowActionDefinition( actionId="ai.summarizeDocument", description="Summarize one or more documents, extracting key points and main ideas", dynamicMode=True, parameters={ "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference(s) to summarize" ), "summaryLength": WorkflowActionParameter( name="summaryLength", type="str", frontendType=FrontendType.SELECT, frontendOptions=["brief", "medium", "detailed"], required=False, default="medium", description="Desired summary length" ), "focus": WorkflowActionParameter( name="focus", type="str", frontendType=FrontendType.TEXT, required=False, description="Specific aspect to focus on in the summary (e.g., financial data, key decisions)" ), "resultType": WorkflowActionParameter( name="resultType", type="str", frontendType=FrontendType.SELECT, frontendOptions=["txt", "md", "docx"], required=False, default="txt", description="Output file extension" ) }, execute=summarizeDocument.__get__(self, self.__class__) ), "translateDocument": WorkflowActionDefinition( actionId="ai.translateDocument", description="Translate documents to a target language while preserving formatting and structure", dynamicMode=True, parameters={ "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference(s) to translate" ), "targetLanguage": WorkflowActionParameter( name="targetLanguage", type="str", frontendType=FrontendType.TEXT, required=True, description="Target language code or name (e.g., de, German, French, es)" ), "sourceLanguage": WorkflowActionParameter( name="sourceLanguage", type="str", frontendType=FrontendType.TEXT, required=False, description="Source language if known (e.g., en, English). If not provided, AI will detect" ), "preserveFormatting": WorkflowActionParameter( name="preserveFormatting", type="bool", frontendType=FrontendType.CHECKBOX, required=False, default=True, description="Whether to preserve original formatting" ), "resultType": WorkflowActionParameter( name="resultType", type="str", frontendType=FrontendType.TEXT, required=False, description="Output file extension. If not specified, uses same format as input" ) }, execute=translateDocument.__get__(self, self.__class__) ), "convertDocument": WorkflowActionDefinition( actionId="ai.convertDocument", description="Convert documents between different formats (PDF→Word, Excel→CSV, etc.)", dynamicMode=True, parameters={ "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference(s) to convert" ), "targetFormat": WorkflowActionParameter( name="targetFormat", type="str", frontendType=FrontendType.SELECT, frontendOptions=["docx", "pdf", "xlsx", "csv", "txt", "html", "json", "md"], required=True, description="Target format extension" ), "preserveStructure": WorkflowActionParameter( name="preserveStructure", type="bool", frontendType=FrontendType.CHECKBOX, required=False, default=True, description="Whether to preserve document structure (headings, tables, etc.)" ) }, execute=convertDocument.__get__(self, self.__class__) ), "generateDocument": WorkflowActionDefinition( actionId="ai.generateDocument", description="Generate documents from scratch or based on templates/inputs", dynamicMode=True, parameters={ "prompt": WorkflowActionParameter( name="prompt", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="Description of the document to generate" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=False, description="Template documents or reference documents to use as a guide" ), "documentType": WorkflowActionParameter( name="documentType", type="str", frontendType=FrontendType.SELECT, frontendOptions=["letter", "memo", "proposal", "contract", "report", "email"], required=False, description="Type of document" ), "resultType": WorkflowActionParameter( name="resultType", type="str", frontendType=FrontendType.TEXT, required=False, default="txt", description="Output format (e.g., txt, html, pdf, docx, md, json, csv, xlsx, pptx, png, jpg). Any format supported by renderers is acceptable. Default: txt" ) }, execute=generateDocument.__get__(self, self.__class__) ), "generateCode": WorkflowActionDefinition( actionId="ai.generateCode", description="Generate code files - explicitly sets intent to 'code'", dynamicMode=True, parameters={ "prompt": WorkflowActionParameter( name="prompt", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="Description of code to generate" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=False, description="Reference documents" ), "resultType": WorkflowActionParameter( name="resultType", type="str", frontendType=FrontendType.SELECT, frontendOptions=["py", "js", "ts", "html", "java", "cpp", "txt"], required=False, description="Output format (html, js, py, etc.). Default: based on prompt" ) }, execute=generateCode.__get__(self, self.__class__) ) } # Validate actions after definition self._validateActions() # Register actions as methods (optional, für direkten Zugriff) self.process = process.__get__(self, self.__class__) self.webResearch = webResearch.__get__(self, self.__class__) self.summarizeDocument = summarizeDocument.__get__(self, self.__class__) self.translateDocument = translateDocument.__get__(self, self.__class__) self.convertDocument = convertDocument.__get__(self, self.__class__) self.generateDocument = generateDocument.__get__(self, self.__class__) self.generateCode = generateCode.__get__(self, self.__class__) def _format_timestamp_for_filename(self) -> str: """Format current timestamp as YYYYMMDD-hhmmss for filenames.""" return datetime.now(UTC).strftime("%Y%m%d-%H%M%S")