Power Optimizely CMS using the same language model as ChatGPT

Image generated by OpenAI's image generation API, using Optimizely CMS

On March 1st, OpenAI released their Chat completion API using the gpt-3.5-turbo language model, the same language model used by ChatGPT. The new model performs with similar capabilities as the older text-davinci-003 model but at 10% of the price.

The most straightforward form of integration with Optimizely CMS is adding a few extra properties to the page types you want to supercharge with AI capabilities.

The result from the above input is this blog post. It also includes an image generated using the same technique and the image generation API from OpenAI, with the same model as DALLĀ·E 2.

To generate content, enter the prompt, check the appropriate checkboxes, and publish. Within a second or two, your content is generated!

This can be achieved using good old content events, like this.

public static void OnPublishingContent(object sender, ContentEventArgs e)
{
    if (e.Content is BlogPostPage blogPostPage)
    {
        if (blogPostPage.AiMainBodyBlock.GenerateMainBodyFromPrompt)
        {
            const string prefix = "Generate a blog post for the following prompt, and use html p-tags to delimit paragraphs:\r\n";
            blogPostPage.MainBody = new XhtmlString(HttpUtility.HtmlDecode(_service.GenerateText(prefix + blogPostPage.AiMainBodyBlock.Prompt).Result));
            blogPostPage.AiMainBodyBlock.GenerateMainBodyFromPrompt = false;
        }

        if (blogPostPage.AiHeadingBlock.GenerateHeadingFromMainBody)
        {
            const string prefix = "Generate a heading for the following text:\r\n";
            blogPostPage.Name = _service.GenerateText(prefix + blogPostPage.MainBody.ToHtmlString().StripHtml()).Result;
            blogPostPage.AiHeadingBlock.GenerateHeadingFromMainBody = false;
        }
        else if (blogPostPage.AiHeadingBlock.GenerateHeadingFromPrompt)
        {
            const string prefix = "Generate a heading using the following prompt:\r\n";
            blogPostPage.Name = _service.GenerateText(prefix + blogPostPage.AiHeadingBlock.Prompt.Result);
            blogPostPage.AiHeadingBlock.GenerateHeadingFromPrompt = false;
        }
    }
}

And the properties placed in local blocks like this:

[ContentType(DisplayName = "AI Heading block", Description = "", GUID = "B7C007A2-BE9B-48F2-9394-930363A93453", AvailableInEditMode = false, Order = 100)]

public class AiHeadingBlock : BaseBlock
{
    [Display(Name = "Generer overskrift fra tekstinnhold", Order = 10)]
    public virtual bool GenerateHeadingFromMainBody { get; set; }

    [Display(Name = "Generer overskrift fra prompt", Order = 20)]
    public virtual bool GenerateHeadingFromPrompt { get; set; }

    [UIHint(UIHint.Textarea)]
    [Display(Name = "Prompt", Order = 30)]
    public virtual string Prompt { get; set; }
}

And this:

[ContentType(DisplayName = "AI MainBody block", AvailableInEditMode = false)]

public class AiMainBodyBlock : BaseBlock
{
    [Display(Name = "Generer tekstinnhold fra prompt", Order = 20)]
    public virtual bool GenerateMainBodyFromPrompt { get; set; }

    [UIHint(UIHint.Textarea)]
    [Display(Name = "Prompt", Order = 30)]
    public virtual string Prompt { get; set; }
}

And the service could look something like this.

public class OpenAiService
{
    private readonly HttpClient _client;
    private readonly string _endpointCompletions;
    private readonly string _endpointImages;
    private readonly IContentRepository _contentRepository;

    public OpenAiService(HttpClient httpClient, IContentRepository contentRepository)
    {
        _client = httpClient;
        var apiKey = "********";
        _endpointCompletions = "https://api.openai.com/v1/chat/completions";
        _endpointImages = "https://api.openai.com/v1/images/generations";
        _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
        _contentRepository = contentRepository;
    }

    /// <summary>
    /// Generate text from provided prompt
    /// </summary>
    /// <param name="prompt">The prompt</param>
    /// <param name="model">The model</param>
    /// <param name="temperature">Randomness</param>
    /// <returns></returns>
    public async Task<string> GenerateText(string prompt, string model = "gpt-3.5-turbo", double temperature = 0.9)
    {
        var sanitizedPrompt = prompt.StripHtml().Trim();

        var request = new HttpRequestMessage(HttpMethod.Post, _endpointCompletions);
        var messages = new []{ new{role = "user", content = sanitizedPrompt} };
        var content = new StringContent(JsonConvert.SerializeObject(new { messages = messages, model = model }), Encoding.UTF8, "application/json");
        request.Content = content;

        var response = await _client.SendAsync(request);
        var json = await response.Content.ReadAsStringAsync();
        var data = JsonConvert.DeserializeObject<dynamic>(json);

        var errorMessage = data.error?.message?.ToString();
        if (!string.IsNullOrEmpty(errorMessage))
        {
            throw new Exception(errorMessage);
        }

        var text = data?.choices[0]?.message?.content?.ToString();
        if (!string.IsNullOrEmpty(text))
        {
            return text;
        }

        return "Error generating AI text";
    }
}

In order to get started, register at https://platform.openai.com/ to access the API, and start prompting!