# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Dynamic Mode Prompt Generation Handles prompt templates for dynamic mode action handling. """ import json from typing import Any, List from modules.datamodels.datamodelChat import PromptBundle, PromptPlaceholder from modules.workflows.processing.shared.placeholderFactory import ( extractUserPrompt, extractUserLanguage, extractAvailableMethods, extractAvailableDocumentsSummary, extractAvailableDocumentsIndex, extractAvailableConnectionsIndex, extractPreviousActionResults, extractLearningsAndImprovements, extractLatestRefinementFeedback, extractWorkflowHistory, ) from modules.workflows.processing.shared.methodDiscovery import methods, getActionParameterList def generateDynamicPlanSelectionPrompt(services, context: Any, learningEngine=None) -> PromptBundle: """Define placeholders first, then the template; return PromptBundle.""" placeholders: List[PromptPlaceholder] = [ PromptPlaceholder(label="OVERALL_TASK_CONTEXT", content=services.currentUserPromptNormalized, summaryAllowed=False), PromptPlaceholder(label="TASK_OBJECTIVE", content=context.taskStep.objective, summaryAllowed=False), PromptPlaceholder(label="USER_PROMPT", content=extractUserPrompt(context), summaryAllowed=False), PromptPlaceholder(label="USER_LANGUAGE", content=extractUserLanguage(services), summaryAllowed=False), PromptPlaceholder(label="AVAILABLE_DOCUMENTS_SUMMARY", content=extractAvailableDocumentsSummary(services, context), summaryAllowed=True), PromptPlaceholder(label="AVAILABLE_METHODS", content=extractAvailableMethods(services), summaryAllowed=False), # Provide enriched history context for Stage 1 to craft parametersContext PromptPlaceholder(label="WORKFLOW_HISTORY", content=extractWorkflowHistory(services), summaryAllowed=True), # Provide deterministic indexes so the planner can choose exact labels PromptPlaceholder(label="AVAILABLE_DOCUMENTS_INDEX", content=extractAvailableDocumentsIndex(services, context), summaryAllowed=True), PromptPlaceholder(label="AVAILABLE_CONNECTIONS_INDEX", content=extractAvailableConnectionsIndex(services), summaryAllowed=False), ] # Add adaptive learning context if available adaptiveContext = {} if learningEngine: workflowId = getattr(context, 'workflow_id', 'unknown') userPrompt = extractUserPrompt(context) adaptiveContext = learningEngine.getAdaptiveContextForActionSelection(workflowId, userPrompt) if adaptiveContext: # Add learning-aware placeholders placeholders.extend([ PromptPlaceholder(label="ADAPTIVE_GUIDANCE", content=adaptiveContext.get('adaptiveGuidance', ''), summaryAllowed=True), PromptPlaceholder(label="FAILURE_ANALYSIS", content=json.dumps(adaptiveContext.get('failureAnalysis', {}), indent=2), summaryAllowed=True), PromptPlaceholder(label="ESCALATION_LEVEL", content=adaptiveContext.get('escalationLevel', 'low'), summaryAllowed=False), ]) template = """Select exactly one next action to advance the task incrementally. === TASK === CONTEXT: {{KEY:OVERALL_TASK_CONTEXT}} OBJECTIVE: {{KEY:TASK_OBJECTIVE}} === AVAILABLE RESOURCES === AVAILABLE_DOCUMENTS_INDEX: {{KEY:AVAILABLE_DOCUMENTS_SUMMARY}} {{KEY:AVAILABLE_DOCUMENTS_INDEX}} AVAILABLE_CONNECTIONS_INDEX: {{KEY:AVAILABLE_CONNECTIONS_INDEX}} === AVAILABLE ACTIONS === {{KEY:AVAILABLE_METHODS}} === CONTEXT === HISTORY: {{KEY:WORKFLOW_HISTORY}} GUIDANCE: {{KEY:ADAPTIVE_GUIDANCE}} FAILURES: {{KEY:FAILURE_ANALYSIS}} ESCALATION: {{KEY:ESCALATION_LEVEL}} === SELECTION RULE === 1. Read OBJECTIVE and identify what it requires 2. Check AVAILABLE_METHODS to find action whose PURPOSE matches that requirement 3. Select action that can DO what objective needs - do not select actions that do something different === OUTPUT FORMAT === Return ONLY JSON (no markdown, no explanations). The chosen action MUST: - Match the objective's requirement (verify action's purpose in AVAILABLE_METHODS) - Be the next logical incremental step (not complete entire objective in one step) - Target exactly one output format if producing files - Use ONLY exact references from AVAILABLE_DOCUMENTS_INDEX (docList:... or docItem:...) - ALWAYS use FULL document references with filename: docItem:: (filename is required) - Learn from previous validation feedback and avoid repeated mistakes - Include intent analysis fields (dataType, expectedFormats, qualityRequirements, successCriteria) {{ "action": "method.action_name", "actionObjective": "...", "dataType": "numbers|text|documents|analysis|code|unknown", "expectedFormats": ["pdf", "docx", "xlsx", "txt", "json", "csv", "html", "md"], "qualityRequirements": {{ "accuracyThreshold": 0.0-1.0, "completenessThreshold": 0.0-1.0 }}, "successCriteria": ["specific criterion 1", "specific criterion 2"], "userMessage": "User-friendly message in language '{{KEY:USER_LANGUAGE}}' explaining what this action will do (1 sentence, first person, friendly tone)", "learnings": ["..."], "requiredInputDocuments": ["docItem::", "docList: