AI Tools

Note

This plugin is a paid add-on. You need to buy Redactor together with AI Tools plugin if you don't have it yet. Or buy the plugin separately in your account if you already have Redactor.

Concept #

The Redactor AI plugin provides an interface for accessing and processing requests to OpenAI API. On your side you need an API key and a small code to process requests on the server side.

This way we don't charge for requests and tokens. These solutions are completely on your side. It seems to us that this is the best option when you can decide which AI model to query, what limits to set, etc.

Why do we need a server-side solution?

Simple, the key to the API must be protected and not publicly available. So it is impossible to do it only using Javascript. That's why Redactor forms a request to the server, so that there in turn the request to the API is sent.

Below you will find an example of setting up and running the editor with the AI plugin and examples of server-side handlers.

Start with AI #

This functionality is seamlessly integrated as a plugin, available for purchase either individually or bundled with Redactor. You can activate the editor with the AI plugin enabled.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        },
        image: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/images/generations',
            model: 'dall-e-3'
        }
    }
});

You can look and try how it works in this example:

Server-side example #

Below is an example of a server-side handler for a request from Redactor and submitting it using the Open AI API. This example is written in PHP. You can modify it according to your server-side environment.


<?php
// Your OpenAI API key
$openaiApiKey = 'YOUR-API-KEY';

// API endpoint
$url = $_POST['url'];

// Data payload
$data = json_decode($_POST['data']);

// Initialize cURL
$ch = curl_init($url);

// Set cURL options
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer ' . $openaiApiKey,
]);

// Execute cURL request
$response = curl_exec($ch);

// Check for errors
if (curl_errno($ch)) {
    echo 'cURL error: ' . curl_error($ch);
}

// Close cURL session
curl_close($ch);

// Output response
echo $response;

Server-side stream example #

Below is an example of a server-side stream handler for a request from Redactor and submitting it using the Open AI API. This example is written in PHP. You can modify it according to your server-side environment.


<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

// Your OpenAI API key
$openaiApiKey = 'YOUR-API-KEY';

// API endpoint
$url = $_POST['url'];

// Data payload
$data = json_decode($_POST['data']);

// Set the curl options for the API request
$options = array(
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POSTFIELDS => json_encode($data),
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'Authorization: Bearer ' . $openaiApiKey
  ),
  // Process each chunk of data as it arrives
  CURLOPT_WRITEFUNCTION => function($ch, $chunk) {
    echo $chunk;
    return strlen($chunk);
  }
);

// Initialize the curl request
$ch = curl_init();

// Set the curl options specified earlier
curl_setopt_array($ch, $options);

// Execute the curl request and close the connection
curl_exec($ch);
curl_close($ch);

What data Redactor sends to the server? #

Redactor makes a POST request to the server and sends the following data:

  • url — is the endpoint parameter specified in the AI plugin settings. I.e. the address of the request to OpenAI API.
  • data — is a set of JSON parameters that form a request to the API.

Here's an example of what the data created by the editor and sent to the server could look like.

Text request


// post data
{
    "model": "gpt-4o",
    "stream": true,
    "messages": [
        {
            "role": "user",
            "content": "Some text..."
        },
        {
            "role": "user",
            "content":"Improve it"
        }
    ]
}

Image request


// post data
{
    "model": "dall-e-3",
    "n": 1,
    "size": "1792x1024",
    "prompt": "Some prompt..."
}

How should the server's response look? #

OpenAI API returns all requests in JSON format. This is exactly what you need for Redactor. So take this into account in your implementation.

In fact, just return the response from OpenAI to the server as it is in JSON format and as described in the OpenAI documentation.

Node.js and Python #

You can find examples of how to implement a request with Node.js or Python to the OpenAI API in the documentation OpenAI.

Open AI has clear and simple SDKs for this. You just need to accept Post data from the editor and send it using libraries to the OpenAI API.

Settings #

stream

Turns off a request to the API as a stream.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: false
        }
    }
});

items

Default

items: {
    improve: { title: 'Improve it', command: 'ai.set', params: { prompt: 'Improve it' } },
    simplify: { title: 'Simplify it', command: 'ai.set', params: { prompt: 'Simplify it' } },
    fix: { title: 'Fix any mistakes', command: 'ai.set', params: { prompt: 'Fix any mistakes' } },
    shorten: { title: 'Make it shorter', command: 'ai.set', params: { prompt: 'Make it shorter' } },
    detailed: { title: 'Make it more detailed', command: 'ai.set', params: { prompt: 'Make it more detailed' } },
    complete: { title: 'Complete sentence', command: 'ai.set', params: { prompt: 'Complete sentence' } },
    tone: { title: 'Change tone', command: 'ai.popupTone' },
    translate: { title: 'Translate', command: 'ai.popupTranslate' }
}

Adds items to the AI dropdown.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        items: {
            turnToBullets: { title: 'Turn prose to bullets', command: 'ai.set', params: { prompt: 'Turn prose to bullets' } }
        },
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        }
    }
});

Replace items in the AI dropdown with your own.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        items: {
            set: true,
            improve: { title: 'Improve it', command: 'ai.set', params: { prompt: 'Improve it' } },
            simplify: { title: 'Simplify it', command: 'ai.set', params: { prompt: 'Simplify it' } },
            fix: { title: 'Fix any mistakes', command: 'ai.set', params: { prompt: 'Fix any mistakes' } }
        },
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        }
    }
});

tone

Default

tone: [
    'Academic',
    'Assertive',
    'Casual',
    'Confident',
    'Constructive',
    'Empathetic',
    'Exciting',
    'Fluent',
    'Formal',
    'Friendly',
    'Inspirational',
    'Professional'
]

Creates your own set of writing tone.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        tone: [
            'Academic',
            'Fluent',
            'Professional'
        ],
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        }
    }
});

style

Default

style: [
    '3d model',
    'Digital art',
    'Isometric',
    'Line art',
    'Photorealistic',
    'Pixel art'
]

Creates your own set of image styling tone.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        style: [
            'Photorealistic',
            'Pixel art'
        ],
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        },
        image: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/images/generations',
            model: 'dall-e-3'
        }
    }
});

translate

Default

translate: [
    'Arabic',
    'Chinese',
    'English',
    'French',
    'German',
    'Greek',
    'Italian',
    'Japanese',
    'Korean',
    'Portuguese',
    'Russian',
    'Spanish',
    'Swedish',
    'Ukrainian'
]

Creates your own set of translate languages.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        translate: [
            'English',
            'Finnish',
            'Swedish'
        ],
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        }
    }
});

Additional data #

When you make a request to the server side, it is possible to send additional parameters with the same POST request.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true,
            data: {
                id: 10,
                elements: '#my-input-1, #my-input-2, #my-form'
            }
        }
    }
});

The example above shows how to send POST params with request. The data object has key=value params, including values in the input and form fields if the additional object has key elements with field and form selectors.

The values of the input and form fields are appended as values: field-name: value.

Passing an empty prompt

Sometimes you may need to make an empty prompt from the editor. In this case, you will generate it on the server side, for example, using additional parameters as described above. You can do this by specifying your custom query items with the empty parameter, like this:


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true,
            data: {
                id: 10,
                elements: '#my-input-1, #my-input-2, #my-form'
            },
            items: {
                empty: { title: 'Empty request', command: 'ai.set', params: { empty: true } },
                turn: { title: 'Turn prose to bullets', command: 'ai.set', params: { prompt: 'Turn prose to bullets' } },
                improve: { title: 'Improve it', command: 'ai.set', params: { prompt: 'Improve it' } },
                simplify: { title: 'Simplify it', command: 'ai.set', params: { prompt: 'Simplify it' } },
                fix: { title: 'Fix any mistakes', command: 'ai.set', params: { prompt: 'Fix any mistakes' } },
                shorten: { title: 'Make it shorten', command: 'ai.set', params: { prompt: 'Make it shorten' } },
                detailed: { title: 'Make it more detailed', command: 'ai.set', params: { prompt: 'Make it more detailed' } },
                complete: { title: 'Complete sentence', command: 'ai.set', params: { prompt: 'Complete sentence' } },
                tone: { title: 'Change tone', command: 'ai.popupTone' },
                translate: { title: 'Translate', command: 'ai.popupTranslate' }
            }
        }
    }
});

In this case, the AI plugin will send a request to the server with an empty prompt.

Events #

AI Tools plugin has several events, for example, ai.before.send event that sends xhr. This can be useful if you want to add your headers to the request.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        },
        subscribe: {
            'ai.before.send': function(event) {
                let xhr = event.get('xhr');
                xhr.setRequestHeader('X-CSRF-Token', 'your-token-value');
            }
        }
    }
});

See more about AI Tools events.

Capturing images created by AI tools #

The AI plugin allows you to set a URL for processing generated images. To enable this, use the ai.image.save configuration option.


Redactor('#entry', {
    plugins: ['ai'],
    image: {
        save: '/save-image-api-url/',
        url: '/prompt-path-to-server-side/',
        endpoint: 'https://api.openai.com/v1/images/generations',
        model: 'dall-e-3'
    }
});

When an image is generated by the AI, it will be automatically sent via a POST request to the URL specified in the settings. For example, in PHP, you can handle the incoming image by saving it to the server. After processing, you can return a JSON response containing the link to the stored image.


function generateRandomString($length = 10) {
    return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', $length)), 0, $length);
}

if (isset($_POST['url'])) {
    $imageUrl = $_POST['url'];

    // Generating a random file name
    $randomFileName = generateRandomString() . '.jpg';
    $imagePath = '/tmp/ai/' . $randomFileName;
    $root = '/root-path';

    // Download the image from the URL
    $imageData = file_get_contents($imageUrl);

    if ($imageData) {
        // Creating an image resource
        $image = imagecreatefromstring($imageData);

        if ($image !== false) {
            // Save the image in JPEG format
            imagejpeg($image, $root . $imagePath, 90); // 90 - JPEG quality
            imagedestroy($image); // Free up memory

            echo json_encode(['message' => 'Image saved successfully as JPG.', 'filename' => $imagePath]);
        } else {
            echo json_encode(['message' => 'Failed to create image from data.']);
        }
    } else {
        echo json_encode(['message' => 'Failed to download the image.']);
    }
} else {
    echo json_encode(['message' => 'No image URL provided.']);
}

The AI plugin provides events to manage the image saving request: ai.before.save and ai.save. For example, using the event, you can modify the data and the request itself before it is sent.


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        image: {
            save: '/save-image-api-url/',
            url: '/prompt-path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/images/generations',
            model: 'dall-e-3'
        }
    },
    subscribe: {
        'ai.before.save': function(event) {
            let data = event.get('data');
            let xhr = event.get('xhr');
            xhr.setRequestHeader('X-CSRF-Token', 'your-token-value');
            console.log(data);
        }
    }
});

See more about AI Tools events.

Translations #

AI plugin by default, contains the following object with language variables:


translations: {
    en: {
        "ai": {
            "placeholder-image": "Describe the image you want to generate.",
            "placeholder-text": "Tell me what you want to write.",
            "send": "Send",
            "stop": "Stop",
            "discard": "Discard",
            "insert": "Insert",
            "prompt": "Prompt",
            "image-style": "Image style",
            "change-tone": "Change tone"
        }
    }
}

You can change the language values to your own, for example when you start the editor like this:


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        }
    },
    lang: 'sv',
    translations: {
        sv: {
            "ai": {
                "placeholder-image": "Beskriv bilden du vill generera.",
                "placeholder-text": "Berätta vad du vill skriva.",
                "send": "Skicka",
                "stop": "Stoppa",
                "discard": "Kassera",
                "insert": "Infoga",
                "prompt": "Uppmaning",
                "image-style": "Bildstil",
                "change-tone": "Ändra ton"
            }
        }
    }
});

This example shows running Redactor with Swedish and with the plugin translated into Swedish.

See more details on how to customize the languages.

Translate prompts #

Also you can run the editor with all the plugin settings override them and in those settings specify text phrases in the language you want. This is what it might look like:


Redactor('#entry', {
    plugins: ['ai'],
    ai: {
        items: {
            improve: { title: 'Improve it', command: 'ai.set', params: { prompt: 'Improve it' } },
            simplify: { title: 'Simplify it', command: 'ai.set', params: { prompt: 'Simplify it' } },
            fix: { title: 'Fix any mistakes', command: 'ai.set', params: { prompt: 'Fix any mistakes' } },
            shorten: { title: 'Make it shorter', command: 'ai.set', params: { prompt: 'Make it shorter' } },
            detailed: { title: 'Make it more detailed', command: 'ai.set', params: { prompt: 'Make it more detailed' } },
            complete: { title: 'Complete sentence', command: 'ai.set', params: { prompt: 'Complete sentence' } },
            tone: { title: 'Change tone', command: 'ai.popupTone' },
            translate: { title: 'Translate', command: 'ai.popupTranslate' }
        },
        tone: [
            'Academic',
            'Assertive',
            'Casual',
            'Confident',
            'Constructive',
            'Empathetic',
            'Exciting',
            'Fluent',
            'Formal',
            'Friendly',
            'Inspirational',
            'Professional'
        ],
        style: [
            '3d model',
            'Digital art',
            'Isometric',
            'Line art',
            'Photorealistic',
            'Pixel art'
        ],
        translate: [
            'Arabic',
            'Chinese',
            'English',
            'French',
            'German',
            'Greek',
            'Italian',
            'Japanese',
            'Korean',
            'Portuguese',
            'Russian',
            'Spanish',
            'Swedish',
            'Ukrainian'
        ],
        size: {
            '1792x1024': 'Landscape',
            '1024x1792': 'Portrait',
            '1024x1024': 'Square'
        },
        makeit: 'Make it',
        translateto: 'Translate to',
        text: {
            url: '/path-to-server-side/',
            endpoint: 'https://api.openai.com/v1/chat/completions',
            model: 'gpt-4o',
            stream: true
        }
    }
});

Just replace the English text with your own, it should work.