alemonjs-aichat 1.0.31-beta.2 → 1.0.31

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.
@@ -181,7 +181,7 @@ const tools = [
181
181
  description: "要执行的终端命令",
182
182
  },
183
183
  },
184
- required: ["command"],
184
+ required: ["userId", "command"],
185
185
  },
186
186
  },
187
187
  },
@@ -234,7 +234,7 @@ const tools = [
234
234
  type: "function",
235
235
  function: {
236
236
  name: "MemoryOperation",
237
- description: "这个工具用于获取或修改对于用户的记忆数据, 包括用户形象, 用户偏好, 历史对话等. 你可以使用这个工具来存储一些需要长期记忆的信息, 例如用户的兴趣爱好, 重要事件等. 当你想要获取或修改这些信息时, 可以调用这个工具并提供具体的操作指令和数据. 例如, 在聊天过程中他对你的态度,以及他喜欢的东西等,可以通过这个工具及时记录下来, 也可以通过这个工具获取之前的对话记录,便于继续之前的话题",
237
+ description: "这个工具用于获取或修改对于用户的记忆数据, 包括用户形象, 用户偏好, 历史对话等. 你可以使用这个工具来存储一些需要长期记忆的信息, 例如用户的兴趣爱好, 重要事件等, 在不认识对方时可以尝试一下获取. 当你想要获取或修改这些信息时, 可以调用这个工具并提供具体的操作指令和数据. 例如, 在聊天过程中他对你的态度,以及他喜欢的东西等,可以通过这个工具及时记录下来, 也可以通过这个工具获取之前的对话记录,便于继续之前的话题",
238
238
  parameters: {
239
239
  type: "object",
240
240
  properties: {
@@ -55,7 +55,7 @@ var res = onResponse(selects, async (e) => {
55
55
  message.send(format(Text("你又再骗我画涩图了喵~已经保存到主人的收藏夹了,不给你看,哼!~")));
56
56
  }
57
57
  else {
58
- message.send(format(Text("画图完成喵~\n"), Text(content), Image(res[0])));
58
+ message.send(format(Text("画图完成喵~\n"), Text(content), Image(imgUrl)));
59
59
  }
60
60
  await redisClient.addImgUrl(e.guid, imgUrl);
61
61
  }
@@ -66,7 +66,7 @@ var res = onResponse(selects, async (e) => {
66
66
  message.send(format(Text(json.error)));
67
67
  }
68
68
  catch (error) {
69
- message.send(format(Text("修图报错了喵~")));
69
+ message.send(format(Text("修图报错了喵~:" + res)));
70
70
  }
71
71
  }
72
72
  }
@@ -4,7 +4,7 @@ import OpenAi from 'openai';
4
4
  import { TTSClient } from '../../api/tts.js';
5
5
  import { availableTools } from '../../api/aitools.js';
6
6
  import redisClient from '../../config.js';
7
- import { shouldSkipAIReply, getChatConfig, buildUserMessage } from './getChatConfig.js';
7
+ import { shouldSkipAIReply, getChatConfig, buildUserMessage, getDeepThoughtReasoning } from './getChatConfig.js';
8
8
  import { createTTSMessage } from './tts.js';
9
9
  import { parseAIReply } from './tools.js';
10
10
 
@@ -44,46 +44,34 @@ const CApiReply = async (e) => {
44
44
  createParams["tools"] = cfg.toolConfig.cApiToolList;
45
45
  createParams["tool_choice"] = "auto";
46
46
  }
47
- if (cfg.deepThoughtSwitch === "0") {
48
- createParams["verbosity"] = "low";
49
- }
50
- createParams["stream"] = true;
47
+ const deepThoughtReasoning = getDeepThoughtReasoning(cfg);
48
+ Object.assign(createParams, deepThoughtReasoning);
49
+ createParams["stream"] = false;
50
+ log("请求AI,参数:", createParams);
51
51
  const stream = (await openai.chat.completions.create(createParams));
52
+ log("AI回复原始数据:\n", JSON.stringify(stream, null, 2));
52
53
  let fullContent = "";
53
54
  let toolCalls = [];
54
- for await (const chunk of stream) {
55
- const delta = chunk.choices?.[0]?.delta;
56
- if (!delta)
57
- continue;
58
- if (delta.content) {
59
- fullContent += delta.content;
60
- }
61
- if (delta.tool_calls) {
62
- for (const tc of delta.tool_calls) {
63
- if (tc.index !== undefined) {
64
- if (!toolCalls[tc.index]) {
65
- toolCalls[tc.index] = {
66
- id: tc.id || "",
67
- type: "function",
68
- function: { name: "", arguments: "" },
69
- };
70
- }
71
- if (tc.id)
72
- toolCalls[tc.index].id = tc.id;
73
- if (tc.function?.name)
74
- toolCalls[tc.index].function.name += tc.function.name;
75
- if (tc.function?.arguments)
76
- toolCalls[tc.index].function.arguments += tc.function.arguments;
77
- }
78
- }
79
- }
55
+ const completion = stream;
56
+ const aiReplyMessage = completion?.choices?.[0]?.message;
57
+ if (aiReplyMessage?.content) {
58
+ fullContent = aiReplyMessage.content;
59
+ }
60
+ if (Array.isArray(aiReplyMessage?.tool_calls)) {
61
+ toolCalls = aiReplyMessage.tool_calls.map((tc) => ({
62
+ id: tc.id || "",
63
+ type: "function",
64
+ function: {
65
+ name: tc.function?.name || "",
66
+ arguments: tc.function?.arguments || "",
67
+ },
68
+ }));
80
69
  }
81
70
  let res = {
82
71
  choices: [
83
72
  {
84
73
  message: {
85
- role: "assistant",
86
- content: fullContent || null,
74
+ ...aiReplyMessage,
87
75
  tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
88
76
  },
89
77
  },
@@ -95,13 +83,17 @@ const CApiReply = async (e) => {
95
83
  }
96
84
  messages.push(usermessage);
97
85
  await redisClient.addAIChatHistory(e.guid, usermessage);
86
+ if (res.choices[0].message?.tool_calls && fullContent.trim() !== "") {
87
+ message.send(format(Text(fullContent)));
88
+ }
98
89
  // 检查是否有工具调用需要处理
99
90
  while (res.choices[0].message?.tool_calls?.length) {
100
91
  const responseMessage = res.choices[0].message;
92
+ log("AI返回工具调用,内容:-----", res.choices[0]);
101
93
  // 先处理对话
102
94
  messages.push(responseMessage);
103
95
  await redisClient.addAIChatHistory(e.guid, responseMessage);
104
- console.log("模型决定调用工具:", responseMessage.tool_calls);
96
+ log("模型决定调用工具:", responseMessage.tool_calls);
105
97
  if (cfg.toolConfig.toolPromptSwitch === "1") {
106
98
  const useToolsMessage = responseMessage.tool_calls.map((toolCall) => {
107
99
  return `工具调用: ${toolCall.function.name}(${cfg.toolConfig.toolPromptArgsSwitch === "1" ? toolCall.function.arguments : ""})`;
@@ -165,6 +157,7 @@ const CApiReply = async (e) => {
165
157
  tool_call_id: toolCall.id,
166
158
  name: functionName,
167
159
  content: toolContent,
160
+ reasoning_content: responseMessage.reasoning_content || "",
168
161
  });
169
162
  // 记录工具调用结果
170
163
  await redisClient.addAIChatHistory(e.guid, {
@@ -172,15 +165,20 @@ const CApiReply = async (e) => {
172
165
  tool_call_id: toolCall.id,
173
166
  name: functionName,
174
167
  content: toolContent,
168
+ reasoning_content: responseMessage.reasoning_content || "",
175
169
  });
176
170
  }
177
- // 重新请求
178
- res = await openai.chat.completions.create({
171
+ const params = {
179
172
  model: cfg.config.model,
180
173
  messages: messages,
181
174
  tools: cfg.toolConfig.cApiToolList,
182
175
  tool_choice: "auto",
183
- });
176
+ };
177
+ const deepThoughtReasoning = getDeepThoughtReasoning(cfg);
178
+ Object.assign(params, deepThoughtReasoning);
179
+ log("重新请求AI,参数:", params);
180
+ // 重新请求
181
+ res = await openai.chat.completions.create(params);
184
182
  // 检查是否还有工具调用需要处理
185
183
  if (!res.choices || res.choices.length === 0) {
186
184
  log("AI未返回内容");
@@ -193,7 +191,7 @@ const CApiReply = async (e) => {
193
191
  log("AI选择不回复");
194
192
  return;
195
193
  }
196
- console.log("AI回复原文:\n", reply);
194
+ console.log("AI回复原文:\n", JSON.stringify(res, null, 2));
197
195
  // 处理消息
198
196
  // 提取ai发送的消息有多少条
199
197
  const aireply = parseAIReply(reply, cfg, message, e);
@@ -101,16 +101,25 @@ const getChatConfig = async (e) => {
101
101
  当场景为私聊时, 用户昵称前方会出现[私聊]标识
102
102
  当平台适配环境较好时,用户消息前面会包含消息id, 例如: [私聊]用户昵称(用户id)(发送时间)(msgid:消息id):消息内容
103
103
  你发送的消息内容会被框架自动加上消息id, 以便你在需要撤回消息时使用
104
+
104
105
  ### 你的回复格式
105
106
  ${botName}:消息内容
106
107
  #### 拒绝回复:
107
108
  在讨论中如果你觉得不适合回复或觉得与你无关又或者不感兴趣, 可以直接回复"[]"来拒绝本次回复, 例如:
108
109
  ${botName}:[]
109
-
110
+ #### 图片
111
+ ${botName}:<img=http://example.com/image.jpg>
112
+ ${botName}:<img=./path/to/image.jpg>
113
+ #### 艾特用户
114
+ ${botName}:<at=用户id>消息内容
115
+ #### 撤回消息
116
+ ${botName}:<revoke=消息id>
110
117
  ${complexOutputIsOpen === "1" && isOpenTTSReply === "1"
111
- ? ""
118
+ ? `#### 语音
119
+ ${botName}:<tts=你好这是一个语音消息>
120
+ #### 切换音色
121
+ ${botName}:<音色=派蒙-默认><tts=这是派蒙的默认声音哦>`
112
122
  : "#### 语音回复: 当前不可用"}
113
-
114
123
  ${isOpenR18 === "1"
115
124
  ? ""
116
125
  : `
@@ -330,5 +339,48 @@ const buildUserMessage = async (e, cfg, mode = "capi") => {
330
339
  ],
331
340
  };
332
341
  };
342
+ const getDeepThoughtReasoning = (cfg) => {
343
+ if (cfg.deepThoughtSwitch === "1") {
344
+ // 开启深度思考, 深度 中等medium
345
+ if (cfg.config.model.includes("deepseek")) {
346
+ return {
347
+ thinking: { type: "enabled" },
348
+ reasoning_effort: "medium",
349
+ };
350
+ }
351
+ if (cfg.config.model.includes("doubao")) {
352
+ return {
353
+ reasoning: {
354
+ effort: "medium",
355
+ },
356
+ };
357
+ }
358
+ if (cfg.config.model.includes("qwen")) {
359
+ return {
360
+ enable_thinking: true,
361
+ };
362
+ }
363
+ }
364
+ else {
365
+ // 关闭深度思考
366
+ if (cfg.config.model.includes("deepseek")) {
367
+ return {
368
+ thinking: { type: "disabled" },
369
+ };
370
+ }
371
+ if (cfg.config.model.includes("doubao")) {
372
+ return {
373
+ reasoning: {
374
+ effort: "minimal",
375
+ },
376
+ };
377
+ }
378
+ if (cfg.config.model.includes("qwen")) {
379
+ return {
380
+ enable_thinking: false,
381
+ };
382
+ }
383
+ }
384
+ };
333
385
 
334
- export { buildUserMessage, getChatConfig, normalizeTools, shouldSkipAIReply };
386
+ export { buildUserMessage, getChatConfig, getDeepThoughtReasoning, normalizeTools, shouldSkipAIReply };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alemonjs-aichat",
3
- "version": "1.0.31-beta.2",
3
+ "version": "1.0.31",
4
4
  "description": "alemonjs-aichat",
5
5
  "author": "suancaixianyu",
6
6
  "license": "MIT",
@@ -19,10 +19,10 @@
19
19
  "@alemonjs/bubble": "^2.1.10",
20
20
  "@alemonjs/db": "^0.0.16",
21
21
  "@alemonjs/onebot": "^2.1.14",
22
- "@types/node": "^22",
23
- "alemonjs": "2.1.57",
22
+ "@types/node": "^22.19.17",
23
+ "alemonjs": "2.1.65",
24
24
  "cssnano": "^7",
25
- "jsxp": "^1.3.0",
25
+ "jsxp": "^1.4.0",
26
26
  "lvyjs": "^0.2.25",
27
27
  "pm2": "^5",
28
28
  "tailwindcss": "3"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: send-media
3
- description: 发送媒体文件, 包括文本、图片、音频和视频等格式规范
3
+ description: 发送媒体文件, 包括文本、图片、音频和视频等格式规范, 在与用户对话之前请务必仔细阅读该技能文档, 以免发送消息失败
4
4
  ---
5
5
 
6
6
  # Send Media Skill
@@ -25,7 +25,7 @@ description: 发送媒体文件, 包括文本、图片、音频和视频等格
25
25
 
26
26
  ```txt
27
27
  小咸鱼: <img=http://example.com/image.jpg>
28
- 小咸鱼: <img=/path/to/image.jpg>
28
+ 小咸鱼: <img=./path/to/image.jpg>
29
29
  ```
30
30
 
31
31
  ## 3. 发送音频