AI 能力
AI 能力概览
了解 SVeditor 的 AI 文本处理、流式自动补全、校对建议及修改审阅能力。
AI 能力概览
SVeditor 提供三个 AI 扩展模块:
| 扩展 | 说明 |
|---|---|
ai-basic | 文本处理(缩写/扩写/语法修复/翻译)、流式内容、自动补全 |
ai-suggestion | 校对与写作建议 |
ai-changes | AI 修改差异对比与审阅(接受/拒绝) |
配置方式
AI 扩展采用回调函数配置模式——你提供请求处理函数,SVeditor 负责 UI 渲染、流式传输和编辑器集成。
SVeditor.create({
el: '#editor',
extensionsOptions: {
ai: {
enabled: true,
aiConfig: {
/**
* 处理流式 AI 请求。
* 返回纯文本块的 ReadableStream<Uint8Array>。
*/
onStreamRequest: async (options) => {
const res = await fetch('/api/ai/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: options.action,
text: options.text,
}),
signal: options.aborter?.signal,
});
return res.body;
},
/**
* 处理非流式 AI 请求。
* 返回完整响应字符串。
*/
onCompletionRequest: async (options) => {
const res = await fetch('/api/ai/completion', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: options.action,
text: options.text,
}),
signal: options.aborter?.signal,
});
const data = await res.json();
return data.content;
},
},
// 生命周期回调
onLoading: (context) => {
console.log(`AI 处理中: ${context.action}`);
},
onSuccess: (context) => {
console.log(`AI 完成: ${context.action}`);
},
onError: (error, context) => {
console.error(`AI 失败: ${context.action}`, error);
},
},
},
});AiTextResolverOptions 参数
onStreamRequest 和 onCompletionRequest 均接收一个 options 对象:
interface AiTextResolverOptions {
/** Tiptap 编辑器实例 */
editor: Editor;
/**
* AI 操作类型。
* 内置值:'shorten' | 'extend' | 'fix-grammar' | 'simplify' |
* 'complete' | 'improve-writing' | 'translate'
*/
action: string;
/** 要处理的文本内容 */
text: string;
/** 文本格式选项 */
textOptions: {
format?: 'plain-text' | 'rich-text';
};
/** 扩展配置 */
extensionOptions: AiOptions;
/** 用于取消请求的 AbortController */
aborter?: AbortController;
}支持的操作类型
| 操作 | 说明 |
|---|---|
shorten | 缩短文本,保留核心含义 |
extend | 扩展文本,增加更多细节 |
fix-grammar | 修正语法、拼写和标点 |
simplify | 简化语言,提升可读性 |
complete | 自然续写补全文本 |
improve-writing | 改善表达清晰度和行文流畅度 |
translate | 中译英或英译中 |
Playground 示例
自动保存演示 展示 AI 的分层接入 — 不仅是工具栏按钮。将 extensionsOptions.ai 指向你的补全 API,可在同一编辑器中组合 ai、aiSuggestion、aiChanges、自动保存、上传与导出。
const suggestionRules = [
{
id: 'grammar',
title: 'Grammar',
prompt:
'Fix grammar, spelling, and punctuation errors while preserving the original meaning. Return only the revised text.',
color: '#dc2626',
backgroundColor: 'rgba(220, 38, 38, 0.10)',
displayAsDiff: true,
},
{
id: 'style',
title: 'Style',
prompt:
'Improve clarity and flow for a product-facing audience. Return only the revised text.',
color: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.12)',
displayAsDiff: true,
},
];
SVeditor.create({
el: '#editor',
extensionsOptions: {
autoSave: {
onFetch: () => fetchDoc(docId),
onSaveContent: (content) => saveDoc(docId, { content }),
debounceMs: 900,
},
meta: {
onFetchMeta: () => fetchMeta(docId),
onSaveMeta: (meta) => saveDoc(docId, { meta }),
},
versionHistory: {
enabled: true,
autoSnapshot: {
enabled: true,
debounceMs: 2600,
},
},
image: {
proxyUrl: '',
onUpload: uploadImage,
},
attachment: {
proxyUrl: '',
onUpload: uploadAttachment,
},
wechatCopy: {
enabled: true,
},
ai: {
enabled: true,
aiConfig: {
apiUrl: '/api/sdk-demo/api/ai',
},
},
aiChanges: {
enabled: true,
},
aiSuggestion: {
enabled: true,
rules: suggestionRules,
loadOnStart: true,
reloadOnUpdate: true,
debounceTimeout: 2200,
resolver: resolveSdkDemoSuggestions,
},
},
});完整实现示例
步骤一:创建请求处理函数
// src/utils/ai.ts
import type { AiTextResolverOptions } from '@wztlink1013/sveditor';
const SYSTEM_PROMPTS: Record<string, string> = {
shorten: '请缩短以下文本,保留核心含义。只返回缩短后的文本。',
extend: '请扩展以下文本,添加更多细节和上下文。只返回扩展后的文本。',
'fix-grammar': '请修正以下文本中的所有语法、拼写和标点错误。只返回修正后的文本。',
simplify: '请简化以下文本,使其更易理解。只返回简化后的文本。',
complete: '请自然地续写以下文本。只返回续写的内容。',
'improve-writing': '请改善以下文本的写作风格、表达清晰度和行文流畅度。只返回改进后的文本。',
translate: '如果是中文,请翻译成英文;如果是英文,请翻译成中文。只返回翻译结果。',
};
function parseSSEStream(body: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {
const reader = body.getReader();
const decoder = new TextDecoder();
const encoder = new TextEncoder();
return new ReadableStream({
async start(controller) {
let buffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed === 'data: [DONE]') continue;
if (trimmed.startsWith('data: ')) {
try {
const data = JSON.parse(trimmed.slice(6));
const content = data?.choices?.[0]?.delta?.content;
if (content) controller.enqueue(encoder.encode(content));
} catch {
// 跳过格式错误的 SSE 事件
}
}
}
}
controller.close();
} catch (error) {
controller.error(error);
}
},
});
}
export const aiConfig = {
onStreamRequest: async (options: AiTextResolverOptions) => {
const { action, text, aborter } = options;
const systemPrompt = SYSTEM_PROMPTS[action] ?? '处理以下文本。';
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: text },
],
stream: true,
}),
signal: aborter?.signal,
});
if (!res.ok) throw new Error(`AI 请求失败: ${res.status}`);
if (!res.body) throw new Error('无响应体');
return parseSSEStream(res.body);
},
onCompletionRequest: async (options: AiTextResolverOptions) => {
const { action, text, aborter } = options;
const systemPrompt = SYSTEM_PROMPTS[action] ?? '处理以下文本。';
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: text },
],
stream: false,
}),
signal: aborter?.signal,
});
if (!res.ok) throw new Error(`AI 请求失败: ${res.status}`);
const data = await res.json();
return data?.choices?.[0]?.message?.content ?? null;
},
};步骤二:配置编辑器
import { SVeditor } from '@wztlink1013/sveditor';
import { aiConfig } from './utils/ai';
SVeditor.create({
el: '#editor',
extensionsOptions: {
ai: {
enabled: true,
aiConfig,
},
},
});兼容的 AI 服务
OpenAI
const AI_CONFIG = {
apiKey: 'sk-...',
baseURL: 'https://api.openai.com/v1',
model: 'gpt-4',
};Azure OpenAI
const AI_CONFIG = {
apiKey: 'your-azure-key',
baseURL: 'https://your-resource.openai.azure.com/openai/deployments/your-deployment',
model: 'gpt-4',
};阿里云通义千问
const AI_CONFIG = {
apiKey: 'sk-...',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model: 'qwen-max',
};其他 OpenAI 兼容 API
const AI_CONFIG = {
apiKey: 'your-key',
baseURL: 'https://your-service.com/v1',
model: 'your-model',
};注意事项
-
回调必须通过抛出异常来处理错误 — SDK 捕获抛出的错误并路由到
onError。HTTP 错误时不要返回null,而应throw。 -
流式响应需要纯文本块格式 —
onStreamRequest必须返回纯文本块的ReadableStream<Uint8Array>(不是 SSE 格式)。如果你的 API 返回 SSE,请使用上面的parseSSEStream辅助函数。 -
请求可以被取消 — 务必将
options.aborter?.signal传给fetch。SDK 在用户关闭 AI 面板或触发新操作时会取消进行中的请求。 -
onCompletionRequest是可选的 — 未提供时,所有 AI 操作均使用onStreamRequest。