koishi-plugin-deepseekapi-chat 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +41 -0
  2. package/src/index.js +183 -0
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "koishi-plugin-deepseekapi-chat",
3
+ "version": "1.0.0",
4
+ "description": "高性能 DeepSeek 大模型接入插件,支持自定义人设、多轮对话记忆及详细参数调节",
5
+ "main": "./src/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "koishi",
11
+ "plugin",
12
+ "deepseek",
13
+ "ai",
14
+ "chat",
15
+ "llm",
16
+ "context",
17
+ "rpg"
18
+ ],
19
+ "author": "YourName",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": ""
24
+ },
25
+ "koishi": {
26
+ "description": {
27
+ "en": "High-performance DeepSeek AI integration with customizable persona, context memory, and advanced parameter tuning.",
28
+ "zh": "高性能 DeepSeek 大模型接入插件。支持自定义系统人设、多轮对话记忆(可配置轮数)、温度/惩罚系数调节,提供 .ds/.ai 指令,适合群聊智能助手。"
29
+ },
30
+ "service": {
31
+ "required": [],
32
+ "optional": []
33
+ }
34
+ },
35
+ "peerDependencies": {
36
+ "koishi": "^4.16.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ }
41
+ }
package/src/index.js ADDED
@@ -0,0 +1,183 @@
1
+ const { Context } = require('koishi');
2
+
3
+ exports.name = 'deepseekapi-chat';
4
+ exports.usage = '使用 .ds <问题> 向 DeepSeek 提问,支持通过配置文件自定义人设和参数';
5
+
6
+ // 定义插件的配置架构
7
+ // 这里的 schema 定义了网页后台的配置表单结构,同时也作为默认值的来源
8
+ exports.Config = (ctx) => ({
9
+ // --- 核心认证 ---
10
+ apiKey: '',
11
+ endpoint: 'https://api.deepseek.com',
12
+
13
+ // --- 模型设置 ---
14
+ model: 'deepseek-chat',
15
+
16
+ // --- 人设与提示词 (核心修改点) ---
17
+ // 默认系统提示词,可以在这里写很长的 Prompt
18
+ systemPrompt: '你是一个 helpful 的 AI 助手。回答请简洁、幽默,适合 QQ 群聊环境。如果用户问的是代码问题,请直接给出代码片段并简要解释。',
19
+
20
+ // --- 对话上下文管理 ---
21
+ // 最大保留的对话轮数 (1轮 = 1问1答)。设置为 0 则不保留历史,每次都是新对话。
22
+ maxContextRounds: 10,
23
+ // 是否在每次请求时强制重新注入 systemPrompt (建议开启,防止被历史记录冲淡)
24
+ injectSystemPromptEveryTime: true,
25
+
26
+ // --- 生成参数 (LLM Hyperparameters) ---
27
+ // 创造性 (0-2)。越高越随机,越低越严谨。
28
+ temperature: 0.7,
29
+ // 核采样概率 (0-1)。与 temperature 配合使用。
30
+ top_p: 0.9,
31
+ // 最大生成令牌数
32
+ max_tokens: 1024,
33
+ // 频率惩罚 (-2 到 2)。降低重复度。
34
+ frequency_penalty: 0,
35
+ // 存在惩罚 (-2 到 2)。鼓励谈论新话题。
36
+ presence_penalty: 0,
37
+
38
+ // --- 行为控制 ---
39
+ // 是否启用流式输出 (目前命令模式暂不支持流式回传,但保留配置供未来扩展或日志记录)
40
+ stream: false,
41
+ // 出错时是否显示详细调试信息 (生产环境建议关闭)
42
+ verboseError: false
43
+ });
44
+
45
+ exports.apply = (ctx, config) => {
46
+ // 存储每个用户的上下文
47
+ // 结构:{ 'session_id': [{role, content}, ...] }
48
+ const sessions = new Map();
49
+
50
+ ctx.command('ds <text:text>', '向 DeepSeek 提问')
51
+ .alias('ai', 'ask', 'deepseek')
52
+ .option('reset', '-r, --reset', { desc: '清空当前会话历史' })
53
+ .action(async ({ session, options }, text) => {
54
+
55
+ // 1. 基础检查
56
+ if (!text && !options.reset) {
57
+ return '请提供问题,例如:.ds 今天天气怎么样?\n使用 .ds -r 可清空记忆。';
58
+ }
59
+
60
+ if (!config.apiKey) {
61
+ return '❌ 插件未配置 API Key,请在配置文件或网页后台填入 deepseek-chat.apiKey。';
62
+ }
63
+
64
+ // 2. 生成唯一会话 ID
65
+ // 策略:群聊中区分用户 (group:gid:user:uid),私聊直接用 uid
66
+ const sessionId = session.groupId
67
+ ? `group:${session.groupId}:user:${session.userId}`
68
+ : `user:${session.userId}`;
69
+
70
+ // 3. 处理重置选项
71
+ if (options.reset) {
72
+ sessions.delete(sessionId);
73
+ return '✅ 已清空当前的对话记忆,我们可以重新开始聊了!';
74
+ }
75
+
76
+ // 初始化或获取历史
77
+ if (!sessions.has(sessionId)) {
78
+ sessions.set(sessionId, []);
79
+ }
80
+ const history = sessions.get(sessionId);
81
+
82
+ // 4. 更新历史记录
83
+ // 只有当用户真的发了文字时才加入历史 (reset 选项不加入)
84
+ if (text) {
85
+ history.push({ role: 'user', content: text });
86
+
87
+ // 限制历史记录长度
88
+ // 注意:maxContextRounds 是“轮数”,每轮包含 1 个 user 和 1 个 assistant
89
+ const maxMessages = config.maxContextRounds * 2;
90
+
91
+ if (config.maxContextRounds > 0 && history.length > maxMessages) {
92
+ // 移除最旧的记录,直到符合长度
93
+ history.splice(0, history.length - maxMessages);
94
+ }
95
+ }
96
+
97
+ // 5. 构造发送给 API 的消息体
98
+ const messages = [];
99
+
100
+ // 添加系统提示词
101
+ // 策略:如果配置了 injectSystemPromptEveryTime,每次都加在最前面
102
+ // 否则,只在历史记录为空时加一次 (这种策略较少用,因为历史记录可能被截断导致丢失人设)
103
+ if (config.systemPrompt) {
104
+ messages.push({ role: 'system', content: config.systemPrompt });
105
+ }
106
+
107
+ // 添加历史对话
108
+ messages.push(...history);
109
+
110
+ // 6. 调用 DeepSeek API
111
+ try {
112
+ const response = await fetch(`${config.endpoint}/v1/chat/completions`, {
113
+ method: 'POST',
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ 'Authorization': `Bearer ${config.apiKey}`
117
+ },
118
+ body: JSON.stringify({
119
+ model: config.model,
120
+ messages: messages,
121
+ stream: config.stream,
122
+ max_tokens: config.max_tokens,
123
+ temperature: config.temperature,
124
+ top_p: config.top_p,
125
+ frequency_penalty: config.frequency_penalty,
126
+ presence_penalty: config.presence_penalty,
127
+ // DeepSeek 特有或通用参数可根据需要添加
128
+ })
129
+ });
130
+
131
+ if (!response.ok) {
132
+ let errDetail = '';
133
+ try {
134
+ const errData = await response.json();
135
+ errDetail = errData.error?.message || JSON.stringify(errData);
136
+ } catch (e) {
137
+ errDetail = await response.text();
138
+ }
139
+
140
+ const errorMsg = config.verboseError
141
+ ? `API 错误 (${response.status}): ${errDetail}`
142
+ : `请求失败 (状态码 ${response.status})`;
143
+
144
+ throw new Error(errorMsg);
145
+ }
146
+
147
+ const data = await response.json();
148
+
149
+ if (!data.choices || data.choices.length === 0) {
150
+ throw new Error('API 返回了空响应');
151
+ }
152
+
153
+ const aiContent = data.choices[0].message.content;
154
+
155
+ // 7. 将 AI 回复加入历史 (仅当成功时)
156
+ if (text) { // 确保是因为有用户输入才记录的
157
+ history.push({ role: 'assistant', content: aiContent });
158
+ }
159
+
160
+ return aiContent;
161
+
162
+ } catch (error) {
163
+ console.error('[DeepSeek Chat] Error:', error.message);
164
+ // 如果是网络错误或 API 密钥错误,可以特殊处理
165
+ if (error.message.includes('401')) {
166
+ return '❌ API Key 无效或已过期,请检查配置。';
167
+ }
168
+ if (error.message.includes('429')) {
169
+ return '⏳ 请求过于频繁,请稍后再试。';
170
+ }
171
+ return `❌ 发生错误:${error.message}`;
172
+ }
173
+ });
174
+
175
+ // 可选:添加一个管理员命令来热重载配置或查看状态
176
+ if (ctx.config.admin) {
177
+ ctx.command('deepseek-status', '查看 DeepSeek 插件状态')
178
+ .action(() => {
179
+ const count = sessions.size;
180
+ return `🤖 DeepSeek 插件运行中\n当前活跃会话数: ${count}\n模型: ${config.model}\n上下文轮数限制: ${config.maxContextRounds}`;
181
+ });
182
+ }
183
+ };