存储和重新生成响应
AI Generation 扩展会将当前状态存储在其扩展存储中,位置为 editor.storage.ai || editor.storage.aiAdvanced(取决于你是否使用 extension-ai 或 extension-ai-advanced 扩展)。该存储用于追踪 AI 响应的当前状态以及所有历史响应。
| key | 类型 | 定义 |
|---|---|---|
| state | 'loading' | 'error' | 'idle' | 当 AI 正在生成响应时,状态为 loading。响应生成结束后,状态为 idle。发生错误时状态为 error。 |
| response | string | undefined | AI 最近生成的消息。当状态为 idle 并且是字符串时,表示之前生成的消息;若为 undefined,表示尚未生成任何消息。状态为 loading 时,该字符串包含 AI 当前已生成的内容(流式响应场景)。状态为 error 时为 undefined。 |
| error | Error | undefined | 当前错误,仅在错误状态时设置。 |
| generatedWith | { action: TextAction; options: TextOptions; range: undefined | Range; } | undefined | 表示上一次生成响应时使用的选项对象,只有在将内容插入编辑器时会设置 range。 |
| pastResponses | string[] | 存储之前生成的响应(成功时),以最新的为先。接受或拒绝响应时此列表会被清空。 |
你可以使用此存储读取 AI 响应的当前状态,如下示例所示:
const aiStorage = editor.storage.ai || editor.storage.aiAdvanced
if (aiStorage.response.state === 'error') {
// 出现的错误
aiStorage.response.error
}
if (aiStorage.response.state === 'loading') {
// 当前正在处理的消息
aiStorage.response.message
}
if (aiStorage.response.state === 'idle') {
if (aiStorage.response.message) {
// 成功的响应
aiStorage.response.message
} else {
// 尚未请求响应
}
}使用 AI 存储
想利用 Tiptap 内容 AI 生成结果,但不想让结果直接显示在编辑器中吗?你可以在任何 AI TextOption 中使用 insert: false,这样结果会存储到扩展中,而不是插入编辑器。
const chatMessage = 'Hello, how are you?'
editor
.chain()
.aiTextPrompt({
text: chatMessage,
stream: true,
insert: false,
format: 'rich-text',
})
.run()之后,你可以使用 aiAccept、aiReject 和 aiRegenerate 命令。
aiAccept
该命令在用户接受 AI 响应时执行,默认行为是将响应插入编辑器。行为可根据传入的选项调整。
| key | 类型 | 定义 |
|---|---|---|
| insertAt | number | { from: number, to: number } | 如果是数字,接受响应并插入到编辑器指定索引位置;如果是 { from: number, to: number },则接受响应并替换编辑器中该范围的内容。 |
| append | boolean | 若为 true,则不替换当前选区,而是追加在当前选区后面。 |
默认行为(无选项传入)是接受响应并替换当前选区内容插入编辑器。
// 接受响应并插入编辑器
editor.chain().aiAccept().run()
// 接受响应并插入到编辑器开头
editor.chain().aiAccept({ insertAt: 0 }).run()
// 接受响应并插入到编辑器末尾
editor.chain().aiAccept({ insertAt: editor.state.doc.content.size }).run()
// 接受响应并追加到当前选区后面
editor.chain().aiAccept({ append: true }).run()aiRegenerate
该命令在用户想要重新生成 AI 响应时执行,会使用上一次 AI 文本操作的所有相同选项,并将新的响应添加到 (editor.storage.ai || editor.storage.aiAdvanced).pastResponses 数组中。
| key | 类型 | 定义 |
|---|---|---|
| insert | boolean | 是否将重新生成的响应插入编辑器。 |
| insertAt | number | { from: number, to: number } | 如果未指定,重新生成的响应会插入到上一次响应的位置。若为数字,重新生成的响应将插入到编辑器开头;若为 { from: number, to: number },则重新生成响应替换编辑器中该范围内容。 |
默认行为(无选项传入)是重新生成响应并替换当前选区内容插入编辑器。
// 重新生成响应并插入编辑器
editor.chain().aiRegenerate().run()
// 重新生成响应并插入到编辑器开头
editor.chain().aiRegenerate({ insertAt: 0 }).run()
// 重新生成响应并插入到编辑器末尾
editor.chain().aiRegenerate({ insertAt: editor.state.doc.content.size }).run()
// 重新生成响应并追加到当前选区后面
editor.chain().aiRegenerate({ append: true }).run()aiReject
该命令在用户拒绝 AI 响应时执行,会将扩展的状态重置为初始的 idle 状态,并清空 (editor.storage.ai || editor.storage.aiAdvanced).pastResponses。
| key | 类型 | 定义 |
|---|---|---|
| type | 'reset' | 'pause' | 是重置 AI 到 idle 状态,还是仅暂停当前响应。默认值为 'reset'。 |
editor.chain().aiReject().run()
// 不会清空 editor.storage.ai || editor.storage.aiAdvanced,适合保留当前响应在编辑器存储中
editor.chain().aiReject({ type: 'pause' }).run()高级示例
扩展存储的一个应用场景是渲染 AI 生成内容的预览。
为了在编辑器中渲染聊天内容的预览,我们可以使用编辑器的 schema 生成对应的 HTML。这段 HTML 可用于某个元素内展示该内容的预览。
// 以 HTML 形式显示响应
import { tryParseToTiptapHTML } from '@tiptap-pro/extension-ai'
// 尝试将当前消息解析为 HTML,无法解析时返回 null
tryToParseToHTML((editor.storage.ai || editor.storage.aiAdvanced).response.message, editor)
// 尝试将过去的响应解析为 HTML,无法解析时返回 null
tryToParseToHTML((editor.storage.ai || editor.storage.aiAdvanced).pastResponses[0], editor)
// 例如在 React 中
function PreviewComponent({ editor }) {
const htmlResponse = tryToParseToHTML(
(editor.storage.ai || editor.storage.aiAdvanced).response.message,
editor,
)
/* 由于先用 prose-mirror 解析,风险较小 */
return <div dangerouslySetInnerHTML={{ __html: htmlResponse }}></div>
}请参阅下面的演示,了解聊天预览的完整示例。