集成自定义 LLM
如果你想使用自己的后端提供对自定义 LLM 的访问,可以在扩展配置中重写下面定义的解析函数。
确保你返回了正确类型的响应,并且正确处理了错误。
注意!
我们强烈建议不要在前端直接调用 OpenAI,因为这可能导致 API 令牌泄露!你应该通过后端代理来保护你的 API 令牌安全。
访问私有注册表
高级 AI 扩展发布在 Tiptap 的私有 npm 注册表中。请按照私有注册表指南集成该扩展。如果你已经验证了你的 Tiptap 账号,可以直接前往#安装。
安装
为了使用自定义解析函数,你需要安装我们 Tiptap AI 扩展的高级版本。
npm install @tiptap-pro/extension-ai同时使用自定义 LLM 和 Tiptap AI 云服务
如果你想在某些情况下依赖我们的云服务,确保你已按照此处说明设置了你的团队。
解析函数
你可以在扩展选项中定义自定义解析函数。请注意它们预期的返回类型如下:
| 类型 | 方法名 | 返回类型 |
|---|---|---|
| completion | aiCompletionResolver | Promise<string | null> |
| streaming | aiStreamResolver | Promise<ReadableStream<Uint8Array> | null> |
| image | aiImageResolver | Promise<string | null> |
使用 aiCompletionResolver 以非流式方式向编辑器添加文本。
使用 aiStreamResolver 直接将内容流式传输到编辑器,实现打字机效果。
确保流返回 HTML,以便 Tiptap 直接将内容渲染为富文本。此方式免去了 Markdown 解析器的需求,使前端保持轻量。
示例
在完成模式中重写特定命令的解析
本例中我们希望在调用 rephrase 动作/命令时,调用自定义后端。
其他所有情况均由 Tiptap Cloud 默认后端处理。
// ...
import Ai from '@tiptap-pro/extension-ai-advanced'
// ...
Ai.configure({
appId: 'APP_ID_HERE',
token: 'TOKEN_HERE',
// ...
onError(error, context) {
// 处理错误
},
// 定义完成解析函数(注意:流式和图像需单独定义!)
aiCompletionResolver: async ({
editor,
action,
text,
textOptions,
extensionOptions,
defaultResolver,
}) => {
// 根据 action、text 或其他条件判断
// 决定是否使用自定义接口
if (action === 'rephrase') {
const response = await fetch('https://dummyjson.com/quotes/random')
const json = await response.json()
if (!response.ok) {
throw new Error(`${response.status} ${json?.message}`)
}
return json?.quote
}
// 其他情况则转发至 Tiptap AI 服务
return defaultResolver({
editor,
action,
text,
textOptions,
extensionOptions,
defaultResolver,
})
},
})注册新的 AI 命令并调用自定义后端动作
本例中,我们注册了一个名为 aiCustomTextCommand 的编辑器命令,使用 Tiptap 的 runAiTextCommand 函数完成其余工作,并添加自定义命令的解析以调用自定义后端(完成模式)。
// …
import { Ai, runAiTextCommand } from '@tiptap-pro/extension-ai-advanced'
// …
// 如果使用 TypeScript,请声明类型:
//
// declare module '@tiptap/core' {
// interface Commands<ReturnType> {
// ai: {
// aiCustomTextCommand: () => ReturnType,
// }
// }
// }
const AiExtended = Ai.extend({
addCommands() {
return {
...this.parent?.(),
aiCustomTextCommand:
(options = {}) =>
(props) => {
// 你可以自行实现逻辑,比如获取所选文字并传递给特定命令解析
return runAiTextCommand(props, 'customCommand', options)
},
}
},
})
// … 这里初始化你的 Tiptap 编辑器实例并注册扩展
const editor = new Editor({
extensions: [
/* … 添加其他扩展 */
AiExtended.configure({
/* … 添加配置(appId、token 等) */
onError(error, context) {
// 处理错误
},
aiCompletionResolver: async ({
action,
text,
textOptions,
extensionOptions,
defaultResolver,
editor,
}) => {
if (action === 'customCommand') {
const response = await fetch('https://dummyjson.com/quotes/random')
const json = await response.json()
if (!response.ok) {
throw new Error(`${response.status} ${json?.message}`)
}
return json?.quote
}
return defaultResolver({
editor,
action,
text,
textOptions,
extensionOptions,
defaultResolver,
})
},
}),
],
content: '',
})
// … 使用以下方式运行新命令:
// editor.chain().focus().aiCustomTextCommand().run()在流模式下使用你的自定义后端
本例完全依赖自定义后端。
确保 aiStreamResolver 函数返回 ReadableStream<Uint8Array>。
请注意:如果你同时想使用流模式和传统的完成模式(非流模式),也需定义 aiCompletionResolver!
// ...
import Ai from '@tiptap-pro/extension-ai-advanced'
// ...
Ai.configure({
appId: 'APP_ID_HERE',
token: 'TOKEN_HERE',
// ...
onError(error, context) {
// 处理错误
},
// 定义流解析函数
aiStreamResolver: async ({ action, text, textOptions }) => {
const fetchOptions = {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
...textOptions,
text,
}),
}
const response = await fetch(`<YOUR_STREAMED_BACKEND_ENDPOINT>`, fetchOptions)
if (!response.ok) {
const json = await response.json()
throw new Error(`${json?.error} ${json?.message}`)
}
return response.body
},
})