alemonjs-aichat 1.0.13 → 1.0.15

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.
package/README.md CHANGED
@@ -1,39 +1,95 @@
1
1
 
2
2
  ## 1.安装
3
3
 
4
+ 安装中遇到问题可访问alemonjs官网查询[https://alemonjs.com/docs](https://alemonjs.com/docs)
5
+
4
6
  ### yarn安装方式(推荐)
7
+ 前置条件:
8
+ 安装alemonjs: [alemonjs官网](https://alemonjs.com)
5
9
 
6
- ```
10
+ ``` sh
7
11
  yarn add alemonjs-aichat
8
12
  ```
9
13
 
10
- ### alemongo安装方式
11
-
12
- 地址
13
- ```
14
- https://gitee.com/suancaixianyu/alemonjs-aichat.git
14
+ ```sh
15
+ ## 运行
16
+ yarn start
15
17
  ```
16
18
 
17
- 分支
19
+ ### git拉取项目安装
20
+
21
+ ```sh
22
+ git clone git@gitee.com:suancaixianyu/alemonjs-aichat.git
23
+ cd alemonjs-aichat
24
+ yarn
18
25
  ```
19
- release
26
+
27
+ ```sh
28
+ ## 运行
29
+ yarn dev
30
+
31
+ ## 打包
32
+ yarn build
20
33
  ```
21
34
 
22
- ## 2.配置`alemon.config.yaml`
23
- 在配置中添加该插件, 使用`ALemonDesk`只需要启用插件即可
35
+ ## 2.配置
36
+
37
+ #### 配置app
38
+ 在配置中添加该插件, 才能正常触发指令
39
+
40
+ 如果使用的是git拉取项目的方式, 就不需要配置这个
24
41
  ``` yaml
25
42
  app:
26
43
  - 'alemonjs-aichat'
27
44
  ```
28
45
 
29
- ## 3.配置redis
46
+ #### 配置redis
30
47
  `alemon.config.yaml`
31
48
  ``` yaml
32
49
  redis:
33
- host: '127.0.0.1'
50
+ # host默认 127.0.0.1
51
+ host: '127.0.0.1'
52
+ # 端口号默认 6379
34
53
  port: 6379
54
+ # 无密码则留空或者不写, 默认无密码
35
55
  password: ''
56
+ ```
57
+
58
+ #### 配置AI修图
59
+ 修图基于xai的grok接口实现, 该接口返回的图片也是xai的, 在特定平台中可能无法发送此图片,这时候可以用自己写个代理将图片转为可访问状态
60
+
61
+ 已知无需转换的平台: `QQ`,`Bubble`
62
+ `alemon.config.yaml`
63
+ ``` yaml
64
+ # 代理默认可不填
65
+ xaiImgProxy: 'http://127.0.0.1:3002/ximgen'
66
+ ```
67
+
68
+ ai修图依赖于xai, 需要使用该功能必须添加一个grok模型, 在启动机器人后, 使用以下指令添加
69
+ ```
70
+ #添加ai grok https://api.x.ai/v1 <你的apiKey> grok-4-1-fast-non-reasoning
71
+ ```
72
+ 通常情况下nodejs不会使用本地的代理, 因此你可能需要自己解决一下无法访问ai的问题, 例如另写一个脚本, 脚本已经预备好了, 下载项目中的`daili.js`然后运行它即可(Clash的默认端口一般是7890, 可能需要修改一下daili.js中的端口)
73
+ ``` sh
74
+ node daili
75
+ ```
76
+
77
+ 如果你使用的是`daili.js`, 那么地址需要改为`http://127.0.0.1:3002/x/v1`
36
78
 
79
+ #### 配置搜索工具
80
+ 网页搜索功能基于[https://serpapi.com](https://serpapi.com)的接口制作
81
+ 需要前往他们的官网获取key
82
+ `alemon.config.yaml`
83
+ ``` yaml
84
+ # 默认无, 可能无法使用网页搜索
85
+ searchApiKey: ''
86
+ ```
87
+
88
+ #### 配置管理员
89
+ `alemon.config.yaml`
90
+ ``` yaml
91
+ # 默认无, 小部分功能需要管理员权限才能使用
92
+ master_id: ['3501869534']
37
93
  ```
38
94
 
39
95
  ## 4.使用
package/lib/api.js CHANGED
@@ -1,42 +1,10 @@
1
1
  import { redis } from './redis.js';
2
2
  import { getConfigValue } from 'alemonjs';
3
3
  import fs from 'fs';
4
+ import { uploadImageToR2 } from './s3.js';
5
+ import ttsmodels from './data/ttsmodels.json.js';
4
6
 
5
7
  const value = getConfigValue();
6
- const uploadImage = async (base64) => {
7
- const apiUrl = "https://img.scdn.io/api/v1.php";
8
- const uploadSingleImage = async (imageBase64) => {
9
- const singleFormData = new FormData();
10
- const blob = await (await fetch(imageBase64)).blob();
11
- singleFormData.append("image", blob, "image.jpg");
12
- const controller = new AbortController();
13
- const timeoutId = setTimeout(() => controller.abort(), 30000);
14
- const response = await fetch(apiUrl, {
15
- method: "POST",
16
- body: singleFormData,
17
- signal: controller.signal,
18
- });
19
- clearTimeout(timeoutId);
20
- if (!response.ok) {
21
- return "";
22
- }
23
- const result = await response.json();
24
- return result.url || "";
25
- };
26
- if (Array.isArray(base64)) {
27
- const results = [];
28
- for (const item of base64) {
29
- const result = await uploadSingleImage(item);
30
- if (result) {
31
- results.push(result);
32
- }
33
- }
34
- return results;
35
- }
36
- else {
37
- return [await uploadSingleImage(base64)];
38
- }
39
- };
40
8
  const getTimeString = () => {
41
9
  const now = new Date();
42
10
  const month = String(now.getMonth() + 1).padStart(2, "0");
@@ -58,6 +26,41 @@ class TTSClient {
58
26
  const data = await res.json();
59
27
  return data.models;
60
28
  }
29
+ async installModel(modelname) {
30
+ // 遍历ttsmodels,查找model对应的下载链接,模糊匹配
31
+ const matchedModel = ttsmodels.find((m) => {
32
+ return m.models.find((md) => md.modelname.includes(modelname));
33
+ });
34
+ if (!matchedModel) {
35
+ return { success: false, message: "未找到对应的模型" };
36
+ }
37
+ const modelInfo = matchedModel.models.find((md) => md.modelname.includes(modelname));
38
+ if (!modelInfo) {
39
+ return { success: false, message: "未找到对应的模型" };
40
+ }
41
+ // console.log("matchedModel", matchedModel);
42
+ console.log("modelInfo", modelInfo);
43
+ // 在这里实现模型安装逻辑
44
+ const res = await fetch(`${this.host}/install_model`, {
45
+ method: "POST",
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ },
49
+ body: JSON.stringify({
50
+ category: matchedModel.category,
51
+ dl_url: modelInfo.dl_link,
52
+ language: matchedModel.lang,
53
+ model_name: modelInfo.modelname,
54
+ }),
55
+ });
56
+ const data = await res.json();
57
+ if (!res.ok) {
58
+ const err = await res.text();
59
+ console.log(err);
60
+ return { success: false, message: "模型安装失败: " + err };
61
+ }
62
+ return { success: true, message: data.msg };
63
+ }
61
64
  async setModel(model, emotion) {
62
65
  const models = await this.getModels("v4");
63
66
  if (!models) {
@@ -175,9 +178,10 @@ const availableTools = {
175
178
  * @param {string} prompt - 正向提示词
176
179
  * @param {string} [bad_prompt] - 反向提示词
177
180
  * @param {string} [direction] - 方向: landscape, portrait, square
181
+ * @param {boolean} [base64] - 是否返回base64格式的图片
178
182
  * @returns {Promise<{type: "image", url: string}[]>} 图片列表
179
183
  */
180
- StableDiffusionGenerateImage: async ({ prompt, bad_prompt = "(easynegative:1.1), (verybadimagenegative_v1.3:1), (low quality:1.2), (worst quality:1.2)", direction = "portrait", }) => {
184
+ StableDiffusionGenerateImage: async ({ prompt, bad_prompt = "(easynegative:1.1), (verybadimagenegative_v1.3:1), (low quality:1.2), (worst quality:1.2)", direction = "portrait", base64 = false, }) => {
181
185
  const images = [];
182
186
  const sizeMap = {
183
187
  landscape: { width: 768, height: 512 },
@@ -222,6 +226,9 @@ const availableTools = {
222
226
  const base64Data = data.images[0].replace(/^data:image\/\w+;base64,/, "");
223
227
  const buffer = Buffer.from(base64Data, "base64");
224
228
  fs.writeFileSync(path, buffer);
229
+ if (base64) {
230
+ return data.images;
231
+ }
225
232
  images.push({ type: "image", url: path });
226
233
  return images;
227
234
  }
@@ -299,7 +306,21 @@ const availableTools = {
299
306
  const data = await response.json();
300
307
  return data.data.list.map((item) => ({
301
308
  viewUrl: `https://test.suancaixianyu.cn/#/postDetails/${item.id}`,
302
- ...item,
309
+ title: item.title,
310
+ content: item.content,
311
+ userName: item.creator.nickname,
312
+ userPage: `https://test.suancaixianyu.cn/#/user/${item.creatorId}`,
313
+ createdAt: item.createdAt,
314
+ plate: item.plate.name,
315
+ postVersions: item.postVersions.map((version) => ({
316
+ version: version.version,
317
+ versionName: version.title,
318
+ createdAt: version.createdAt,
319
+ files: version.files.map((file) => ({
320
+ name: file.name,
321
+ url: file.url,
322
+ })),
323
+ })),
303
324
  }));
304
325
  }
305
326
  catch (error) {
@@ -327,6 +348,182 @@ const availableTools = {
327
348
  return [];
328
349
  }
329
350
  },
351
+ AIPS: async ({ image_url, instruction }) => {
352
+ // 从aiconfig中查询grok配置
353
+ const aiConfigStr = await redis.get(`ai:list:grok`);
354
+ if (!aiConfigStr) {
355
+ return "未找到AI配置";
356
+ }
357
+ const aiConfig = JSON.parse(aiConfigStr);
358
+ try {
359
+ // 处理单个或多个图片
360
+ const imageUrls = Array.isArray(image_url) ? image_url : [image_url];
361
+ const imgBase64Array = [];
362
+ for (const url of imageUrls) {
363
+ let imgBase64 = "";
364
+ if (url.startsWith("https://imgen.x.ai")) {
365
+ imgBase64 = url;
366
+ }
367
+ else {
368
+ imgBase64 = await uploadImageToR2(url);
369
+ }
370
+ imgBase64Array.push(imgBase64);
371
+ }
372
+ const requestBody = Array.isArray(image_url)
373
+ ? {
374
+ model: "grok-imagine-image",
375
+ images: imgBase64Array.map((base64) => ({
376
+ url: base64,
377
+ type: "image_url",
378
+ })),
379
+ prompt: instruction,
380
+ n: 1,
381
+ }
382
+ : {
383
+ model: "grok-imagine-image",
384
+ image: {
385
+ url: imgBase64Array[0],
386
+ type: "image_url",
387
+ },
388
+ prompt: instruction,
389
+ n: 1,
390
+ };
391
+ console.log("最终请求体", requestBody);
392
+ const res = await fetch(aiConfig.host + "/images/edits", {
393
+ method: "POST",
394
+ headers: {
395
+ "Content-Type": "application/json",
396
+ Authorization: `Bearer ${aiConfig.key}`,
397
+ },
398
+ body: JSON.stringify(requestBody),
399
+ });
400
+ if (!res.ok) {
401
+ const err = await res.text();
402
+ console.log(err);
403
+ return err;
404
+ }
405
+ const image = await res.json();
406
+ console.log("画好了", image);
407
+ return image.data;
408
+ }
409
+ catch (error) {
410
+ console.error("Error calling AIPS:", error);
411
+ return "调用接口失败" + error;
412
+ }
413
+ },
414
+ AIVideos: async ({ image_url, instruction }) => {
415
+ // 从aiconfig中查询grok配置
416
+ const aiConfigStr = await redis.get(`ai:list:grok`);
417
+ if (!aiConfigStr) {
418
+ return "未找到AI配置";
419
+ }
420
+ const aiConfig = JSON.parse(aiConfigStr);
421
+ // 处理单个或多个图片
422
+ const imageUrls = Array.isArray(image_url) ? image_url : [image_url];
423
+ const imgBase64Array = [];
424
+ for (const url of imageUrls) {
425
+ let imgBase64 = "";
426
+ if (url) {
427
+ if (url.startsWith("https://imgen.x.ai")) {
428
+ imgBase64 = url;
429
+ }
430
+ else {
431
+ imgBase64 = await uploadImageToR2(url);
432
+ }
433
+ imgBase64Array.push(imgBase64);
434
+ }
435
+ }
436
+ try {
437
+ const requestBody = imgBase64Array.length === 0
438
+ ? {
439
+ model: "grok-imagine-video",
440
+ prompt: instruction,
441
+ aspect_ratio: "16:9",
442
+ resolution: "720p",
443
+ }
444
+ : Array.isArray(image_url)
445
+ ? {
446
+ model: "grok-imagine-video",
447
+ images: imgBase64Array.map((base64) => ({
448
+ url: base64,
449
+ type: "image_url",
450
+ })),
451
+ prompt: instruction,
452
+ resolution: "720p",
453
+ }
454
+ : {
455
+ model: "grok-imagine-video",
456
+ image: {
457
+ url: imgBase64Array[0],
458
+ type: "image_url",
459
+ },
460
+ prompt: instruction,
461
+ resolution: "720p",
462
+ };
463
+ console.log("最终请求体", requestBody);
464
+ const res = await fetch(aiConfig.host + "/videos/generations", {
465
+ method: "POST",
466
+ headers: {
467
+ "Content-Type": "application/json",
468
+ Authorization: `Bearer ${aiConfig.key}`,
469
+ },
470
+ body: JSON.stringify(requestBody),
471
+ });
472
+ if (!res.ok) {
473
+ const err = await res.text();
474
+ console.log(err);
475
+ return err;
476
+ }
477
+ const video = await res.json();
478
+ console.log("画好了", video);
479
+ return {
480
+ data: video,
481
+ msg: "生成任务已提交,稍后会自动发送生成结果,无需继续等待~,将request_id提供给用户,方便查询生成状态",
482
+ };
483
+ }
484
+ catch (error) {
485
+ console.error("Error calling AIPS:", error);
486
+ return "调用接口失败" + error;
487
+ }
488
+ },
489
+ AIVideoResult: async ({ request_id }) => {
490
+ // 从aiconfig中查询grok配置
491
+ const aiConfigStr = await redis.get(`ai:list:grok`);
492
+ if (!aiConfigStr) {
493
+ return "未找到AI配置";
494
+ }
495
+ const aiConfig = JSON.parse(aiConfigStr);
496
+ try {
497
+ const res = await fetch(aiConfig.host + `/videos/${request_id}`, {
498
+ method: "GET",
499
+ headers: {
500
+ "Content-Type": "application/json",
501
+ Authorization: `Bearer ${aiConfig.key}`,
502
+ },
503
+ });
504
+ if (!res.ok) {
505
+ const err = await res.text();
506
+ console.log(err);
507
+ return err;
508
+ }
509
+ const video = await res.json();
510
+ console.log("视频生成结果", video);
511
+ return video;
512
+ }
513
+ catch (error) {
514
+ console.error("Error calling AIVideoResult:", error);
515
+ return "调用接口失败" + error;
516
+ }
517
+ },
518
+ /**
519
+ * 获取TTS模型列表
520
+ * @returns TTS模型列表
521
+ */
522
+ GetTTSModels: async () => {
523
+ const ttsClient = new TTSClient();
524
+ const models = await ttsClient.getModels("v4");
525
+ return models;
526
+ },
330
527
  };
331
528
 
332
- export { TTSClient, availableTools, getTimeString, uploadImage };
529
+ export { TTSClient, availableTools, getTimeString };
@@ -0,0 +1 @@
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.box-border{box-sizing:border-box}.flex{display:flex}.w-1\/3{width:33.333333%}.flex-wrap{flex-wrap:wrap}.gap-2{gap:.5rem}.overflow-hidden{overflow:hidden}.rounded-lg{border-radius:.5rem}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/60{background-color:rgba(0,0,0,.6)}.bg-cover{background-size:cover}.p-2{padding:.5rem}.p-4{padding:1rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.italic{font-style:italic}.text-\[\#B8AE8E\]{--tw-text-opacity:1;color:rgb(184 174 142/var(--tw-text-opacity,1))}.text-\[\#e8deba\]{--tw-text-opacity:1;color:rgb(232 222 186/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.drop-shadow-md{--tw-drop-shadow:drop-shadow(0 4px 3px rgba(0,0,0,.07)) drop-shadow(0 2px 2px rgba(0,0,0,.06));filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}body{display:flex;flex-direction:column;margin:0;padding:0}.backdrop-blur-xs{backdrop-filter:blur(4px)}.last\:border-0:last-child{border-width:0}
@@ -1,4 +1,4 @@
1
1
  const reg = ['win32'].includes(process.platform) ? /^file:\/\/\// : /^file:\/\// ;
2
- const fileUrl = new URL('main.css-DlHMK5k-.css', import.meta.url).href.replace(reg, '');
2
+ const fileUrl = new URL('main.css-CXQ9gyIo.css', import.meta.url).href.replace(reg, '');
3
3
 
4
4
  export { fileUrl as default };
package/lib/config.js CHANGED
@@ -82,8 +82,8 @@ const getAIChatHistory = async (guid) => {
82
82
  };
83
83
  const addAIChatHistory = async (guid, data) => {
84
84
  const history = await getAIChatHistory(guid);
85
- // 处理messages, 不要超过15条,超过就删除最早的
86
- while (history.length >= 20) {
85
+ // 处理messages, 不要超过40条,超过就删除最早的
86
+ while (history.length >= 40) {
87
87
  history.shift();
88
88
  }
89
89
  history.push(data);
@@ -105,8 +105,8 @@ const clearAIChatHistory = async (guid) => {
105
105
  * @param userKey 用户标识
106
106
  * @returns
107
107
  */
108
- const getAffectionLevel = async (userKey) => {
109
- const levelStr = await redis.get(`ai:affection:${userKey}`);
108
+ const getAffectionLevel = async (guid, userKey) => {
109
+ const levelStr = await redis.get(`ai:affection:${guid}:${userKey}`);
110
110
  if (levelStr) {
111
111
  return parseInt(levelStr, 10);
112
112
  }
@@ -130,8 +130,8 @@ const getAffectionLevelAll = async (guid) => {
130
130
  * @param level 好感度等级
131
131
  * @returns
132
132
  */
133
- const setAffectionLevel = async (userKey, level) => {
134
- return await redis.set(`ai:affection:${userKey}`, level.toString());
133
+ const setAffectionLevel = async (guid, userKey, level) => {
134
+ return await redis.set(`ai:affection:${guid}:${userKey}`, level.toString());
135
135
  };
136
136
  /**
137
137
  * 增加好感度等级
@@ -139,10 +139,10 @@ const setAffectionLevel = async (userKey, level) => {
139
139
  * @param increment 增加的等级
140
140
  * @returns
141
141
  */
142
- const incrementAffectionLevel = async (userKey, increment) => {
143
- const currentLevel = await getAffectionLevel(userKey);
142
+ const incrementAffectionLevel = async (guid, userKey, increment) => {
143
+ const currentLevel = await getAffectionLevel(guid, userKey);
144
144
  const newLevel = currentLevel + increment;
145
- await setAffectionLevel(userKey, newLevel);
145
+ await setAffectionLevel(guid, userKey, newLevel);
146
146
  return newLevel;
147
147
  };
148
148
  /** * 重置好感度等级
@@ -180,12 +180,26 @@ const deleteAI = async (name) => {
180
180
  const affectionSwitch = async (guid, enable) => {
181
181
  return await redis.set(`ai:affection:switch:${guid}`, enable ? "1" : "0");
182
182
  };
183
+ /**
184
+ * 获取好感度开关状态
185
+ */
186
+ const getAffectionSwitch = async (guid) => {
187
+ const switchStr = (await redis.get(`ai:affection:switch:${guid}`)) || "1";
188
+ return switchStr;
189
+ };
190
+ /**
191
+ * 获取tts回复开关状态
192
+ */
193
+ const getTTSResponseSwitch = async (guid) => {
194
+ const switchStr = (await redis.get(`chat:tts:response:${guid}`)) || "0";
195
+ return switchStr;
196
+ };
183
197
  const tools = [
184
198
  {
185
199
  type: "function",
186
200
  function: {
187
201
  name: "StableDiffusionGenerateImage",
188
- description: "使用 Stable Diffusion 模型生成图像",
202
+ description: "使用 Stable Diffusion 模型生成图像, 该工具只擅长生成动漫风格的图片, 文本生成图像, 如需修改图像请使用AIPS工具",
189
203
  parameters: {
190
204
  type: "object",
191
205
  properties: {
@@ -209,23 +223,24 @@ const tools = [
209
223
  },
210
224
  },
211
225
  },
212
- {
213
- type: "function",
214
- function: {
215
- name: "StableDiffusionImageToPrompt",
216
- description: "使用 Stable Diffusion 将图片转换为提示词, 仅供绘图时使用, 其他场景请勿使用",
217
- parameters: {
218
- type: "object",
219
- properties: {
220
- image_url: {
221
- type: "string",
222
- description: "图片的URL地址,必须是你所收到的图片地址",
223
- },
224
- },
225
- required: ["image_url"],
226
- },
227
- },
228
- },
226
+ // {
227
+ // type: "function",
228
+ // function: {
229
+ // name: "StableDiffusionImageToPrompt",
230
+ // description:
231
+ // "使用 Stable Diffusion 将图片转换为提示词, 它只对动漫图片有效, 只能用来提取动漫图片的提示词",
232
+ // parameters: {
233
+ // type: "object",
234
+ // properties: {
235
+ // image_url: {
236
+ // type: "string",
237
+ // description: "图片的URL地址,必须是你所收到的图片地址",
238
+ // },
239
+ // },
240
+ // required: ["image_url"],
241
+ // },
242
+ // },
243
+ // },
229
244
  {
230
245
  type: "function",
231
246
  function: {
@@ -277,6 +292,38 @@ const tools = [
277
292
  },
278
293
  },
279
294
  },
295
+ {
296
+ type: "function",
297
+ function: {
298
+ name: "AIPS",
299
+ description: "AI修图工具, 可以对图片进行图像编辑, 无法用于图片分析. 使用时只需提供需要修改的图片url和修改需求即可. 当遇到审核拦截的问题时, 请不要再次尝试提交相同的修改需求, 这可能会导致账号被封禁. 请修改你的修改需求文本描述, 使其更具体和详细, 以避免触发审核拦截. ",
300
+ parameters: {
301
+ type: "object",
302
+ properties: {
303
+ image_url: {
304
+ type: "string",
305
+ description: "需要修改的图片URL地址,必须是你所收到的图片地址",
306
+ },
307
+ instruction: {
308
+ type: "string",
309
+ description: "修改需求的文本描述,尽量详细和具体,例如:'请将图片中的人物换成一个穿着宇航服的宇航员,背景改成月球表面,保持其他元素不变'",
310
+ },
311
+ },
312
+ required: ["image_url", "instruction"],
313
+ },
314
+ },
315
+ },
316
+ {
317
+ type: "function",
318
+ function: {
319
+ name: "GetTTSModels",
320
+ description: "获取可用的TTS模型列表, 可以用来选择不同的音色进行语音合成. 模型会不定期更新, 你可以定期调用这个工具来获取最新的模型列表. 当你想要使用TTS功能时, 可以先调用这个工具来获取可用的模型列表, 然后选择一个你喜欢的模型来进行语音合成. 例如, 你可以选择一个叫做'派蒙-默认'的模型来模仿派蒙的声音进行语音合成",
321
+ parameters: {
322
+ type: "object",
323
+ properties: {},
324
+ },
325
+ },
326
+ },
280
327
  ];
281
328
 
282
- export { addAI, addAIChatHistory, addPrompt, affectionSwitch, clearAIChatHistory, defaultAIConfig, deleteAI, deletePrompt, getAIChatHistory, getAIConfig, getAIList, getAffectionLevel, getAffectionLevelAll, getPromptList, incrementAffectionLevel, resetAffectionLevel, resetAllAffectionLevels, setAffectionLevel, setBotID, switchAI, switchPrompt, systemPrompt, tools };
329
+ export { addAI, addAIChatHistory, addPrompt, affectionSwitch, clearAIChatHistory, defaultAIConfig, deleteAI, deletePrompt, getAIChatHistory, getAIConfig, getAIList, getAffectionLevel, getAffectionLevelAll, getAffectionSwitch, getPromptList, getTTSResponseSwitch, incrementAffectionLevel, resetAffectionLevel, resetAllAffectionLevels, setAffectionLevel, setBotID, switchAI, switchPrompt, systemPrompt, tools };