Initial commit of SnappyMail LLM Email Writing Prompts Plugin with Grok 3 API support
This commit is contained in:
commit
fe21504ab5
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
# SnappyMail LLM Email Writing Prompts Plugin with Grok 3 API Support
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This plugin enhances SnappyMail, a lightweight web-based email client, by integrating AI-driven email writing prompts powered by xAI's Grok 3 API as the primary Large Language Model (LLM) provider, with fallbacks to other LLMs. The purpose is to assist users in drafting professional, concise, or creative emails through context-aware prompt suggestions, leveraging Grok 3's advanced reasoning capabilities.
|
||||||
|
|
||||||
|
## Purpose and Goals
|
||||||
|
|
||||||
|
- **Primary Goal**: Enable SnappyMail users to access Grok 3-generated prompts for drafting emails with high-quality, reasoning-focused outputs.
|
||||||
|
- **Secondary Goals**:
|
||||||
|
- Ensure compatibility with SnappyMail's lightweight and privacy-focused design.
|
||||||
|
- Provide a user-friendly interface for selecting and customizing prompts.
|
||||||
|
- Maintain performance and security standards for self-hosted environments.
|
||||||
|
- Allow fallback to other LLM APIs (e.g., OpenAI, Hugging Face) for flexibility.
|
||||||
|
|
||||||
|
## Target Audience
|
||||||
|
|
||||||
|
- **Primary Users**: Individuals and small businesses using SnappyMail for personal or professional email communication, especially those seeking AI-assisted writing with Grok 3's logical outputs.
|
||||||
|
- **Secondary Users**: System administrators managing SnappyMail instances who want to enhance functionality with Grok 3 integration.
|
||||||
|
- **Use Cases**:
|
||||||
|
- Professionals drafting formal emails (e.g., business proposals).
|
||||||
|
- Casual users writing personal emails with creative tones.
|
||||||
|
- Non-native speakers needing assistance with grammar and phrasing.
|
||||||
|
|
||||||
|
## Functional Requirements
|
||||||
|
|
||||||
|
### Core Features
|
||||||
|
|
||||||
|
- **Prompt Library**: A set of predefined prompts in categories like Professional, Creative, Assistance, and Quick Replies, optimized for Grok 3's reasoning capabilities.
|
||||||
|
- **Context-Aware Suggestions**: Analyzes email subject and body to suggest relevant prompts using Grok 3's chain-of-thought reasoning.
|
||||||
|
- **LLM Integration**: Primary provider is xAI Grok 3 API ([https://api.x.ai/v1/chat/completions](mdc:https:/api.x.ai/v1/chat/completions)), with configurable fallbacks to OpenAI, Hugging Face, or local LLMs.
|
||||||
|
- **Draft Generation**: Generates email drafts based on prompts and context, supporting multiple iterations (e.g., tone adjustments).
|
||||||
|
- **Custom Prompts**: Users can create and save custom prompts, stored securely as JSON files.
|
||||||
|
- **Privacy and Security**: No email data stored beyond API calls, with optional opt-in for context analysis and secure API key storage.
|
||||||
|
|
||||||
|
### User Interface
|
||||||
|
|
||||||
|
- **Compose Window Integration**: A "Smart Prompts" button or sidebar in the email composition interface for Grok 3-powered suggestions.
|
||||||
|
- **Settings Panel**: Options to configure Grok 3 API key, select model, manage custom prompts, and toggle context analysis.
|
||||||
|
- **Visual Design**: Matches SnappyMail's minimalist UI with dark mode and mobile responsiveness.
|
||||||
|
|
||||||
|
## Technical Requirements
|
||||||
|
|
||||||
|
- **Platform**: Compatible with SnappyMail v2.12+, PHP 7.4+, and JavaScript ES2018.
|
||||||
|
- **Plugin Architecture**: Located in `/plugins/llm-prompts/` with files like `index.php`, `js/`, `templates/`, and `config.json`.
|
||||||
|
- **LLM Integration**: Uses Grok 3 API with OpenAI SDK compatibility, secure API key storage, and fallback logic for other providers.
|
||||||
|
- **Security**: Encrypts API keys and prompts, sanitizes outputs to prevent XSS, and complies with GDPR.
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
- **Phase 1**: Research and setup Grok 3 API access (2 weeks).
|
||||||
|
- **Phase 2**: Core development of prompt library and API integration (4 weeks).
|
||||||
|
- **Phase 3**: UI and settings development (3 weeks).
|
||||||
|
- **Phase 4**: Testing and security audits (3 weeks).
|
||||||
|
- **Phase 5**: Release and documentation (2 weeks).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- SnappyMail Official Site: [https://snappymail.eu](mdc:https:/snappymail.eu)
|
||||||
|
- SnappyMail GitHub: [https://github.com/the-djmaze/snappymail](mdc:https:/github.com/the-djmaze/snappymail)
|
||||||
|
- xAI Grok 3 API: [https://x.ai/api](mdc:https:/x.ai/api)
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"grok3_api_key": "",
|
||||||
|
"selected_model": "grok-3",
|
||||||
|
"enable_context_analysis": true,
|
||||||
|
"custom_prompts": []
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class LLMPromptsPlugin extends /snappymail/v/0.0.0/app/libraries/SnappyMail/Plugins/AbstractPlugin
|
||||||
|
{
|
||||||
|
public $id = 'llm-prompts';
|
||||||
|
public $name = 'LLM Email Writing Prompts';
|
||||||
|
public $version = '1.0';
|
||||||
|
public $description = 'Enhances email composition with AI-driven prompts powered by xAI Grok 3 API.';
|
||||||
|
public $author = 'Your Name';
|
||||||
|
public $url = 'https://github.com/your-repo/llm-prompts';
|
||||||
|
public $license = 'MIT';
|
||||||
|
|
||||||
|
public function Boot()
|
||||||
|
{
|
||||||
|
$this->addJs('js/llm-prompts.js');
|
||||||
|
$this->addTemplate('templates/llm-prompts.html');
|
||||||
|
$this->addHook('filter.compose', 'onFilterCompose');
|
||||||
|
$this->addHook('action.pre-send', 'onActionPreSend');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onFilterCompose($oAccount, $oCompose)
|
||||||
|
{
|
||||||
|
// Inject UI for Smart Prompts in compose window
|
||||||
|
// This will be handled by JavaScript
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onActionPreSend($oAccount, $oMessage)
|
||||||
|
{
|
||||||
|
// Validate content before sending if generated by LLM
|
||||||
|
// Add any necessary validation or sanitization
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configMapping(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'grok3_api_key' => ['string', ''],
|
||||||
|
'selected_model' => ['string', 'grok-3'],
|
||||||
|
'enable_context_analysis' => ['bool', true],
|
||||||
|
'custom_prompts' => ['json', '[]']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
// LLM Prompts Plugin for SnappyMail
|
||||||
|
// Provides UI and API interaction for AI-driven email writing prompts
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Initialize plugin when DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
if (typeof rl !== 'undefined') {
|
||||||
|
rl.addSettingsViewModel(
|
||||||
|
'LLMPromptsSettings',
|
||||||
|
'LLMPromptsSettings',
|
||||||
|
'LLM Prompts',
|
||||||
|
'Settings for AI-driven email writing prompts powered by Grok 3',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add UI for Smart Prompts in compose window
|
||||||
|
rl.addHook('view-model', function (vm) {
|
||||||
|
if (vm.viewModelName === 'ComposeViewModel') {
|
||||||
|
initComposeUI(vm);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function initComposeUI(composeVM) {
|
||||||
|
// Add Smart Prompts button or sidebar to compose window
|
||||||
|
const toolbar = document.querySelector('.compose-toolbar');
|
||||||
|
if (toolbar) {
|
||||||
|
const smartPromptsButton = document.createElement('button');
|
||||||
|
smartPromptsButton.className = 'button smart-prompts';
|
||||||
|
smartPromptsButton.innerHTML = 'Smart Prompts';
|
||||||
|
smartPromptsButton.onclick = function () {
|
||||||
|
toggleSmartPromptsSidebar(composeVM);
|
||||||
|
};
|
||||||
|
toolbar.appendChild(smartPromptsButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSmartPromptsSidebar(composeVM) {
|
||||||
|
// Toggle visibility of prompts sidebar
|
||||||
|
let sidebar = document.querySelector('.smart-prompts-sidebar');
|
||||||
|
if (!sidebar) {
|
||||||
|
sidebar = document.createElement('div');
|
||||||
|
sidebar.className = 'smart-prompts-sidebar';
|
||||||
|
sidebar.innerHTML = getPromptsHTML();
|
||||||
|
document.querySelector('.compose-box').appendChild(sidebar);
|
||||||
|
|
||||||
|
// Add event listeners for prompt selection
|
||||||
|
setupPrompts(sidebar, composeVM);
|
||||||
|
} else {
|
||||||
|
sidebar.style.display = sidebar.style.display === 'none' ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPromptsHTML() {
|
||||||
|
// HTML for prompt categories and suggestions
|
||||||
|
return `
|
||||||
|
<h3>Smart Prompts (Grok 3)</h3>
|
||||||
|
<div class="prompt-category">
|
||||||
|
<h4>Professional</h4>
|
||||||
|
<ul>
|
||||||
|
<li data-prompt="Write a concise follow-up email to a client">Follow-up Email</li>
|
||||||
|
<li data-prompt="Draft a professional proposal email">Proposal Email</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-category">
|
||||||
|
<h4>Creative</h4>
|
||||||
|
<ul>
|
||||||
|
<li data-prompt="Compose a friendly invitation for a team event">Team Event Invite</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-category">
|
||||||
|
<h4>Assistance</h4>
|
||||||
|
<ul>
|
||||||
|
<li data-prompt="Rephrase this email to sound more polite">Make Polite</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-category">
|
||||||
|
<h4>Quick Replies</h4>
|
||||||
|
<ul>
|
||||||
|
<li data-prompt="Generate a thank-you response for a received email">Thank You Reply</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="context-input">
|
||||||
|
<textarea placeholder="Add context or modify prompt..." rows="3"></textarea>
|
||||||
|
<button onclick="generateDraft(this)">Generate Draft</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPrompts(sidebar, composeVM) {
|
||||||
|
const prompts = sidebar.querySelectorAll('.prompt-category li');
|
||||||
|
prompts.forEach(prompt => {
|
||||||
|
prompt.onclick = function () {
|
||||||
|
const promptText = this.getAttribute('data-prompt');
|
||||||
|
sidebar.querySelector('.context-input textarea').value = promptText;
|
||||||
|
// Highlight selected prompt
|
||||||
|
prompts.forEach(p => p.classList.remove('selected'));
|
||||||
|
this.classList.add('selected');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateDraft(button) {
|
||||||
|
const sidebar = button.closest('.smart-prompts-sidebar');
|
||||||
|
const prompt = sidebar.querySelector('.context-input textarea').value;
|
||||||
|
const subject = document.querySelector('.compose-subject input').value;
|
||||||
|
const body = document.querySelector('.compose-body .editor').innerText;
|
||||||
|
|
||||||
|
// Make API call to Grok 3 or configured LLM
|
||||||
|
fetchLLMDraft(prompt, subject, body).then(draft => {
|
||||||
|
if (draft) {
|
||||||
|
// Insert draft into compose body
|
||||||
|
const editor = document.querySelector('.compose-body .editor');
|
||||||
|
editor.innerHTML = draft.replace(/\n/g, '<br>');
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error generating draft:', error);
|
||||||
|
alert('Failed to generate draft. Please check settings.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLLMDraft(prompt, subject, body) {
|
||||||
|
// This would make an API call to the backend which handles Grok 3 API interaction
|
||||||
|
// For now, simulate an API call
|
||||||
|
const response = await fetch('/?/Api/LLMPrompts/GenerateDraft', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt: prompt,
|
||||||
|
subject: subject,
|
||||||
|
body: body,
|
||||||
|
enableContextAnalysis: rl.settingsGet('LLMPromptsSettings.enable_context_analysis')
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.result;
|
||||||
|
}
|
||||||
|
})();
|
|
@ -0,0 +1,42 @@
|
||||||
|
<div data-bind="template: { name: 'PluginLLMPromptsSettings' }"></div>
|
||||||
|
|
||||||
|
<script type="text/html" id="PluginLLMPromptsSettings">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h2 class="panel-title">LLM Email Writing Prompts (Grok 3)</h2>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="grok3ApiKey">Grok 3 API Key</label>
|
||||||
|
<input type="password" class="form-control" id="grok3ApiKey" data-bind="textInput: grok3_api_key" placeholder="Enter your xAI Grok 3 API key">
|
||||||
|
<small class="form-text text-muted">Get your API key at <a href="https://x.ai/api" target="_blank">xAI API</a>.</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="selectedModel">Model Selection</label>
|
||||||
|
<select class="form-control" id="selectedModel" data-bind="value: selected_model">
|
||||||
|
<option value="grok-3">Grok 3</option>
|
||||||
|
<option value="grok-3-mini">Grok 3 Mini</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="enableContextAnalysis" data-bind="checked: enable_context_analysis">
|
||||||
|
<label for="enableContextAnalysis">Enable Context Analysis (analyze email content for better suggestions)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Custom Prompts</label>
|
||||||
|
<div data-bind="foreach: custom_prompts">
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<input type="text" class="form-control" data-bind="textInput: $data" placeholder="Enter custom prompt">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-danger" type="button" data-bind="click: $parent.removeCustomPrompt.bind($parent, $index())">Remove</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-secondary" data-bind="click: addCustomPrompt">Add Custom Prompt</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button type="submit" class="btn btn-primary" data-bind="click: save">Save Changes</button>
|
||||||
|
</div>
|
||||||
|
</script>
|
Loading…
Reference in New Issue