const DOMPurify = require("dompurify");
const marked = require("marked");
window.aiResponseParser = (content) => {
const markedContent = marked.parse(content.trim());
const processedContent = markedContent
.replace("
", "")
.replace("
", "")
.replace("\n\n", "");
const purifiedContent = DOMPurify.sanitize(processedContent, {
ADD_TAGS: ["think"],
});
return purifiedContent;
};
// 支持的模型类型
const API_TYPES = {
OPENAI: "openai",
OLLAMA: "ollama",
UTOOLS: "utools",
};
// 角色提示词
const ROLE_PROMPTS = {
// 翻译
translate: `你是一名翻译专家,请将我给你的内容进行翻译,要求:
1. 无论给的内容长短,请直接翻译,不要进行任何解释
2. 提供中文时,翻译成地道的英文,符合英文的表达习惯
3. 提供英文时,翻译成地道的中文,符合中文的表达习惯
4. 保持原文的专业性和准确性
5. 保持原文的段落格式
6. 不要输出原文
7. 不要使用markdown格式,请直接输出翻译后的内容
`,
// 生成SHELL命令
shell: `你是一名shell命令专家,请根据我的描述生成 shell 命令,要求:
1. 命令应当简洁高效
2. 优先使用常见的命令行工具
3. 确保命令的安全性和可靠性
4. 对于复杂操作,添加注释说明
5. 如果需要多个命令,使用 && 连接或使用脚本格式
6. 直接输出命令,不要输出任何解释,不要使用markdown格式
`,
// 总结
summarize: `你是一名总结专家,请总结我给你的内容的要点,要求:
1. 提取最重要和最有价值的信息
2. 使用简洁的语言
3. 按重要性排序
4. 保持逻辑性和连贯性
5. 请将所有内容放在一个段落内
6. 不要输出原文
7. 不要使用markdown格式
`,
// 润色
polish: `你是一名文字润色专家,请对我给你的内容进行润色,要求:
1. 保持原文的核心意思不变
2. 使用更优美、专业的表达方式
3. 改善语言流畅度和可读性
4. 纠正语法错误和不恰当的表达
5. 保持原文的语言风格(中文/英文)
6. 保持原文的段落格式
7. 直接输出润色后的内容,不要解释修改原因
8. 不要使用markdown格式
`,
// 扩写
expand: `你是一名文字扩写专家,请对我给你的内容进行扩写,要求:
1. 在保持原意的基础上扩充内容
2. 添加相关的细节和例子
3. 使用生动的描述和丰富的表达
4. 确保扩写内容逻辑连贯
5. 适度扩写,避免过度冗长
6. 保持语言风格的一致性
7. 直接输出扩写后的内容,不要解释扩写原因
8. 不要使用markdown格式
`,
};
// API URL 处理
const API_ENDPOINTS = {
[API_TYPES.OPENAI]: {
chat: "/v1/chat/completions",
models: "/v1/models",
},
[API_TYPES.OLLAMA]: {
chat: "/api/chat",
models: "/api/tags",
},
};
// 构建API URL
function buildApiUrl(baseUrl, endpoint) {
if (!baseUrl.endsWith(endpoint)) {
return baseUrl.replace(/\/?$/, endpoint);
}
return baseUrl;
}
// 构建请求配置
function buildRequestConfig(apiConfig) {
const config = {
headers: {
"Content-Type": "application/json",
},
};
if (apiConfig.apiType === API_TYPES.OPENAI && apiConfig.apiToken) {
config.headers["Authorization"] = `Bearer ${apiConfig.apiToken}`;
}
return config;
}
// 构建请求数据
function buildRequestData(content, apiConfig) {
const { model } = apiConfig;
const { prompt, role, context = [] } = content;
const rolePrompt = ROLE_PROMPTS[role] || role;
const roleMessage = rolePrompt
? [
{
role: "system",
content: rolePrompt,
},
]
: [];
// 统一的消息格式处理
const messages = [
// 添加系统角色消息(如果有)
...roleMessage,
// 添加上下文消息
...context.map((msg) => ({
role: msg.role || "user",
content: msg.content,
})),
// 添加当前用户消息
{
role: "user",
content: prompt,
},
];
return {
model,
messages,
stream: true,
};
}
// 处理模型列表响应
function parseModelsResponse(response, apiType) {
if (apiType === API_TYPES.OPENAI) {
if (!response.data.data) {
throw new Error("OpenAI 响应格式错误");
}
return response.data.data.map((model) => model.id);
} else {
if (!response.data.models) {
throw new Error("Ollama 响应格式错误");
}
return response.data.models.map((model) => model.name);
}
}
let reasoning_content_start = false;
function processContentWithReason(response, onStream) {
if (response.reasoning_content) {
if (!reasoning_content_start) {
reasoning_content_start = true;
onStream("", false);
}
onStream(response.reasoning_content, false);
}
if (response.content) {
if (reasoning_content_start) {
reasoning_content_start = false;
onStream("", false);
}
onStream(response.content, false);
}
}
// 处理 OpenAI 流式响应
async function handleOpenAIStreamResponse(line, onStream) {
if (line.startsWith("data:")) {
const jsonStr = line.replace(/^data:[ ]*/, "");
if (jsonStr === "[DONE]") {
onStream("", true);
return;
}
const json = JSON.parse(jsonStr);
const response = json.choices[0]?.delta;
if (response) {
processContentWithReason(response, onStream);
}
}
}
// 处理 Ollama 流式响应
async function handleOllamaStreamResponse(line, onStream) {
const json = JSON.parse(line);
if (json.done) {
onStream("", true);
return;
}
const response = json.message;
if (response) {
processContentWithReason(response, onStream);
}
}
// 处理 uTools AI 流式响应
async function handleUToolsAIStreamResponse(response, onStream) {
processContentWithReason(response, onStream);
}
// 处理流式响应
async function handleStreamResponse(response, apiConfig, onStream) {
// 处理 uTools AI 响应
if (apiConfig.apiType === API_TYPES.UTOOLS) {
try {
await handleUToolsAIStreamResponse(response, onStream);
return { success: true };
} catch (error) {
throw error;
}
}
// 处理其他 API 的流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { value, done } = 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) {
if (line.trim()) {
try {
if (apiConfig.apiType === API_TYPES.OPENAI) {
await handleOpenAIStreamResponse(line, onStream);
} else {
await handleOllamaStreamResponse(line, onStream);
}
} catch (e) {
console.error("解析响应失败:", e);
}
}
}
}
// 处理剩余的缓冲区
if (buffer.trim()) {
try {
if (apiConfig.apiType === API_TYPES.OPENAI) {
await handleOpenAIStreamResponse(buffer, onStream);
} else {
await handleOllamaStreamResponse(buffer, onStream);
}
} catch (e) {
console.error("解析剩余响应失败:", e);
}
}
} catch (error) {
if (error.name === "AbortError") {
return {
success: false,
error: "请求已取消",
cancelled: true,
};
}
throw error;
} finally {
reader.releaseLock();
}
return { success: true };
}
/**
* AI对话功能
* @param {Object} content - 对话内容参数
* @param {Object} apiConfig - API配置参数
* @param {Object} options - 其他选项
* @returns {Promise