apiKey = ''; $this->model = ''; $this->files = []; } /** * Process an AI message and generate an email template. * * @param string $message The user's message describing the desired template * @param array $history Previous conversation history for context * @param string $model The AI model to use (e.g., 'gemini25pro', 'deepseek') * @param string $apiKey The API key for the selected model * @param array $files Attached files [{name, mimeType, base64Data}, ...] * @param string $promptName Prompt filename without extension (default: 'email_template_prompt') * @return array Response with HTML, message, and updated history */ public function process($message, $history = [], $model = '', $apiKey = '', $files = [], $promptName = 'email_template_prompt') { $this->model = $model; $this->apiKey = $apiKey; $this->files = $files; if (empty($this->apiKey)) { return [ 'error' => __('AI API key is not configured. Please set it in Settings > AI.', 'advanced-emailing-for-woocommerce') ]; } // Load the system prompt from the text file $systemPrompt = $this->loadSystemPrompt($promptName); if ($systemPrompt === false) { return [ 'error' => __('Could not load AI prompt template file.', 'advanced-emailing-for-woocommerce') ]; } // Build conversation messages $messages = []; if (!empty($history)) { foreach ($history as $historyItem) { // History items arrive as stdClass from json_decode — use object access $role = is_array($historyItem) ? $historyItem['role'] : $historyItem->role; $content = is_array($historyItem) ? $historyItem['content'] : $historyItem->content; $messages[] = [ 'role' => $role, 'content' => $content ]; } } $messages[] = ['role' => 'user', 'content' => $message]; // Call the AI via the appropriate provider try { require_once __DIR__ . '/ai_callers/AICallerBase.php'; $caller = AICallerBase::create($this->model, $this->apiKey); $aiResponse = $caller->callAI($systemPrompt, $messages, $this->files); } catch (\Exception $e) { return ['error' => $e->getMessage()]; } // Log the raw AI response for debugging \rnadvanceemailingwc\Managers\LogManager::LogDebug("=== AI RAW RESPONSE START ===\r\n" . $aiResponse . "\r\n=== AI RAW RESPONSE END ==="); // Update conversation history $updatedHistory = $history; $updatedHistory[] = ['role' => 'user', 'content' => $message]; $updatedHistory[] = ['role' => 'assistant', 'content' => $aiResponse]; return [ 'response' => $aiResponse, 'history' => $updatedHistory ]; } /** * Public accessor for the compiled system prompt. * Used by the external chat feature to give users the prompt they need. * * @param string $promptName Prompt filename without extension * @return string|false The prompt text, or false on failure */ public function getSystemPrompt($promptName = 'email_template_prompt') { return $this->loadSystemPrompt($promptName); } /** * Public accessor for HTML extraction from AI response text. * Used by the external chat feature to process manually-pasted AI responses. * * @param string $response The raw AI response text * @return string The extracted HTML */ public function extractHtml($response) { return $this->extractHtmlFromResponse($response); } /** * Loads the system prompt from the template file. * * @param string $promptName Prompt filename without extension (sanitized to [a-zA-Z0-9_] only) * @return string|false The prompt text, or false on failure */ private function loadSystemPrompt($promptName = 'email_template_prompt') { // Sanitize: only allow alphanumeric and underscores to prevent directory traversal if (!preg_match('/^[a-zA-Z0-9_]+$/', $promptName)) { return false; } $promptFile = __DIR__ . '/prompts/' . $promptName . '.txt'; if (!file_exists($promptFile)) { return false; } $prompt = file_get_contents($promptFile); if ($prompt === false) { return false; } return $prompt; } /** * Extracts the HTML content from the AI response. * * The AI should return raw HTML, but sometimes it wraps it in * ```html code blocks or adds commentary. This method handles * all cases and extracts the clean HTML. * * @param string $response The raw AI response text * @return string The extracted HTML */ private function extractHtmlFromResponse($response) { $response = trim($response); // Case 1: AI wrapped the HTML in ```html ... ``` code blocks if (preg_match('/```(?:html)?\s*\n([\s\S]*?)\n```/', $response, $matches)) { return trim($matches[1]); } // Case 2: Response starts with a tag — it's already raw HTML if (preg_match('/^\s*