koishi-plugin-deepseek-ai
Version:
一个可以使用deepseek-ai的插件,支持预设
233 lines (231 loc) • 11.5 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Config: () => Config,
apply: () => apply,
name: () => name,
usage: () => usage
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
var name = "deepseek-ai";
var usage = "deepseek的api适配插件,可以调用官方的api来接入koishi。插件本身用deepseek开发,所以里面的代码其实还留有很多备注。本人并不是专业开发者,所以后续可能很难有更新,不过也可以关注也许,也许以后还能开发出更多功能?(笑)apikey请自行到官网 https://www.deepseek.com/ 创建获取,本插件并不提供。";
var Config = import_koishi.Schema.object({
apiKey: import_koishi.Schema.string().required().description("DeepSeek API密钥"),
apiEndpoint: import_koishi.Schema.string().description("API端点").default("https://api.deepseek.com/v1"),
defaultModelIndex: import_koishi.Schema.number().min(1).description("默认模型索引(从1开始)").default(1),
presets: import_koishi.Schema.array(
import_koishi.Schema.object({
name: import_koishi.Schema.string().required().description("预设名称"),
content: import_koishi.Schema.string().required().description("预设内容")
})
).description("预设列表").default([]),
defaultPresetIndex: import_koishi.Schema.number().min(1).description("默认预设索引(从1开始)").default(1),
enableReasoner: import_koishi.Schema.boolean().description("启用DeepSeek-R1推理模型优化").default(false),
timeout: import_koishi.Schema.number().description("API请求超时时间(毫秒)").default(1e4),
waitingMessage: import_koishi.Schema.string().description("等待提示消息").default("请求处理中,请稍候..."),
requestLimit: import_koishi.Schema.number().description("API请求次数限制(0或负数表示无限制)").default(0),
maxHistory: import_koishi.Schema.number().description("历史消息缓存限制(0或负数表示无限制)").default(10),
apiErrorText: import_koishi.Schema.string().description("API请求失败提示内容").default("请求处理失败,请稍后重试"),
creativity: import_koishi.Schema.number().min(0).max(2).step(0.1).description("创意活跃度 (范围为0~2,值越大回答越有创意)").default(1),
openness: import_koishi.Schema.number().min(0).max(1).step(0.1).description("思维开放度 (范围为0~1,值越大考虑可能性越多)").default(1),
divergence: import_koishi.Schema.number().min(-2).max(2).step(0.1).description("表述发散度 (范围为-2~2,值越大表达越多样)").default(0),
vocabulary: import_koishi.Schema.number().min(-2).max(2).step(0.1).description("词汇丰富度 (范围为-2~2,值越大用词越丰富)").default(0)
});
function apply(ctx, config) {
let models = [];
let currentModelIndex = config.defaultModelIndex - 1;
let currentPresetIndex = config.defaultPresetIndex - 1;
const userRequestCounts = {};
const userHistory = {};
ctx.on("ready", async () => {
try {
const response = await ctx.http.get(`${config.apiEndpoint}/models`, {
headers: { Authorization: `Bearer ${config.apiKey}` }
});
models = response.data.map((model) => model.id);
currentModelIndex = Math.min(currentModelIndex, models.length - 1);
ctx.logger.info(`加载模型成功:${models.join(", ")}`);
} catch (error) {
ctx.logger.error("模型加载失败:", error);
}
});
function getAllPresets() {
return config.presets;
}
__name(getAllPresets, "getAllPresets");
async function addPreset(name2, content) {
const newPreset = { name: name2, content };
config.presets = [...config.presets, newPreset];
await ctx.scope.update(config);
ctx.logger.info(`已添加预设:${name2}`);
return newPreset;
}
__name(addPreset, "addPreset");
async function removePreset(index) {
if (index < 0 || index >= config.presets.length) {
throw new Error("无效预设索引");
}
const removedPreset = config.presets[index];
config.presets = config.presets.filter((_, i) => i !== index);
await ctx.scope.update(config);
ctx.logger.info(`已删除预设:${removedPreset.name}`);
return removedPreset;
}
__name(removePreset, "removePreset");
function clearUserHistory(userId) {
if (userHistory[userId]) {
delete userHistory[userId];
ctx.logger.info(`已清除用户 ${userId} 的历史消息`);
}
}
__name(clearUserHistory, "clearUserHistory");
ctx.command("deepseek", "调用deepseek").usage("使用说明:切换模型请使用二级命令-m,列出的模型前面标有序号只需要跟着后面的序号即可。切换预设也是一样的,如果已经配置了预设切换就只需要输入预设前的序号就行,删除预设也是一样的。添加预设略有不同,执行完二级指令-a后根据机器人输出的提示输入预设名称和内容即可,而列出预设和模型用法和添加预设类似,唯一不同的是列出预设只需执行完-l即可。二级指令的使用示例:deepseek -m 1(示例只示范了切换模型的用法,像是添加预设只需要deepseek -a即可)。").alias("say").option("model", "-m <index:number> 切换模型").option("preset", "-p <index:number> 切换预设").option("list", "-l 列出模型和预设").option("addPreset", "-a 添加预设").option("removePreset", "-r <index:number> 删除预设").option("clearHistory", "-c 清除历史消息缓存").action(async ({ session, options }, message) => {
const userId = session.userId;
if (options?.clearHistory) {
clearUserHistory(userId);
return "已清除历史消息缓存";
}
if (options?.list) {
const modelList = models.map((m, i) => `${i + 1}. ${m}${i === currentModelIndex ? " (当前使用)" : ""}`).join("\n");
const presets = getAllPresets();
const presetList = presets.map((p, i) => `${i + 1}. ${p.name}${i === currentPresetIndex ? " (当前使用)" : ""}`).join("\n");
const remainingRequests = config.requestLimit > 0 ? `
剩余调用次数:${config.requestLimit - (userRequestCounts[userId] || 0)}` : "\n剩余调用次数:无限制";
return [
`当前模型:${models[currentModelIndex]} (${currentModelIndex + 1}/${models.length})`,
modelList && `
可用模型:
${modelList}`,
presets.length ? `
当前预设:${presets[currentPresetIndex]?.name || "无"}` : "",
presets.length ? `
可用预设:
${presetList}` : "",
remainingRequests
].filter(Boolean).join("\n");
}
if (options?.model !== void 0) {
const index = options.model - 1;
if (index < 0 || index >= models.length) {
return `无效模型索引,当前有 ${models.length} 个模型`;
}
currentModelIndex = index;
return `已切换至模型:${models[index]}`;
}
if (options?.preset !== void 0) {
const presets = getAllPresets();
const index = options.preset - 1;
if (index < 0 || index >= presets.length) {
return `无效预设索引,当前有 ${presets.length} 个预设`;
}
currentPresetIndex = index;
return `已切换至预设:${presets[index].name}`;
}
if (options?.addPreset) {
if (!session) return "无法在非会话上下文中添加预设";
await session.send("请输入预设名称:");
const name2 = await session.prompt();
if (!name2) return "预设名称不能为空";
if (config.presets.some((p) => p.name === name2)) {
return `预设名称 "${name2}" 已存在,请使用其他名称`;
}
await session.send("请输入预设内容:");
const content = await session.prompt();
if (!content) return "预设内容不能为空";
const newPreset = await addPreset(name2, content);
return `已添加预设:${newPreset.name}`;
}
if (options?.removePreset !== void 0) {
const index = options.removePreset - 1;
try {
const removedPreset = await removePreset(index);
return `已删除预设:${removedPreset.name}`;
} catch (error) {
return error.message;
}
}
if (!message) return "请输入消息内容";
if (config.requestLimit > 0) {
const userRequestCount = userRequestCounts[userId] || 0;
if (userRequestCount >= config.requestLimit) {
return `您的 API 请求次数已达到上限(${config.requestLimit} 次)`;
}
userRequestCounts[userId] = userRequestCount + 1;
}
try {
session?.send(config.waitingMessage);
const presets = getAllPresets();
const finalMessage = presets[currentPresetIndex] ? `${presets[currentPresetIndex].content}
${message}` : message;
if (!userHistory[userId]) userHistory[userId] = [];
userHistory[userId].push({ role: "user", content: finalMessage });
if (config.maxHistory > 0 && userHistory[userId].length > config.maxHistory) {
userHistory[userId] = userHistory[userId].slice(-config.maxHistory);
}
const response = await ctx.http.post(
`${config.apiEndpoint}/chat/completions`,
{
model: models[currentModelIndex],
messages: [...userHistory[userId]],
temperature: config.creativity,
top_p: config.openness,
repetition_penalty: 1 + Math.abs(config.divergence) / 2,
// 转换为1.0-2.0范围
frequency_penalty: config.vocabulary
},
{
headers: {
Authorization: `Bearer ${config.apiKey}`,
"Content-Type": "application/json"
},
timeout: config.enableReasoner ? config.timeout * 2 : config.timeout
}
);
const content = response.choices[0].message.content;
userHistory[userId].push({ role: "assistant", content });
if (config.enableReasoner) {
const splitRegex = /\n\nFinal Answer:\s*/i;
const parts = content.split(splitRegex);
if (parts.length >= 2) {
const reasoning = parts[0].trim();
const answer = parts.slice(1).join("").trim();
if (reasoning) session?.send(`推理过程:
${reasoning}`);
return `最终答案:
${answer || "未提供最终答案"}`;
}
}
return content;
} catch (error) {
ctx.logger.error("API请求失败:", error);
return config.apiErrorText;
}
});
}
__name(apply, "apply");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Config,
apply,
name,
usage
});