You're on the right track! You want to create a **Roundcube plugin** that allows users to **enhance or rewrite the email body using an LLM**, by sending the current message content to a backend API (your LLM service), then replacing the textarea content with the response. Here’s a complete working example of such a plugin, called `llm_compose_helper`. It adds a button to the compose screen, sends the current message text to a configured URL via AJAX, and replaces the message body with the LLM-generated result. --- ## βœ… Goal - Add a "Rewrite with AI" button in the compose window. - On click: open a popup asking the user for a rewrite prompt/instructions. - Submit both the current message body and the user prompt to the configured LLM API endpoint. - Replace the `' + ''; var buttons = [ { text: rcmail.gettext('rewrite_submit', 'llm_compose_helper'), classes: 'mainaction', click: function(e, ref) { var promptValue = document.getElementById(promptId).value || ''; // Show loading rcmail.set_busy(true, 'loading'); // Send to LLM API with message and prompt rcmail.http_post('plugin.llm_rewrite', { message: messageText, prompt: promptValue }, function() { rcmail.set_busy(false); }); if (ref && ref.hide) ref.hide(); } }, { text: rcmail.gettext('rewrite_cancel', 'llm_compose_helper'), click: function(e, ref) { if (ref && ref.hide) ref.hide(); } } ]; // Open Roundcube dialog rcmail.show_popup_dialog(dialogHtml, rcmail.gettext('rewrite_with_llm', 'llm_compose_helper'), buttons, {modal: true, width: 520}); }); }); // Handle response from server rcmail.addEventListener('plugin.llm_rewrite_response', function(response) { if (response.status === 'success' && response.text) { const newText = response.text; if (rcmail.env.html_editor && rcmail.editor && rcmail.editor.setData) { rcmail.editor.setData(newText); // For CKEditor } else { $('#composebody').val(newText); } rcmail.showMessage(rcmail.gettext('rewrite_success', 'llm_compose_helper'), 'confirmation'); } else { var errorMsg = response && response.message ? String(response.message) : rcmail.gettext('rewrite_error', 'llm_compose_helper'); rcmail.showMessage(errorMsg, 'error'); } }); ``` --- ## 5. Extend `llm_compose_helper.php` – Add Server-Side Action Update the `llm_compose_helper.php` file to register the AJAX action and handle the request: ```php function init() { $this->load_config(); $this->add_texts('localization/', true); $rcmail = rcmail::get_instance(); if ($rcmail->action == 'compose') { $this->include_script('js/llm_compose_helper.js'); $this->register_handler('plugin.llm_button', array($this, 'llm_button')); // Register custom action for AJAX $this->register_action('plugin.llm_rewrite', array($this, 'action_handler')); } } function action_handler() { $rcmail = rcmail::get_instance(); // Get input $message = rcube_utils::get_input_value('message', rcube_utils::INPUT_POST); $prompt = rcube_utils::get_input_value('prompt', rcube_utils::INPUT_POST); if (empty($message)) { $rcmail->output->command('plugin.llm_rewrite_response', [ 'status' => 'error', 'message' => 'No message provided' ]); return; } // Get config $api_url = $rcmail->config->get('llm_api_url'); if (!$api_url) { $rcmail->output->command('plugin.llm_rewrite_response', [ 'status' => 'error', 'message' => 'LLM API URL not configured' ]); return; } $headers = $rcmail->config->get('llm_api_headers', ['Content-Type: application/json']); // Prepare request $data = json_encode(['text' => $message, 'prompt' => $prompt]); // Use file_get_contents or cURL $options = [ 'http' => [ 'header' => $headers, 'method' => 'POST', 'content' => $data, 'timeout' => 30 ], 'ssl' => [ 'verify_peer' => true, 'verify_peer_name' => true, 'cafile' => '/etc/ssl/certs/ca-certificates.crt', // Adjust as needed ] ]; $context = stream_context_create($options); $result = file_get_contents($api_url, false, $context); if ($result === false) { $rcmail->output->command('plugin.llm_rewrite_response', [ 'status' => 'error', 'message' => 'Request failed' ]); return; } $response = json_decode($result, true); $rewritten = isset($response['text']) && $response['text'] !== null ? $response['text'] : ($response['message'] ?? $result); // Return success $rcmail->output->command('plugin.llm_rewrite_response', [ 'status' => 'success', 'text' => $rewritten ]); } ``` --- ## 6. Example LLM API Response Format (Expected) Your external LLM endpoint should accept POST JSON: ```json { "text": "Hello, I am writing to ask about...", "prompt": "Make it shorter and more formal" } ``` And return: ```json { "text": "Hi, I hope you're doing well. I'm reaching out to inquire about..." } ``` Ensure CORS is allowed if hosted separately. --- ## 7. Optional: Add More Labels In `localization/en_US.inc`, add: ```php $messages = array( 'rewrite_success' => 'Message rewritten successfully!', 'rewrite_error' => 'Failed to rewrite message.', 'non_empty' => 'Please write something before using AI rewrite.' ); ``` --- ## 8. Enable the Plugin In `config/config.inc.php`: ```php $config['plugins'] = array( // ... other plugins 'llm_compose_helper' ); ``` --- ## βœ… Final Result When composing or replying: - A new button labeled "AI Rewrite" appears in the toolbar. - Clicking it opens a dialog asking for a rewrite prompt. - Submitting sends the current message and the prompt to your LLM API. - The response replaces the message body. - Works with plain text and HTML (via CKEditor if enabled).