dotAI Plugin

Last Updated: Apr 23, 2024
documentation for the dotCMS Content Management System

The dotAI plugin integrates powerful AI tools into your dotCMS instance, allowing new horizons of automation — content and image generation, semantic searches, workflows capable of handling nitty-gritty procedures like content tagging, and more.

Currently, these integrations leverage the services of OpenAI. In future iterations, the choice of AI service provider will become a configurable option. Additionally, the dotAI features will soon be available by default in forthcoming dotCMS releases, as these tools are folded into the core codebase; if upgrading to a version with dotAI core functionality, special actions may be required — most importantly, stopping and undeploying the plugin prior to upgrade.

Screenshot of dotAI App card within dotCMS.

The use of this plugin has three requirements:

  1. dotCMS Enterprise Edition, as this plugin has both Workflow and App components, both of which are Enterprise only;
  2. An OpenAI API key, obtained separately.
  3. Postgres 15 with the pgvector extension installed.
    • An easy way to bootstrap this is to pick the latter up with docker pull ankane/pgvector; ensure image: ankane/pgvector appears under db in your docker-compose.yml file.
    • In other cases, see the Embeddings Note.

Building, Deploying, and Configuring the Plugin

You can find the plugin's source at its GitHub repository; clone it, and then run ./gradlew jar in its root directory to build the JAR file under com.dotcms.ai/build/libs.

Upload the file via DevTools → Plugins. Once it's uploaded and confirmed as active, head to System → Apps to configure it.

Installed dotZapier plugin.

As with any App integration, determine whether you're configuring the dotAI plugin for a single site or the System Host — i.e., all sites. Then, add your OpenAI key to the appropriate API Key required field.

App Configuration

A full list of the App integration fields follows:

FieldDescription
API KeyYour account's API key; must be present to utilize OpenAI services.
Role PromptA prompt describing the role (if any) the text generator will play for the dotCMS user.
Text PromptA prompt describing the overall writing style of generated text.
Image PromptA specification of output aspect ratio. If the ratio specified differs significantly from the Image Size (below), the image will “letterbox” accordingly.
Image SizeSelects the default dimensions of generated images.
ModelThe text model used to generate written responses.
Image ModelThe image model used to generate graphical responses.
Auto Index Content ConfigAllows App-level configuration of content indexes used as the basis for text generation. Takes a JSON mapping; each property name becomes an index, and each value is the Content Type it will take as its target content. Optional; indexes are also fully configurable under the dotAI Tool. Most useful when configured in the System Host, as this will instantiate the indexes across multiple sites.
Custom PropertiesAdditional key-value pairs for dotAI configuration.

Once installed and configured, the plugin surfaces a variety of utilities throughout dotCMS, described in the sections below.

dotAI Tool

Appearing in the dotCMS backend under Dev Tools -> dotAI, this tool allows the immediate use of AI operations in context, as well as additional management and configuration options.

dotAI Tool tabs.

Semantic Content Search and Chat

The first tab in the tool allows the use and testing of search and chat operations. In its simplest expression, select an index to search, a response type, and enter a prompt. Results will be displayed on the right, including the number of matches, relative vectorized semantic distance from the prompt, and an excerpt representing the best match in each entry.

Further options are surfaced behind the Advanced button. See below for the complete list.

FieldDescription
Content IndexSelect one of the indexes defined under Manage Embeddings/Indexes or Auto Index Content Config.
Response TypeChoose between Semantic Search, which uses only dotCMS content to provide results; Streaming Chat, which incorporates more of OpenAI's wider-reaching linguistic capabilities along with dotCMS supporting content to give plain-language responses to open-ended prompts; and REST/JSON Chat, which provides the same results as Streaming Chat, plus a full and untruncated “completion” response JSON object containing a wealth of information about the query, its response, and the information used in its generation. See the REST ai/completions call for an example of such a response.
PromptThe direct user-input search or chat prompt to which the tool will respond.
ModelThe text model used to generate the response.
TemperatureDetermines randomness of the response. Takes a number between 0 and 2, with 0 as the most deterministic and 2 as the most random. Decimal fractions are permitted.
Response LengthThe maximum length of the response, specified in “tokens.” According to OpenAI, each token is approximated at about 4 characters in English, or about 3/4 of an average word — i.e., 100 tokens tends to yield about 75 words.
Vector OperatorDetermines how the semantic distance function is calculated, with the choice of Cosine Similarity, Distance, or Inner Product. The distinction is technical and perhaps a bit esoteric; according to OpenAI, “the choice of distance function typically doesn't matter much.”
Distance ThresholdA number, from 0 to 1, indicating allowable semantic distance. Lower numbers yield more similar results.
SiteAllows limiting results to one Site. Leave blank to include all Sites.
Content TypesAllows limiting results to one or more Content Type, represented as a comma-separated list. Leave blank to include all.

Image Playground

The Image Playground can create graphics on demand; just input a prompt, select a size, and hit Submit.

The right-hand side of the pane will display the resulting image; a Save button underneath, which will immediately capture the image to your system as a dotAsset; and additional information in the form of the rewritten, exact OpenAI prompt that generated the image, and a JSON object containing the original prompt, revised prompt, url, temp file name, etc.

dotAI Tool tabs.

Manage Embeddings/Indexes

This tab provides a view of all indexes configured to interact with the dotAI plugin. From here, you can add more indexes, or rebuild or delete existing ones.

When building an index, use the Content to Index by Query field to input the content via Lucene query syntax. Under the Advanced button, two additional options surface: You can use a Velocity Template to specify which fields should be included and in what fashion, or a list of fields by variable to include. Leaving the Advanced fields blank will cause dotCMS to attempt to detect which fields are most relevant for generating content indexes.

To delete any existing index on the table, click the delete link to the right of its row; to rebuild the whole list, click the rebuild db link at the bottom of the table. To rebuild individual indexes, re-specify it using the left-hand menu; deletion is not necessary.

Embeddings Note: DB Extension Required

For embeddings to function, a vector extension must be added to the Postgres database. The dotAI plugin will add this extension automatically, but this process requires dotCMS's database user has superuser privileges, ensuring extensions can be installed.

If the database user does not have sufficient rights, it may be necessary for IT or administrators to manually add the extension. The simplest implementation is via the ankane/pgvector Docker tag, easily accessible via the command docker pull ankane/pgvector. The image can be applied to a docker-compose.yml by adding it to the database section:

 db:
    image: ankane/pgvector

Note also that these privileges are only required for the extension's installation, and not for its subsequent use.

Config Values

This tab contains a table listing all the plugin's configuration values. These are important because they can override and parameterize the prompts that it sends to OpenAI. All values displayed can be changed via the App configuration, either by altering the settings where a field exists, or by adding a Custom Property.

Blocks

Perhaps the simplest way to utilize dotAI is through the Block Editor, as the plugin introduces two new blocks: AI Content and AI Image.

Block selector showing the two new blocks.

The AI Content block creates a small field in which one can enter a prompt for text content. Once submitted, the block will asynchronously carry out the prompt and insert the resulting text when ready.

AI Content Block.

The AI Image block functions similarly, but generating graphics from either the text prompt supplied, or based on content present in the field by using the right-hand selector.

AI Image Block.

Workflow Sub-Actions

dotAI Workflow Actions.

The dotAI plugin adds four Workflow Sub-Actions, or actionlets, to dotCMS. Each is considered separately in its own subsection below.

For all asynchronous functions, it is recommended to save all user-generated content and exit the contentlet editor while the write action occurs in the background; the asynchronous process will save when complete, which can result in the loss of unsaved user-generated content.

AI Auto-Tag Content

This actionlet will attempt to Auto-tag your contentlet based on its values — automatically addressing itself to a Content Type's Tag field, when present. It takes five parameters:

ParameterDescription
Overwrite tagsSelect true or false. If false, it will only write to an empty field.
Limit the keywords to pre-existing tagsSelect true or false. If false, it will add tags to the dotCMS instance's list of tags.
Update content asynchronouslyTakes an integer value. If 0, it runs only at the time the actionlet is called; otherwise, after being called, it will update the tags in X seconds. (Please see the note about asynchronous Workflow processes.)
AI modelThe text model used to generate written responses.
AI temperatureDetermines randomness of the response. Takes a number between 0.1 and 2.0, with 0 as the most deterministic and 2 as the most random, or creative in associativity.

AI Content Prompt

This actionlet will generate text content. You may specify both a prompt, which can itself contain Velocity code referencing content data via the $dotContentMap built-in content object.

If the prompt specifies that the AI should return a JSON object, be sure to specify the keys of the relevant object properties; their associated values will be sent to the corresponding fields by variable name.

ParameterDescription
ResultsSpecify a field to receive the results of the prompt supplied. If left blank, be sure to indicate in the prompt that the AI should return a JSON object.
OverwriteSelect true or false. If false, it will only write to an empty field.
PromptThe prompt that will be sent to the AI. May include references to the content object via $dotContentMap.
Update content asynchronouslyTakes an integer value. If 0, it runs only at the time the actionlet is called; otherwise, after being called, it will update the tags in X seconds. (Please see the note about asynchronous Workflow processes.)
AI modelThe text model used to generate written responses.
AI temperatureDetermines randomness of the response. Takes a number between 0.1 and 2.0, with 0 as the most deterministic and 2 as the most random, or creative in associativity.

AI Embeddings

This actionlet gives more precise control over the “embeddings” of content. Embeddings are collections of numerical measurements that signify the relatedness of text strings, which are crucial for a variety of AI operations — in particular, for forming semantic search indexes.

If fields are not specify, the plugin will attempt to guess which fields should be used for indexing operations through the following guidelines:

TargetGuideline
ContentletIndex the first Block Editor, WYSIWYG, or TextArea field.
PageRender the Page and use the resultant HTML.
File Asset or dotAssetIndex the first binary field.

The AI Embeddings actionlet has three parameters:

ParameterDescription
List of fieldsA comma- or newline-separated list of fields, in the form of {contentType}.{fieldVar} (or simply {contentType}) used to generate embeddings. If blank, the plugin will attempt to intelligently select which fields are used in indexing operations.
Index NameThe name of the index appearing in the dotAI Tool to be acted upon.
Delete or InsertDepending on this selection, this actionlet can either add or remove the relevant embedding from the specified index, allowing a high degree of fine tuning at the Workflow level.

AI Generate Image

This actionlet allows generation of images based on a dynamically defined prompt. As with the prompt in AI Content Prompt, the prompt may contain references to content via the $dotContentMap built-in content object.

The actionlet's parameters are as follows:

ParameterDescription
Image fieldSpecify to which field, by variable, the image should be saved. If left blank, the process will select the first binary field in the contentlet.
OverwriteSelect true or false. If false, it will only write to an empty field.
PromptThe prompt that will be sent to the AI. May include references to the content object via $dotContentMap.
Update content asynchronouslyTakes an integer value. If 0, it runs only at the time the actionlet is called; otherwise, after being called, it will update the tags in X seconds. (Please see the note about asynchronous Workflow processes.)

Velocity Viewtool

The dotAI plugin makes available a Velocity viewtool accessible through $ai, allowing several AI operations via Velocity script.

Basic Viewtool Generation Tools

The AI Viewtool surfaces two important content generation methods:

  • $ai.generateText(prompt)
  • $ai.generateImage(prompt)

Both may take as their prompt argument either a string or a map — such as a content object. Both methods return a JSON object, though their respective schemata differ.

The object returned by text generation operations assumes the following form, with the content property of each object in the choices array containing the requested output:

{
    "id":"chatcmpl-8rKTdjmfNyD1iEKw5qGD98qc14CW7",
    "object":"chat.completion",
    "created":1707720789,
    "model":"gpt-3.5-turbo-16k-0613",
    "choices":[
        {
            "index":0,
            "message":{
                "role":"assistant",
                "content":"Hello! Wishing you a wonderful day filled with joy and success."
            },
            "logprobs":null,
            "finish_reason":"stop"
        }
        ...
    ],
    "usage":{
        "prompt_tokens":45,
        "completion_tokens":14,
        "total_tokens":59
    },
    "system_fingerprint":null
}

In contrast, image-generation calls return an object with just five properties; the url property contains the direct link to the file output.

{
    "revised_prompt":"Create a meticulous line drawing showcasing a perfectly symmetrical circle neatly inscribed within a geometric square. The lines should be crisp, clear, and simple, evoking a serene sense of symmetry and balance. Please maintain the monochromatic tonality to highlight the geometric simplicity of the drawing.",
    "url":"https://example.url/circle_20240212_065752.png?query=param&others=whynot",
    "originalPrompt":"Produce a line drawing of a circle inscribed in a square.",
    "tempFileName":"circle_20240212_065752.png",
    "response":"temp_a89ea3aabc"
}

Viewtool Searches

$ai.search is a sub-tool of the AI Viewtool, with two methods of its own:

  • $ai.search.query(query, index)
  • $ai.search.related(content, index)

Searches using query() may include query arguments that consist of a string, or a map object. The latter should specify multiple parameters manually.

Example of a query by string:

## Run a semantic query
#set($results = $ai.search.query("Where can I find the Best beaches?", "blogIndex"))
total: $results.count
limit: $results.limit
offset: $results.offset
threshold: $results.threshold

$results.query
#foreach($result in $results.dotCMSResults)
    $result.title
    #foreach($m in $result.matches)
        - $m.distance : $m.extractedText
    #end
#end

Query by map:

## A semantic query using a Map of Parameters
#set($query = {})
$!query.put("query", "Where can I find the Best beaches?")
$!query.put("contentType", "Blog")
$!query.put("indexName", "blogIndex")
$!query.put("limit", "100")
$!query.put("offset", "0")
## $!query.put("host", "SYSTEM_HOST")
## $!query.put("language", "1")
## $!query.put("fieldVar", "content")
#set($results = $ai.search.query($query ))
total: $results.count
limit: $results.limit
offset: $results.offset
threshold: $results.threshold

$results.query
#foreach($result in $results.dotCMSResults)
    $result.title
    #foreach($m in $result.matches)
        - $m.distance : $m.extractedText
    #end
#end

The second method of the AI Search tool is related(content, index), which takes a map, such as — but not limited to — a full contentlet as its first argument:

##  Finding related Content
#set($content = $dotcontent.find("48ec3192-5f04-466b-b7cd-7134f3ea4d67"))

## send in a contentlet to find related content in the index "blog"
#set($relatedContent = $ai.search.related($content, "blogIndex"))

#foreach($result in $relatedContent.dotCMSResults)
    $result.title : $result.inode
    #foreach($m in $result.matches)
        - $m.distance : $m.extractedText
    #end
#end

In any of the above cases, the returned JSON object will use the following “completion” structure, albeit slightly truncated:

{
    "timeToEmbeddings":"860ms",
    "total":5,
    "query":"What's the best place to vacation?",
    "threshold":0.25,
    "dotCMSResults":[
        {
            <content object properties>,
            "matches":[
                {
                    "distance":0.21085739135742188,
                    "extractedText":"<text excerpt>..."
                },
                ...
            ]
        },
        ...
    ],
    "operator":"<=>",
    "offset":0,
    "limit":50,
    "count":3
}

Viewtool Embeddings

The AI Viewtool has an embeddings sub-tool containing three methods that perform operations relevant to embeddings — which, to reiterate, are vector calculations that assist in performing semantic indexing.

MethodDescription
$ai.embeddings.countTokens(prompt)Takes a string argument; returns an integer reflecting how many tokens the string argument would represent.
$ai.embeddings.generateEmbeddings(prompt)Takes a string argument; returns an array of floating-point numbers representing the embeddings of the provided string.
$ai.embeddings.getIndexCount() or $ai.embeddings.indexCountReturns a map containing a list of indexes (see below for example).

Each entry in the object returned by indexCount contains several statistics, in the following fashion:

{
    blogIndex={
        contentTypes=Blog,
        tokensPerChunk=306,
        tokenTotal=2146,
        fragments=7,
        contents=3
    }, 
    ...
}

Viewtool Completions

The completions sub-tool provides three methods that make use of “chat”-style functionality.

MethodDescription
$ai.completions.getConfig() or $ai.completions.configReturns a map reflecting the current set of prompt configurations (see below).
$ai.completions.summarize(prompt, [index])Accepts a string prompt and an index, using the default index if the latter is not specified. More than a simple list of results, as with a search.query(), summarize also attempts to provide a short explanation based on the results collected.
$ai.completions.raw(prompt)Accepts either a map, a JSON object, or a string representing a JSON object. Returns a JSON object containing a chat response.

Here is an example of the map returned by the config method:

{
    com.dotcms.ai.completion.model=gpt-3.5-turbo-16k,
    com.dotcms.ai.completion.role.prompt=You are a helpful assistant with a descriptive writing style.,
    com.dotcms.ai.completion.text.prompt=Answer this question\n\"$!{prompt}?\"\n\nby using only the information in the following text:\n"""\n$!{supportingContent} \n"""\n
}

And an example of the summarize() method in action:

#set($summary = $ai.completions.summarize("Where can I find the Best beaches?", "blogIndex"))

model: $summary.openAiResponse.model
prompt: $summary.openAiResponse.usage.prompt_tokens
tokens: $summary.openAiResponse.usage.total_tokens
$summary.openAiResponse.choices.get(0).message.content

…returns an object as $summary resembling the following:

{
    "timeToEmbeddings":"67ms",
    "total":2,
    "query":"Where can I find the Best beaches?",
    "threshold":0.25,
    "dotCMSResults":[
        {
            <content object properties>,
            "matches":[
                {
                    "distance":0.2304542511701584,
                    "extractedText":"<excerpt>..."
                },
                ...
            ]
        }
    ],
    "operator":"<=>",
    "offset":0,
    "limit":50,
    "count":1,
    "openAiResponse":{
        "id":"chatcmpl-8rPgXQi1SpHgq4F6dJ2kH7WHMuwh5",
        "object":"chat.completion",
        "created":1707740809,
        "model":"gpt-3.5-turbo-16k-0613",
        "choices":[
            {
                "index":0,
                "message":{
                    "role":"assistant",
                    "content":"Based on the given text, Costa Rica is known for its beautiful beaches and is a great destination for beach lovers. The Pacific coast of Costa Rica in particular offers some of the best surfing spots in the world. Whether you are a seasoned surfer or a beginner, you will find a beach that suits your skill level perfectly. Additionally, there are also opportunities for wildlife viewing along the coast, where you can observe various species of birds, monkeys, and other wildlife. So, if you are looking for the best beaches, Costa Rica's Pacific coast is a must-visit destination."
                },
                "logprobs":null,
                "finish_reason":"stop"
            }
        ],
        "usage":{
            "prompt_tokens":680,
            "completion_tokens":116,
            "total_tokens":796
        },
        "system_fingerprint":null
    }
}

Finally, the raw chat might be used in the following fashion with a string prompt:

#set($prompt = '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "You are a chatbot providing travel advice to people who visit a travel website; provide an enticing description of the beaches of Costa Rica"
}
]
}')

#set($chat = $ai.completions.raw($prompt))

Or, for a map as prompt:

#set($prompt = {})
$!prompt.put("model", "gpt-3.5-turbo")
#set($messages =[])
#set($message ={})
$!message.put("role", "user")
$!message.put("content", "You are a chatbot providing travel advice to people who visit a travel website; provide an enticing description of the beaches of Costa Rica")
$messages.add($message)
$prompt.put("messages", $messages)

#set($chat = $ai.completions.raw($prompt))

It returns its response in the following format:

{
    "id":"chatcmpl-8rQ1ZIXIHXEjDiOeKuSyCeuqEVdlH",
    "object":"chat.completion",
    "created":1707742113,
    "model":"gpt-3.5-turbo-0613",
    "choices":[
        {
            "index":0,
            "message":{
                "role":"assistant",
                "content":"Welcome to the tropical paradise of Costa Rica, a dream destination for beach lovers! Get ready to immerse yourself in a world of breathtaking beauty where pristine sandy shores meet crystal-clear turquoise waters. \n\nCosta Rica boasts an incredible 800 miles of coastline, offering an array of picturesque beaches to suit every traveler's taste..."
            },
            "logprobs":null,
            "finish_reason":"stop"
        }
    ],
    "usage":{
        "prompt_tokens":33,
        "completion_tokens":432,
        "total_tokens":465
    },
    "system_fingerprint":null
}

REST API Resources

The plugin offers a wealth of API tools to undertake OpenAI operations headlessly. Similar to the viewtool, the endpoints include those that focus on generation, searching, embeddings, and completions.

The various endpoints dwell as child paths of /api/v1/ai, outlined below by category and path.

Generative Endpoints

PathMethod
/api/v1/ai/text/generateGET
/api/v1/ai/text/generatePOST
/api/v1/ai/image/generateGET
/api/v1/ai/image/generatePOST

/api/v1/ai/text/generate

When called with GET, only a prompt path parameter is required:

curl -X 'GET' \
  'http://localhost:8082/api/v1/ai/text/generate?prompt=vacationing%3F' \
  -H 'accept: application/json'

The response object resembles that of the viewtool's $ai.completions.raw() method:

{
  "id": "chatcmpl-8rREz6irGDpHEqRbOIGQSaq1MKb8n",
  "object": "chat.completion",
  "created": 1707746789,
  "model": "gpt-3.5-turbo-16k-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Vacationing is a great way to relax, recharge, and explore new places. Whether you prefer a beach getaway, a mountain retreat, or a city adventure, there are endless possibilities for your vacation. From sunbathing on the beach, hiking in nature, visiting historical landmarks, or trying local cuisines, vacationing allows you to escape your daily routine and immerse yourself in new experiences. Whether you're planning a solo trip, a family vacation, or a romantic getaway, make sure to plan ahead, pack accordingly, and take the time to enjoy every moment of your well-deserved vacation."
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 38,
    "completion_tokens": 121,
    "total_tokens": 159
  },
  "system_fingerprint": null
}

When called with POST, a data payload is instead required, of the type described under the section on completion forms, providing a greater range of configurability. However, the actual submissions can be as simple as only including a prompt property, as follows:

curl -X 'POST' \
  'http://localhost:8082/api/v1/ai/text/generate' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "prompt": "tell me about costa rica!"
}'

/api/v1/ai/image/generate

When called with GET, only a prompt path parameter is required:

curl -X 'GET' \
  'http://localhost:8082/api/v1/ai/image/generate?prompt=a%20pig%20wearing%20a%20hat' \
  -H 'accept: application/json'

The result is as follows:

{
  "revised_prompt": "A playful farm scene showcasing a pink pig. This pig is unique, it is wearing a hat possibly a sunhat for the bright sunny day. The hat is delicately perched on the pig's big head. Attention is drawn to the pig's gleeful expressions, and the curious sparkle in its eyes. Different shades of pink colour are reflected on its body indicating a healthy pig with a tinge of fun and eccentricity with the presence of the hat. The whole ambiance of the farm is of warmth and vitality. A light breeze may be rustling through making the image lively.",
  "url": "https://oaidalleapiprodscus.blob.core.windows.net/private/org-qZnNeZvyp7uFUh2EX8AJA6gw/user-2MAFKcfsfuJ7ILKoM3ZYWUcs/img-sLsWAgFW0B7U5cLaRblzmQeF.png?st=2024-02-12T13%3A29%3A31Z&se=2024-02-12T15%3A29%3A31Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-02-12T14%3A27%3A13Z&ske=2024-02-13T14%3A27%3A13Z&sks=b&skv=2021-08-06&sig=qwmBl4uETTfn14v4jQhUUUeH1HihtKm2m4udyxlmm%2BE%3D",
  "originalPrompt": "a pig wearing a hat",
  "tempFileName": "pig_20240212_022931.png",
  "response": "temp_298431dce8"
}

An image of a pig wearing a hat, generated by API call.

Calling this path with the POST method instead requires a JSON data payload with three properties: a prompt string, a numberOfImages integer, and a size string corresponding to one of the selectable image dimensions: 256x256, 512x512, 1024x1024,1024x1792, or 1792x1024.

Example:

curl -X 'POST' \
  'http://localhost:8082/api/v1/ai/image/generate' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "prompt": "a ball of string",
  "numberOfImages": 1,
  "size": "1024x1024"
}'

Search Endpoints

PathMethod
/api/v1/ai/searchGET
/api/v1/ai/searchPOST
/api/v1/ai/search/relatedGET
/api/v1/ai/search/relatedPOST

/api/v1/ai/search

When performing a GET call to ai/search, a list of query parameters are accepted that parallel the properties of the completion form used in many other functions.

However, specifying a query and index should suffice as a bare minimum to yield results:

curl -X 'GET' \
  'http://localhost:8082/api/v1/ai/search?query=snow&indexName=blogIndex' \
  -H 'accept: application/json'

The returned object will have the same schema as that returned by a viewtool search function, farther up the page.

The POST version looks for the same data, albeit in payload form rather than query parameters.

/api/v1/ai/search/related

A GET call to ai/search/related, calls for a sequence of query parameters that point the search function toward the relevant content already in the system, which will be used to perform a related-content search. The five parameters are: language, identifier, inode, indexName, and fieldVar — the first three pointing to a piece of content, the fourth to an index, and the fifth to a specific field within the contentlet.

curl -X 'GET' \  'http://localhost:8082/api/v1/ai/search/related?language=0&identifier=3d6fa2a4-2b48-4421-a2f5-b6518a7c0830&inode=ae49b86b-6bd2-47d4-924e-c8e747d9bf0a&indexName=blogIndex&fieldVar=blogContent' \
  -H 'accept: application/json'

The POST call follows the same pattern, albeit submitting its data in payload form instead of query parameters.

curl -X 'POST' 'http://localhost:8082/api/v1/ai/search/related' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "language":0,
  "identifier":"3d6fa2a4-2b48-4421-a2f5-b6518a7c0830",
  "inode":"ae49b86b-6bd2-47d4-924e-c8e747d9bf0a",
  "indexName":"blogIndex",
  "fieldVar":"blogContent"
}'

Embeddings Endpoints

PathMethod
/api/v1/ai/embeddingsPOST
/api/v1/ai/embeddingsDELETE
/api/v1/ai/embeddings/dbDELETE
/api/v1/ai/embeddings/indexCountGET

/api/v1/ai/embeddings

The two methods associated with this path — POST and DELETE — either add or remove embeddings from the chosen index, without otherwise rebuilding or destroying the index.

POST utilizes the embeddings form properties in its payload data.

curl -X 'POST' \
  'http://localhost:8082/api/v1/ai/embeddings' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "+contentType:blogIndex +variant:default +live:true",
  "limit": 1000,
  "offset": 0,
  "indexName": "blogIndex",
  "model": "gpt-3.5-turbo-16k",
  "fields":"blogContent"
}'

The DELETE version differs slightly. It still uses a payload, but its data members more closely resemble a related search, as it looks for indexName, identifier, inode, language, contentType, site, and deleteQuery — once again a Lucene query. As such, embeddings can be deleted on the basis of any of those — from individual contentlets to whole Content Types, to arbitrary query results.

/api/v1/ai/embeddings/db

DELETE call to this path can delete entire AI embeddings index database; use with care.

curl -XDELETE -k http://localhost:8082/api/v1/ai/embeddings/db \
-H "Content-Type: application/json" 

/api/v1/ai/embeddings/indexCount

GET call lists all indexes.

curl -X 'GET' \
  'http://localhost:8082/api/v1/ai/embeddings/indexCount' \
  -H 'accept: application/json'

Result excerpt:

{
    "indexCount":{
        "blogIndex":{
            "contentTypes":"Activity,Blog,calendarEvent,Image,webPageContent",
            "contents":42,
            "fragments":91,
            "tokenTotal":26797,
            "tokensPerChunk":294
        },
        ...,
        "cache":{
            "contentTypes":"cache",
            "contents":1,
            "fragments":80,
            "tokenTotal":21579,
            "tokensPerChunk":269
        }
    }
}

Embeddings Form

The schema of the payload object required for most embeddings operations takes the following form:

{
  "query": "string",
  "limit": int,
  "indexName": "string",
  "velocityTemplate": "string",
  "offset": int,
  "model": "string",
  "fields": "comma,separated,list,of,strings",
  "userId": "string"
}

Completions Endpoints

PathMethod
/api/v1/ai/completionsPOST
/api/v1/ai/completions/rawPromptPOST
/api/v1/ai/completions/configGET

/api/v1/ai/completions

Called with POST, this endpoint accepts a payload containing the properties of the completion form.

curl -XPOST -k http://localhost:8082/api/v1/ai/completions \
-H "Content-Type: application/json" \
-d '{
  "prompt": "where do i vacation?",
"threshold":".2",
"searchLimit":50,
"stream": false,
"indexName": "blogIndex"
}'

It returns data in the form of the following full, untruncated completion operation data, containing the detailed prompt response.

{
    "timeToEmbeddings":"860ms",
    "total":5,
    "query":"What's the best place to vacation?",
    "threshold":0.25,
    "dotCMSResults":[
        {
            <content object properties>,
            "matches":[
                {
                    "distance":0.21085739135742188,
                    "extractedText":"<text excerpt>..."
                },
                ...
            ]
        },
        ...
    ],
    "operator":"<=>",
    "offset":0,
    "limit":50,
    "count":3,
    "openAiResponse":{
        "id":"chatcmpl-8rUy56hdxj210olTgFbNfVpxKVQla",
        "object":"chat.completion",
        "created":1707761117,
        "model":"gpt-3.5-turbo-16k-0613",
        "choices":[
            {
                "index":0,
                "message": {
                    "role":"assistant",
                    "content":"Based on the information provided, Fiji would be an excellent destination for a vacation. Fiji is located in the heart of the South Pacific and is blessed with 333 tropical islands. The islands are known for their luxurious..."
                },
                "logprobs":null,
                "finish_reason":"stop"
            }
        ],
        "usage":{
            "prompt_tokens":325,
            "completion_tokens":139,
            "total_tokens":464
        },
        "system_fingerprint":null
    },
    "totalTime":"2998ms"
}

Note that if stream is set to true, the results will arrive continuously in a sequence of many smaller data properties, containing about a word of content at a time:

data: {
    "id":"chatcmpl-8rTfxumekRWYLmDdCZwnLxlNcWAEO",
    "object":"chat.completion.chunk",
    "created":1707756149,
    "model":"gpt-3.5-turbo-16k-0613",
    "system_fingerprint":null,
    "choices":[
        {
            "index":0,
            "delta":{
                "content":" a"
            },
            "logprobs":null,
            "finish_reason":null
        }
    ]
}

/api/v1/ai/completions/rawPrompt

In addition to the above, completions/rawPrompt is an option that yields a completion response in a lighter-weight fashion, omitting the bulky dotCMSResults property and other context and metadata. The data members of the call are identical.

curl -X 'POST' 'http://localhost:8082/api/v1/ai/completions/rawPrompt' \
  -H 'accept: application/octet-stream' \
  -H "Content-Type: application/json" \
  -d '{
  "prompt": "testing costa rica for water",
  "searchLimit": 1000,
  "searchOffset": 0,
  "responseLengthTokens": 128,
  "language": 0,
  "stream": false,
  "fieldVar": "blogContent",
  "indexName": "blogIndex",
  "threshold": 0,
  "temperature": 1,
  "model": "gpt-3.5-turbo-16k",
  "operator": "<=>",
  "site": "demo.dotcms.com"
}'

The significantly leaner reply proceeds directly to the contents of what had been the openAiResponse property, above.

{
    "id":"chatcmpl-8rVNcDXxU8Z1BHwjpJLEfPJorrMHU",
    "object":"chat.completion",
    "created":1707762700,
    "model":"gpt-3.5-turbo-16k-0613",
    "choices":[
        {
            "index":0,
            "message":{
                "role":"assistant",
                "content":"When it comes to testing water in Costa Rica, there are a few important factors to consider. Costa Rica generally has good water quality, but it's always a good idea to take precautions to ensure that the water you consume is safe. Here are some options for testing water in Costa Rica..."
            },
            "logprobs":null,
            "finish_reason":"length"
        }
    ],
    "usage":{
        "prompt_tokens":13,
        "completion_tokens":128,
        "total_tokens":141
    },
    "system_fingerprint":null,
    "totalTime":"2475ms"
}

/api/v1/ai/completions/config

Sending a GET call to this path yields a JSON object describing the configuration of fundamental prompts and settings contained in the App configuration, and visible in the Config Values tab in the dotAI Tool.

curl -X 'GET' \
  'http://localhost:8082/api/v1/ai/completions/config' \
  -H 'accept: application/json'

A typical returned object will resemble this:

{
    "apiImageUrl":"https://api.openai.com/v1/images/generations",
    "apiKey":"*****",
    "apiUrl":"https://api.openai.com/v1/chat/completions",
    "availableModels":[
        "gpt-3.5-turbo",
        "gpt-3.5-turbo-16k",
        "gpt-4","gpt-4-1106-preview"
    ],
    "com.dotcms.ai.completion.default.temperature":"1",
    "com.dotcms.ai.completion.model":"gpt-3.5-turbo-16k",
    "com.dotcms.ai.completion.role.prompt":"You are a helpful assistant with a descriptive writing style.",
    "com.dotcms.ai.completion.text.prompt":"Answer this question\\n\\\"$!{prompt}?\\\"\\n\\nby using only the information in the following text:\\n\"\"\"\\n$!{supportingContent} \\n\"\"\"\\n",
    "com.dotcms.ai.debug.logging":"false",
    "com.dotcms.ai.embeddings.build.for.file.extensions":"pdf,doc,docx,txt,html",
    "com.dotcms.ai.embeddings.cache.size":"1000",
    "com.dotcms.ai.embeddings.cache.ttl.seconds":"600",
    "com.dotcms.ai.embeddings.delete.old.on.update":"true",
    "com.dotcms.ai.embeddings.minimum.file.size":"1024",
    "com.dotcms.ai.embeddings.minimum.text.length":"64",
    "com.dotcms.ai.embeddings.model":"text-embedding-ada-002",
    "com.dotcms.ai.embeddings.search.default.threshold":".25",
    "com.dotcms.ai.embeddings.split.at.tokens":"512",
    "com.dotcms.ai.embeddings.threads":"3",
    "com.dotcms.ai.embeddings.threads.max":"6",
    "com.dotcms.ai.embeddings.threads.queue":"10000",
    "configHost":"demo.dotcms.com (falls back to system host)",
    "imageModel":"dall-e-3",
    "imagePrompt":"Use 3:4 aspect ratio.",
    "imageSize":"1792x1024",
    "listenerIndexer":"{\n    \"blogIndex\":\"Blog.blogContent\"\n}",
    "model":"gpt-3.5-turbo-16k",
    "rolePrompt":"You are dotCMSbot, and AI assistant to help content creators generate and rewrite content in their content management system.",
    "textPrompt":"Use Descriptive writing style."
}

Completion Form

The schema of the payload object required for most completions operations or text generation takes the following form:

{
  "prompt": "string",
  "searchLimit": int,
  "searchOffset": int,
  "responseLengthTokens": int,
  "language": int,
  "stream": bool,
  "fieldVar": "string",
  "indexName": "string",
  "contentType": "comma,separated,list,of,strings",
  "threshold": float,
  "temperature": float,
  "model": "string",
  "operator": "string",
  "site": "string"
}

On this page

×

We Dig Feedback

Selected excerpt:

×