UNPKG

koishi-plugin-davinci-003

Version:
997 lines (990 loc) 39.1 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/locales/zh.yml var require_zh = __commonJS({ "src/locales/zh.yml"(exports, module2) { module2.exports = { commands: { dvc: { description: "AI聊天", usage: '<>\n <message forward>\n <message id=1>项目地址https://github.com/initialencounter/2022-12-24/blob/main/davinci-003#readme.md </message>\n <message id=2>对于部署者行为及所产生的任何纠纷, Koishi 及 koishi-plugin-davinci-003 概不负责</message>\n <message id=3>指令如下:</message>\n <message id=4>1.[重置会话] 请发送 "dvc.重置会话"</message>\n <message id=5>2.[添加人格] 请发送 "dvc.添加人格"</message>\n <message id=6>3.[清空所有回话] 请发送 "dvc.clear"</message>\n <message id=7>4.[切换人格] 请发送 "dvc.切换人格"</message>\n <message id=8>5.[查询余额] 请发送 "dvc.credit"</message>\n <message id=9>8.若出现400报错,请发送dvc.clear</message>\n </message>\n</>', messages: { censor: "不合规,请检查输入内容是否含有违禁词。", "no-prompt": "你好!很高兴认识你!", thinking: "思考中……", err: "寄!{0}", total_available: "滴!当前账户的余额为${0}。", tooLong: "滋滋..猪脑过载。", switch: "当前存在{0}{1}请输入编号。", "switch-output": "当前存在模式{0}请输入编号。", "switch-err": "不存在人格可切换。", get: "查询中...", clean: "舒服了", "set-personality": "设置失败,请重新触发指令,示例:dvc.添加人格", "set-personality-role": "role只能为system、assistant或user", painting: "GPT生成图片中...", "menu-err": "请输入正确的序号。", "switch-success": "切换{0}成功:{1}", block: "您已被block", louder: "听不清", "usage-exhausted": "调用次数已达上限。" } }, "dvc.count": { description: "减少dvc的使用次数" }, "dvc.clear": { description: "清空所有人的会话" }, "dvc.重置会话": { description: "清空个人的会话" }, "dvc.credit": { description: "查看余额" }, "dvc.output": { description: "切换输出模式" }, "dvc.设置人格": { description: "设置人格, AI的人格会发生改变" }, "dvc.添加人格": { description: "添加人格,会自动保存" }, "dvc.切换人格": { description: "切换现有的人格" }, "dvc.切换引擎": { description: "切换引擎" }, "dvc.生图": { description: "GPT生成图片" }, "dvc.翻译": { description: "AI翻译" } } }; } }); // src/index.tsx var src_exports = {}; __export(src_exports, { default: () => src_default }); module.exports = __toCommonJS(src_exports); var import_koishi3 = require("koishi"); var import_fs = __toESM(require("fs")); var import_koishi_plugin_rate_limit = require("koishi-plugin-rate-limit"); var import_path = require("path"); // src/utils.ts var import_koishi = require("koishi"); async function recall(session, messageId, time) { new Promise((resolve2) => setTimeout(() => { session.bot.deleteMessage(session.channelId, messageId); }, time)); } __name(recall, "recall"); async function switch_menu(session, type_arr, name2) { let type_str = "\n" + name2 + "\n"; let count = 0; const result = (0, import_koishi.segment)("figure"); type_arr.forEach((i, id) => { if (count > 50) { count = 0; result.children.push( (0, import_koishi.segment)("message", { userId: "1114039391", nickname: "AI" }, type_str) ); type_str = ""; } type_str += String(id + 1) + " " + i + "\n"; count++; }); result.children.push( (0, import_koishi.segment)("message", { userId: "1114039391", nickname: "AI" }, type_str) ); await session.send(result); const input = await session.prompt(); if (!input || Number.isNaN(+input)) return ""; const index = parseInt(input) - 1; if (index < 0 || index > type_arr.length - 1) return ""; return type_arr[index]; } __name(switch_menu, "switch_menu"); async function switch_menu_grid(session, type_arr, name2) { let type_str = "\n" + name2 + "\n\n"; let count = 0; function getActualLength(str) { let actualLength = 0; for (let i2 = 0; i2 < str.length; i2++) { const charCode = str.charCodeAt(i2); if (charCode >= 65281 && charCode <= 65374 || charCode >= 12288 && charCode <= 12351) { actualLength += 2; } else { actualLength += 1; } } return actualLength; } __name(getActualLength, "getActualLength"); function multiplyStrings(str, n) { return Array.from({ length: n }, () => str).join(""); } __name(multiplyStrings, "multiplyStrings"); const result = (0, import_koishi.segment)("figure"); for (let id = 0; id < type_arr.length; id += 2) { if (count > 50) { count = 0; result.children.push( (0, import_koishi.segment)("message", { userId: "1114039391", nickname: "AI" }, type_str) ); type_str = ""; } const firstLen = getActualLength(String(id + 1) + " " + type_arr[id]); const spaceString = multiplyStrings(" ", 20 - firstLen); type_str += String(id + 1) + " " + type_arr[id] + spaceString + String(id + 2) + " " + type_arr[id + 1] + "\n"; count++; } result.children.push( (0, import_koishi.segment)("message", { userId: "1114039391", nickname: "AI" }, type_str) ); await session.send(result); let input = await session.prompt(); input = input.trim(); let res = []; for (var i of input.split(" ")) { if (!i || Number.isNaN(+i)) continue; const index = parseInt(i) - 1; if (index < 0 || index > type_arr.length - 1) continue; res.push(type_arr[index]); } return res; } __name(switch_menu_grid, "switch_menu_grid"); // src/type.ts var import_koishi2 = require("koishi"); var Dvc = class extends import_koishi2.Service { static { __name(this, "Dvc"); } static inject = { required: ["console", "database"], optional: ["puppeteer", "vits", "sst", "censor"] }; output_type; session_config; sessions; personality; sessions_cmd; aliasMap; type; l6k; key_number; maxRetryTimes; constructor(ctx, config) { super(ctx, "dvc", true); } }; ((Dvc2) => { Dvc2.Config = import_koishi2.Schema.intersect([ import_koishi2.Schema.union([ import_koishi2.Schema.object({ selectBaseURL: import_koishi2.Schema.const(true), baseURL: import_koishi2.Schema.union([ import_koishi2.Schema.const("https://api.openai.com").description("https://api.openai.com"), import_koishi2.Schema.const("https://api.deepseek.com").description("https://api.deepseek.com"), import_koishi2.Schema.const("https://api.chatanywhere.com.cn").description("https://api.chatanywhere.com.cn"), import_koishi2.Schema.const("https://maas-api.cn-huabei-1.xf-yun.com").description("(讯飞)https://maas-api.cn-huabei-1.xf-yun.com"), import_koishi2.Schema.transform(String, (value) => value) ]).default("https://api.openai.com").description("选择平台") }).description("选择平台"), import_koishi2.Schema.object({ selectBaseURL: import_koishi2.Schema.const(false), baseURL: import_koishi2.Schema.string().default("https://api.openai.com").description("自定义平台") }).description("自定义平台") ]), import_koishi2.Schema.union([ import_koishi2.Schema.object({ selectModel: import_koishi2.Schema.const(true), appointModel: import_koishi2.Schema.union([ import_koishi2.Schema.const("gpt-4o").description("gpt-4o"), import_koishi2.Schema.const("gpt-4o-mini").description("gpt-4o-mini"), import_koishi2.Schema.const("o1").description("o1"), import_koishi2.Schema.const("o3-mini").description("o3-mini"), import_koishi2.Schema.const("deepseek-chat").description("deepseek-chat"), import_koishi2.Schema.const("deepseek-reasoner").description("deepseek-reasoner"), import_koishi2.Schema.const("xdeepseekr1").description("xdeepseekr1(讯飞 deepseekr1)"), import_koishi2.Schema.transform(String, (value) => value) ]).default("gpt-4o-mini").description("[选择模型](https://openai.com/api/pricing/)") }).description("选择模型"), import_koishi2.Schema.object({ selectModel: import_koishi2.Schema.const(false), appointModel: import_koishi2.Schema.string().default("gpt-4o-mini").description("自定义模型") }).description("自定义模型") ]), import_koishi2.Schema.object({ selectBaseURL: import_koishi2.Schema.boolean().default(true).description("选择平台"), selectModel: import_koishi2.Schema.boolean().default(true).description("选择模型"), key: import_koishi2.Schema.union([ import_koishi2.Schema.array(String).role("secret"), import_koishi2.Schema.transform(String, (value) => [value]) ]).default([]).role("secret").description("api_key"), enableContext: import_koishi2.Schema.boolean().default(true).description("是否启用上下文, 关闭后将减少 token 消耗") }).description("基础设置"), import_koishi2.Schema.object({ onlyOnePersonality: import_koishi2.Schema.boolean().default(false).description("所有人共用一个人设,开启后将无法切换人格、删除人格、添加人格"), onlyOneContext: import_koishi2.Schema.boolean().default(false).description("所有人共用一个上下文"), whisper: import_koishi2.Schema.boolean().default(false).description("语音回复,开启后 AI 将回复你的语音消息"), waiting: import_koishi2.Schema.boolean().default(true).description("消息反馈,开启后会发送 `思考中...`"), nickwake: import_koishi2.Schema.boolean().default(false).description("当聊天中出现 AI 的人格名称,AI 将回复你的消息"), recall: import_koishi2.Schema.boolean().default(true).description("一段时间后会撤回“思考中”"), recall_time: import_koishi2.Schema.number().default(5e3).description("撤回的时间"), lang: import_koishi2.Schema.string().description("要翻译的目标语言").default("英文"), enableReasoningContent: import_koishi2.Schema.boolean().default(false).description("是否输出思维链内容"), max_tokens: import_koishi2.Schema.number().description("请求长度,否则报错").default(3e3), temperature: import_koishi2.Schema.number().role("slider").min(0.01).max(1).step(0.01).default(0.01).description("温度"), authority: import_koishi2.Schema.number().role("slider").min(0).max(5).step(1).description("允许使用的最低权限").default(1), superuser: import_koishi2.Schema.array(String).default(["3118087750"]).description("可以无限调用的用户"), usage: import_koishi2.Schema.number().description("每人每日可用次数").default(100), alias: import_koishi2.Schema.array(String).default(["ai"]).description("指令别名"), resolution: import_koishi2.Schema.string().default("1024x1024").description("生成图像的默认比例"), output: import_koishi2.Schema.union([ import_koishi2.Schema.const("minimal").description("只发送文字消息"), import_koishi2.Schema.const("quote").description("引用消息"), import_koishi2.Schema.const("figure").description("以聊天记录形式发送"), import_koishi2.Schema.const("image").description("将对话转成图片"), import_koishi2.Schema.const("voice").description("发送语音") ]).description("输出方式。").default("minimal"), private: import_koishi2.Schema.boolean().default(true).description("开启后私聊AI可触发对话, 不需要使用指令"), mention: import_koishi2.Schema.boolean().default(true).description("开启后机器人被提及(at/引用)可触发对话"), randnum: import_koishi2.Schema.number().role("slider").min(0).max(1).step(0.01).default(0).description("随机触发对话的概率,如需关闭可设置为 0"), maxRetryTimes: import_koishi2.Schema.number().default(30).description("报错后最大重试次数") }).description("进阶设置"), import_koishi2.Schema.object({ blockuser: import_koishi2.Schema.array(String).default([]).description("屏蔽的用户"), blockchannel: import_koishi2.Schema.array(String).default([]).description("屏蔽的频道") }).description("过滤器") ]); })(Dvc || (Dvc = {})); // src/index.tsx var name = "davinci-003"; var logger = new import_koishi3.Logger(name); var version = require("../package.json")["version"]; var localUsage = (0, import_fs.readFileSync)((0, import_path.resolve)(__dirname, "../readme.md")).toString("utf-8").split("更新日志")[0]; var DVc = class extends Dvc { static { __name(this, "DVc"); } pluginConfig; constructor(ctx, config) { super(ctx, config); this.output_type = config.output; this.key_number = 0; this.sessions = {}; this.maxRetryTimes = config.maxRetryTimes; this.pluginConfig = config; ctx.i18n.define("zh", require_zh()); ctx.on("ready", () => { if (!ctx.puppeteer && config.output == "image") logger.warn("未启用 pptr,将无法发送图片消息"); if (!ctx.vits && config.output == "voice") logger.warn("未启用 vits,将无法输出语音"); }); ctx.inject(["console"], (ctx2) => { ctx2.console.addEntry({ dev: (0, import_path.resolve)(__dirname, "../client/index.ts"), prod: (0, import_path.resolve)(__dirname, "../dist") }); }); try { this.personality = JSON.parse( import_fs.default.readFileSync("./personality.json", "utf-8") ); } catch (e) { this.personality = { 预设人格: [{ role: "system", content: "你是我的全能AI助理" }] }; import_fs.default.writeFileSync( "./personality.json", JSON.stringify(this.personality, null, 2) ); } this.session_config = Object.values(this.personality)[0]; this.sessions_cmd = Object.keys(this.personality); ctx.middleware(async (session, next) => { return this.middleware(session, next); }); ctx.command("dvc <text:text>", { authority: config.authority, usageName: "dvc", maxUsage: config.usage }).alias(...config.alias).userFields(["usage"]).action(async ({ session }, ...prompt) => { return this.dvc(session, prompt.join(" ")); }); ctx.command("dvc.clear", "清空所有会话及人格", { authority: 1 }).action(({ session }) => { return this.clear(session); }); if (!config.onlyOnePersonality) { ctx.command("dvc.切换人格 <prompt:text>", "切换为现有的人格", { authority: 1 }).alias("dvc.人格切换", "切换人格").action(async ({ session }, prompt) => { return this.switch_personality(session, prompt); }); ctx.command("dvc.添加人格 <prompt:text>", "更改AI的人格,并重置会话", { authority: 1 }).action(({ session }, prompt) => { session.send( "添加人格失败?看这里!\n https://forum.koishi.xyz/t/topic/2349/4" ); return this.add_personality(session, prompt); }); ctx.command("dvc.删除人格 <prompt:text>", "删除AI的人格,并重置会话", { authority: 1 }).action(({ session }, prompt) => { return this.rm_personality(session, prompt); }); } ctx.command("dvc.重置会话", "重置会话", { authority: 1 }).alias("重置会话").action(({ session }) => { return this.reset(session); }); ctx.command("dvc.output <type:string>", "切换dvc的输出方式").action(({ session }, type) => { return this.switch_output(session, type); }); ctx.command("dvc.生图 <prompt:text>", "生成图片", { authority: 1, usageName: "dvc" }).option("resolution", "-r <resolution:string>").option("img_number", "-n <img_number:number>").action(async ({ session, options }, prompt) => { return this.paint( session, prompt ? prompt : "landscape", options.img_number ? options.img_number : 1, options.resolution ? options.resolution : config.resolution ); }); ctx.command("dvc.翻译 <prompt:text>", "AI翻译", { usageName: "dvc" }).option("lang", "-l <lang:t=string>", { fallback: config.lang }).action(async ({ session, options }, prompt) => { return await this.translate(options.lang, prompt); }); ctx.command("dvc.update", "一键加载 400 条极品预设", { authority: 4 }).alias("dvc.更新预设").option("displace", "-d").action(async ({ session, options }) => { let prompts_latest = await ctx.http.get( "https://gitee.com/initencunter/ChatPrompts/raw/master/safe", { responseType: "text" } ); const prompts_latest_ = JSON.parse( Buffer.from(prompts_latest, "base64").toString("utf-8") ); if (options.displace) { await session.send("该选项将会导致人格丢失,其否继续[Y/n]?"); const confirm = await session.prompt(6e4); if (!confirm) return; if (confirm.toLowerCase() !== "y") return session.send("取消切换"); this.personality = prompts_latest_; } else { for (const i of Object.keys(prompts_latest_)) { this.personality[i] = prompts_latest_[i]; } } import_fs.default.writeFileSync( "./personality.json", JSON.stringify(this.personality, null, 2) ); logger.info("更新预设成功"); return session.execute("切换人格"); }); ctx.command("dvc.cat", "显示一个对话").alias("dvc.会话人格").option("all", "-a --all 显示所有字数").option("personality", "-p <personality:string> 指定人格昵称").option("id", "-i <id:number> 指定会话ID,默认为 0").action(async ({ session, options }) => { if (options?.personality) return JSON.stringify( this.personality[options?.personality ?? "预设人格"] ); const sid = options.id ?? 0; let text = (this.sessions[session.userId]?.[sid] ?? this.session_config[0]).content; if (!options.all && text.length > 200) text = text.slice(0, 200) + "..."; return text; }); ctx.console.addListener("davinci-003/chatTest", async (text) => { const date = (/* @__PURE__ */ new Date()).toLocaleString(); let status = { date }; logger.info("user:" + text); let sessionid = "e2b5e6a3b58f06b914e5ede4d5737afb93afd0cc03f25d66e778bb733e589228"; let session_of_id = this.get_chat_session(sessionid); session_of_id.push({ role: "user", content: ` ${text},现在时间是:${JSON.stringify(status)}` }); let message = await this.try_control(session_of_id); session_of_id.push({ role: "assistant", content: message }); while (JSON.stringify(session_of_id).length > 1e4) { session_of_id.splice(1, 1); if (session_of_id.length <= 1) break; } this.sessions[sessionid] = session_of_id; logger.info(`${this.pluginConfig.appointModel}返回内容: `); logger.info(message); return message; }); ctx.console.addListener("davinci-003/getusage", () => { return localUsage; }); ctx.console.addListener("davinci-003/addPersonality", async (personality) => { try { this.personality[personality.name] = personality.personality; import_fs.default.writeFileSync("./personality.json", JSON.stringify(this.personality, null, 2)); return "success"; } catch (e) { return "error" + e; } }); } /** * * @param lang 目标语言 * @param prompt 要翻译的内容 * @returns 翻译后的内容 */ async translate(lang, prompt) { return this.try_control([ { role: "system", content: "你是一个翻译引擎,请将文本翻译为" + lang + ",只需要翻译不需要解释。" }, { role: "user", content: `请帮我我将如下文字翻译成${lang},“${prompt}”` } ]); } /** * * @param session 会话 * @param prompt 描述词 * @param n 生成数量 * @param size 图片大小 * @returns Promise<string|segment> */ async paint(session, prompt, n, size) { session.send( (0, import_koishi3.h)("quote", { id: session.messageId }) + session.text("commands.dvc.messages.painting") ); try { const response = await this.ctx.http.post( (0, import_koishi3.trimSlash)( `${this.pluginConfig.baseURL ?? "https://api.openai.com"}/v1/images/generations` ), { prompt, n, size }, { timeout: 0, headers: { Authorization: `Bearer ${this.pluginConfig.key[this.key_number]}`, "Content-Type": "application/json" } } ); const result = (0, import_koishi3.segment)("figure"); const attrs = { userId: session.userId, nickname: "GPT" }; for (var msg of response.data) { result.children.push((0, import_koishi3.segment)("message", attrs, import_koishi3.segment.image(msg.url))); } return result; } catch (e) { return session.text("commands.dvc.messages.err", [ `key${this.key_number + 1} 报错:${String(e)}` ]); } } /** * * @param session 会话 * @param prompt 会话内容 * @returns Promise<string | Element> */ async dvc(session, prompt) { if (this.pluginConfig.blockuser.includes(session.userId) || this.pluginConfig.blockchannel.includes(session.channelId)) return; if (!this.pluginConfig.superuser.includes(session.userId)) { let user = await this.ctx.database.getUser( session.platform, session.userId ); let usage = (0, import_koishi_plugin_rate_limit.getUsage)("ai", user); if (usage > this.pluginConfig.usage) return session.text("commands.dvc.messages.usage-exhausted"); } if (!prompt && !session.quote?.content) return session.text("commands.dvc.messages.no-prompt"); if (prompt.length > this.pluginConfig.max_tokens) return session.text("commands.dvc.messages.tooLong"); if (this.pluginConfig.waiting) { const msgId = (await session.bot.sendMessage( session.channelId, (0, import_koishi3.h)("quote", { id: session.messageId }) + session.text("commands.dvc.messages.thinking"), session.guildId ))[0]; if (this.pluginConfig.recall) await recall(session, msgId, this.pluginConfig.recall_time); } if (this.ctx.censor) prompt = await this.ctx.censor.transform(prompt, session); if (!this.pluginConfig.enableContext) { const text = await this.chat_with_gpt([ { role: "user", content: prompt } ]); const resp = [ { role: "user", content: prompt }, { role: "assistant", content: text } ]; return await this.getContent( session.userId, resp, session.messageId, session.bot.selfId ); } else { return await this.chat(prompt, session.userId, session); } } /** * * @param session 当前会话 * @param next 通过函数 * @returns 消息 */ async middleware(session, next) { if (session.elements.filter((i2) => i2.type === "audio" || i2.type === "record").length > 0 && this.pluginConfig.whisper && this.ctx.sst) { const text = await this.ctx.sst.audio2text(session); if (!text) return session.text("commands.dvc.messages.louder"); return this.dvc(session, text); } if (session.subtype === "private" && this.pluginConfig.private) return this.dvc(session, session.content); if (session.stripped.appel && this.pluginConfig.mention) { let msg = ""; for (let i2 of session.elements.slice(1)) { if (i2.type === "text") msg += i2?.attrs?.content; } return this.dvc(session, msg); } if (this.pluginConfig.nickwake) { for (var i of this.sessions_cmd) { if (session.content.startsWith(i)) { this.sessions[session.userId] = this.personality[i]; return await this.dvc(session, session.content); } } } if (this.pluginConfig.randnum == 0) return next(); const randnum = Math.random(); if (randnum < this.pluginConfig.randnum) return await this.dvc(session, session.content); return next(); } /** * * @param message 发送给chatgpt的json列表 * @returns 将返回文字处理成json */ async chat_with_gpt(message) { let url = (0, import_koishi3.trimSlash)( `${this.pluginConfig.baseURL ?? "https://api.openai.com"}/v1/chat/completions` ); const payload = { stream: true, model: this.pluginConfig.appointModel, temperature: this.pluginConfig.temperature, top_p: 1, frequency_penalty: 0, presence_penalty: 0, messages: message }; const config = { timeout: 0, responseType: "stream", keepAlive: true, headers: { Authorization: `Bearer ${this.pluginConfig.key[this.key_number]}`, Accept: "application/json", "Content-Type": "application/json" }, data: payload }; let data; try { data = (await this.ctx.http("POST", url, config)).data; let { contents, reasoning_content } = await this.readableStreamDecoder( data ); reasoning_content = `<think> ${reasoning_content.trim()} </think> `; if (!this.pluginConfig.enableReasoningContent) { reasoning_content = ""; contents = contents.replace(/<think>[\s\S]*?<\/think>/g, ""); } ; return `${reasoning_content}${contents.trim()}`; } catch (e) { if (String(e).includes("Bad Request")) { console.dir(config.data.messages); return "Bad Request"; } this.switch_key(e); return ""; } } async readableStreamDecoder(data) { const reader = data.getReader(); const decoder = new TextDecoder("utf-8"); let sses = "", contents = "", reasoning_content = ""; while (true) { const { done, value } = await reader.read(); if (done) { break; } const newString = decoder.decode(value, { stream: true }); if (newString.startsWith("data:")) { try { for (let sse of sses.split("\n")) { let jsonStr = sse.slice(5).trim(); if (!jsonStr) continue; const json = JSON.parse(jsonStr); const content = json?.choices?.[0]?.delta?.content; const reasoning = json?.choices?.[0]?.delta?.reasoning_content; if (reasoning) reasoning_content += reasoning; if (content) contents += content; } sses = newString; } catch (e) { sses += newString; } } else { sses += newString; } } return { contents, reasoning_content }; } /** * 切换下一个 key */ key_number_pp() { this.key_number++; if (this.key_number === this.pluginConfig.key.length) this.key_number = 0; } /** * 先查询余额 ,如果余额为 0,切换key * @param session 会话 * @param e Error */ async switch_key(e) { logger.info( `key${this.key_number + 1}. ${this.pluginConfig.key[this.key_number]} 报错:${String(e)}` ); this.key_number_pp(); } /** * * @param sessionid QQ号 * @returns 对应QQ的会话 */ get_chat_session(sessionid) { if (Object.keys(this.sessions).indexOf(sessionid) == -1) this.sessions[sessionid] = [...this.session_config]; return this.sessions[sessionid]; } /** * * @param msg prompt消息 * @param sessionid QQ号 * @returns json消息 */ async chat(msg, sessionid, session) { let name2 = session.author?.nick || session.username; logger.info(name2 + ": " + msg); if (this.pluginConfig.onlyOneContext) sessionid = "e2b5e6a3b58f06b914e5ede4d5737afb93afd0cc03f25d66e778bb733e589228"; let session_of_id = this.get_chat_session(sessionid); let message; if (session_of_id[session_of_id.length - 1].role === "user" && this.pluginConfig?.baseURL.includes("api.deepseek.com")) { message = "deepseek 不支持重复的 user, 请等待上一次对话结束"; } else { let rawMsg = { role: "user", content: msg }; if (this.pluginConfig?.baseURL.includes("api.deepseek.com")) { rawMsg["name"] = name2; } session_of_id.push(rawMsg); message = await this.try_control(session_of_id); } if (session_of_id[session_of_id.length - 1].role !== "assistant" || !this.pluginConfig?.baseURL.includes("api.deepseek.com")) { session_of_id.push({ role: "assistant", content: message }); } while (JSON.stringify(session_of_id).length > 1e4) { session_of_id.splice(1, 1); if (session_of_id.length <= 1) break; } this.sessions[sessionid] = session_of_id; logger.info("ChatGPT返回内容: "); logger.info(message); return await this.getContent( sessionid, session_of_id, session.messageId, session.bot.selfId ); } /** * * @param cb chat 回调函数 chat_with_gpt * @param session 会话 * @param session_of_id 会话 ID * @returns */ async try_control(session_of_id) { let try_times = 0; while (try_times < this.pluginConfig.maxRetryTimes) { const res = await this.chat_with_gpt(session_of_id); if (res !== "") return res; try_times++; await this.ctx.sleep(500); } return "请求错误,请查看日志"; } /** * 删除人格逻辑 * @param session * @param nick_name * @returns */ async rm_personality(session, nick_name) { const nick_names = Object.keys(this.personality); if (nick_names.length == 1) return "再删下去就报错了"; if (nick_name && nick_names.indexOf(nick_name) > -1) return this.personality_rm(session, [nick_name]); const input = await switch_menu_grid(session, nick_names, "人格"); if (!input) return session.text("commands.dvc.messages.menu-err"); return this.personality_rm(session, input); } /** * 删除人格 * @param session 会话 * @param nick_name 人格名称 * @returns 字符串 */ personality_rm(session, nick_name) { for (var nick_name_0 of nick_name) { const index = this.sessions_cmd.indexOf(nick_name_0); this.sessions_cmd.splice(index, 1); delete this.personality[nick_name_0]; } this.sessions[session.userId] = [ { role: "system", content: "你是我的全能AI助理" } ]; import_fs.default.writeFileSync( "./personality.json", JSON.stringify(this.personality, null, 2) ); return "人格删除成功"; } /** * * @param session 会话 * @param type 输出类型,字符串 * @returns Promise<string> */ // 切换输出模式 async switch_output(session, type) { const type_arr = ["quote", "figure", "image", "minimal", "voice"]; if (type && type_arr.indexOf(type) > -1) { this.output_type = type; return session.text("commands.dvc.messages.switch-success", [type]); } const input = await switch_menu(session, type_arr, "输出模式"); if (!input) return session.text("commands.dvc.messages.menu-err"); this.output_type = input; return session.text("commands.dvc.messages.switch-success", [ "输出模式", input ]); } /** * * @param userId 用户QQ号 * @param resp gpt返回的json * @returns 文字,图片或聊天记录 */ async getContent(userId, resp, messageId, botId) { if (this.output_type == "voice" && this.ctx.vits) return this.ctx.vits.say({ input: resp[resp.length - 1].content }); else if (this.output_type == "quote") return (0, import_koishi3.h)("quote", { id: messageId }) + resp[resp.length - 1].content; else if (this.output_type == "figure") { const result = (0, import_koishi3.segment)("figure"); for (var msg of resp) { if (msg.role == "user") { result.children.push( (0, import_koishi3.segment)( "message", { userId, nickname: msg.role }, msg.content ) ); continue; } if (msg.role == "assistant") { result.children.push( (0, import_koishi3.segment)( "message", { userId: botId, nickname: msg.role }, msg.content ) ); } else { result.children.push( (0, import_koishi3.segment)( "message", { userId, nickname: msg.role }, msg.content ) ); } } return result; } else if (this.output_type == "image") { const elements = []; for (var msg of resp) { if (msg.role == "user") { elements.push( `<div style='color:#ff9900;font-size: 25px;background:transparent;width=500px;height:50px'>用户:${msg.content}</div>` ); continue; } if (msg.role == "assistant") { elements.push( `<div style='color:black;font-size: 25px;background:transparent;width:500px;height:50px'>AI:${msg.content}</div>` ); } else { elements.push( `<div style='color:#723b8d;font-size: 25px;background:transparent;width:400px'>人格设定:${msg.content}</div>` ); } } let html = `<html> <div style='position: absolute;top:20px;left:20px;width:600px;'> <p style='color:#723b8d'>ChatGPT3.5-Turbo</p> ${elements.join("")} </div> <div style='position: absolute;top:10px;'>create by koishi-plugin-davinci-003@${version}</div> </html>`; return this.ctx.puppeteer.render(html); } else { return import_koishi3.h.text(resp[resp.length - 1].content); } } /** * * @param session 会话 * @returns 切换后的引擎 */ /** * * @param session 会话 * @param prompt 人格昵称 * @returns 人格切换状态 */ async switch_personality(session, prompt) { const nick_names = Object.keys(this.personality); if (prompt && nick_names.indexOf(prompt) > -1) return this.set_personality(session, prompt); const input = await switch_menu_grid(session, nick_names, "人格"); if (!input) return session.text("commands.dvc.messages.menu-err"); return this.set_personality(session, input[0]); } /** * 重置个人会话,保留人格 * @param session 会话 * @returns */ reset(session) { let session_json = this.get_chat_session(session.userId); this.sessions[session.userId] = [ { role: "system", content: session_json[0].content } ]; return "重置成功"; } async add_personality(session, nick_name) { if (!nick_name) { session.send("请输入人格昵称(输入q退出)"); nick_name = await session.prompt(6e4); if (!nick_name || nick_name == "q") session.text("commands.dvc.messages.set-personality"); } let input_key; let input_value; const personality_session = []; while (true) { session.send("请输入role(system||assistant||user)(输入q退出,e结束)"); input_key = await session.prompt(6e4); if (input_key == "q" || !input_key) return session.text("commands.dvc.messages.set-personality"); if (input_key == "e") break; if (["system", "assistant", "user"].indexOf(input_key) == -1) return session.text("commands.dvc.messages.set-personality-role"); session.send("请输入内容(输入q退出)"); input_value = await session.prompt(6e4); if (input_value == "q" || !input_value) return session.text("commands.dvc.messages.set-personality"); personality_session.push({ role: input_key, content: input_value }); } this.sessions_cmd.push(nick_name); this.personality[nick_name] = personality_session; import_fs.default.writeFileSync( "./personality.json", JSON.stringify(this.personality, null, 2) ); return this.set_personality(session, nick_name); } /** * 设置人格 * @param session 会话 * @param nick_name 人格昵称 * @param description 对话 * @returns 字符 */ set_personality(session, nick_name) { this.sessions_cmd.push(nick_name); this.sessions[session.userId] = this.personality[nick_name]; return "人格设置成功: " + nick_name; } /** * * @param session 当前会话 * @returns 返回清空的消息 */ clear(session) { this.sessions = {}; return session.text("commands.dvc.messages.clean"); } }; var src_default = DVc;