Store and regenerate responses
The AI extension stores the current state in it’s extension storage under editor.storage.ai || editor.storage.aiAdvanced
(depending on if you are using the extension-ai or extension-ai-advanced extension). This storage is used to keep track of the current state of the AI response, as well as any past responses.
key | type | definition |
---|---|---|
state | 'loading' | 'error' | 'idle' | While the AI is generating a response, the state is set to loading . After the response is generated, the state is set to idle . When there is an error, the state is set to error |
response | string | undefined | The most recent message generated by the AI. When idle , if this is a string, it is the previous generated message, if undefined , no message has been generated. When loading , this will be a string of what the AI has generated so far (if streaming the response). When error , this is undefined |
error | Error | undefined | The error generated, only ever set in the error state |
generatedWith | { action: TextAction; options: TextOptions; range: undefined | Range; } | undefined | The options that describe what the last generated response was generated with range is only ever set if inserting the content into the editor |
pastResponses | string[] | Stores previously generated responses (on success), most recent first. Cleared when the response is accepted/rejected. |
You can use this storage to read out the current state of AI responses like:
const aiStorage = editor.storage.ai || editor.storage.aiAdvanced
if (aiStorage.response.state === 'error') {
// The error that occurred
aiStorage.response.error
}
if (aiStorage.response.state === 'loading') {
// The message that is currently being processed
aiStorage.response.message
}
if (aiStorage.response.state === 'idle') {
if (aiStorage.response.message) {
// The successful response
aiStorage.response.message
} else {
// No response has been requested yet
}
}
Using AI Storage
Want to leverage the Tiptap Content AI's ability to generate results but, not have the results available outside of the editor? You can use insert: false
on any AI TextOption and it will store the result into the extension.
const chatMessage = 'Hello, how are you?'
editor
.chain()
.aiTextPrompt({
text: chatMessage,
stream: true,
insert: false,
format: 'rich-text',
})
.run()
From there, you can use the aiAccept
, aiReject
, and aiRegenerate
commands
aiAccept
This command is meant to be ran when the user has accepted the AI response, it will insert the response into the editor by default and it’s behavior changes depending on the provided options.
key | type | definition |
---|---|---|
insertAt | number | { from: number, to: number } | When a number , accept the response and insert it into the start of the editor. When { from: number, to: number } , accept the response and replace the content from position from to position to with the AI response |
append | boolean | If true , instead of replacing the current selection, append to it |
The default behavior with no provided options is to, accept the response and insert it into the editor, replacing the current selection
// Accept the response and insert it into the editor
editor.chain().aiAccept().run()
// Accept the response and insert it into the editor at the start
editor.chain().aiAccept({ insertAt: 0 }).run()
// Accept the response and insert it into the editor at the end
editor.chain().aiAccept({ insertAt: editor.state.doc.content.size }).run()
// Accept the response and append it to the current selection
editor.chain().aiAccept({ append: true }).run()
aiRegenerate
This command is meant to be ran when the user wants the to retry the AI response, it will use all the same options as the previous AI text operation and add to the (editor.storage.ai || editor.storage.aiAdvanced).pastResponses
array
key | type | definition |
---|---|---|
insert | boolean | Whether to insert the regenerated response into the editor |
insertAt | number | { from: number, to: number } | If not specified,the regenerated response will be inserted where the previous response was. When a number , regenerate the response and insert it into the start of the editor. When { from: number, to: number } , regenerate the response and replace the content from position from to position to with the AI response |
The default behavior with no provided options is to, regenerate the response and insert it into the editor, replacing the current selection
// Regenerate the response and insert it into the editor
editor.chain().aiRegenerate().run()
// Regenerate the response and insert it into the editor at the start
editor.chain().aiRegenerate({ insertAt: 0 }).run()
// Regenerate the response and insert it into the editor at the end
editor.chain().aiRegenerate({ insertAt: editor.state.doc.content.size }).run()
// Regenerate the response and append it to the current selection
editor.chain().aiRegenerate({ append: true }).run()
aiReject
This command is meant to be ran when the user has rejected the AI response, it will reset the extension’s state to the initial idle state and clear all (editor.storage.ai || editor.storage.aiAdvanced).pastResponses
key | type | definition |
---|---|---|
type | 'reset' | 'pause' | Whether to reset the AI to the idle state. Or just pause the current response. Default is 'reset' |
editor.chain().aiReject().run()
// Will not clear out editor.storage.ai || editor.storage.aiAdvanced, useful for keeping current response in the editor storage
editor.chain().aiReject({ type: 'pause' }).run()
Advanced Example
One use-case of extension storage could be to render a preview of the AI generated content.
To render a preview of what a chat would look like in your editor, we can use your editor’s schema to generate the html that would be generated. With this HTML you can display a preview of that content in an element
// Display the response as HTML
import { tryParseToTiptapHTML } from '@tiptap-pro/extension-ai'
// try to parse the current message as HTML, and null if it could not be parsed
tryToParseToHTML((editor.storage.ai || editor.storage.aiAdvanced).response.message, editor)
// try to parse a previous response as HTML, and null if it could not be parsed
tryToParseToHTML((editor.storage.ai || editor.storage.aiAdvanced).pastResponses[0], editor)
// For example in React
function PreviewComponent({ editor }) {
const htmlResponse = tryToParseToHTML(
(editor.storage.ai || editor.storage.aiAdvanced).response.message,
editor,
)
/* This is safe since we've parsed the content with prose-mirror first */
return <div dangerouslySetInnerHTML={{ __html: htmlResponse }}></div>
}
See our demo below for a full example of how a chat preview could work.