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.
The use of this plugin has three requirements:
- dotCMS Enterprise Edition, as this plugin has both Workflow and App components, both of which are Enterprise only;
- An OpenAI API key, obtained separately.
- Postgres 15 with the pgvector extension installed.
- An easy way to bootstrap this is to pick the latter up with
docker pull ankane/pgvector
; ensureimage: ankane/pgvector
appears underdb
in yourdocker-compose.yml
file. - In other cases, see the Embeddings Note.
- An easy way to bootstrap this is to pick the latter up with
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.
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:
Field | Description |
---|---|
API Key | Your account's API key; must be present to utilize OpenAI services. |
Role Prompt | A prompt describing the role (if any) the text generator will play for the dotCMS user. |
Text Prompt | A prompt describing the overall writing style of generated text. |
Image Prompt | A specification of output aspect ratio. If the ratio specified differs significantly from the Image Size (below), the image will “letterbox” accordingly. |
Image Size | Selects the default dimensions of generated images. |
Model | The text model used to generate written responses. |
Image Model | The image model used to generate graphical responses. |
Auto Index Content Config | Allows 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 Properties | Additional 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.
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.
Field | Description |
---|---|
Content Index | Select one of the indexes defined under Manage Embeddings/Indexes or Auto Index Content Config. |
Response Type | Choose 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. |
Prompt | The direct user-input search or chat prompt to which the tool will respond. |
Model | The text model used to generate the response. |
Temperature | Determines 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 Length | The 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 Operator | Determines 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 Threshold | A number, from 0 to 1 , indicating allowable semantic distance. Lower numbers yield more similar results. |
Site | Allows limiting results to one Site. Leave blank to include all Sites. |
Content Types | Allows 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.
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.
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.
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.
Workflow Sub-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:
Parameter | Description |
---|---|
Overwrite tags | Select true or false . If false , it will only write to an empty field. |
Limit the keywords to pre-existing tags | Select true or false . If false , it will add tags to the dotCMS instance's list of tags. |
Update content asynchronously | Takes 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 model | The text model used to generate written responses. |
AI temperature | Determines 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.
Parameter | Description |
---|---|
Results | Specify 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. |
Overwrite | Select true or false . If false , it will only write to an empty field. |
Prompt | The prompt that will be sent to the AI. May include references to the content object via $dotContentMap . |
Update content asynchronously | Takes 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 model | The text model used to generate written responses. |
AI temperature | Determines 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:
Target | Guideline |
---|---|
Contentlet | Index the first Block Editor, WYSIWYG, or TextArea field. |
Page | Render the Page and use the resultant HTML. |
File Asset or dotAsset | Index the first binary field. |
The AI Embeddings actionlet has three parameters:
Parameter | Description |
---|---|
List of fields | A 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 Name | The name of the index appearing in the dotAI Tool to be acted upon. |
Delete or Insert | Depending 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:
Parameter | Description |
---|---|
Image field | Specify to which field, by variable, the image should be saved. If left blank, the process will select the first binary field in the contentlet. |
Overwrite | Select true or false . If false , it will only write to an empty field. |
Prompt | The prompt that will be sent to the AI. May include references to the content object via $dotContentMap . |
Update content asynchronously | Takes 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.
Method | Description |
---|---|
$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.indexCount | Returns 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.
Method | Description |
---|---|
$ai.completions.getConfig() or $ai.completions.config | Returns 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
Path | Method |
---|---|
/api/v1/ai/text/generate | GET |
/api/v1/ai/text/generate | POST |
/api/v1/ai/image/generate | GET |
/api/v1/ai/image/generate | POST |
/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"
}
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
Path | Method |
---|---|
/api/v1/ai/search | GET |
/api/v1/ai/search | POST |
/api/v1/ai/search/related | GET |
/api/v1/ai/search/related | POST |
/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
Path | Method |
---|---|
/api/v1/ai/embeddings | POST |
/api/v1/ai/embeddings | DELETE |
/api/v1/ai/embeddings/db | DELETE |
/api/v1/ai/embeddings/indexCount | GET |
/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
Path | Method |
---|---|
/api/v1/ai/completions | POST |
/api/v1/ai/completions/rawPrompt | POST |
/api/v1/ai/completions/config | GET |
/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"
}