Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/AI/AI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class AI {
context: Context;
tool: ToolManager;
memory: Memory;
image: ImageManager;
imageManager: ImageManager;
privilege: Privilege;

// 下面是临时变量,用于处理消息
Expand All @@ -45,7 +45,7 @@ export class AI {
this.context = new Context();
this.tool = new ToolManager();
this.memory = new Memory();
this.image = new ImageManager();
this.imageManager = new ImageManager();
this.privilege = {
limit: 100,
counter: -1,
Expand All @@ -66,7 +66,7 @@ export class AI {

static reviver(value: any, id: string): AI {
const ai = new AI(id);
const validKeys = ['version', 'context', 'tool', 'memory', 'image', 'privilege'];
const validKeys = ['version', 'context', 'tool', 'memory', 'imageManager', 'privilege'];

for (const k of validKeys) {
if (value.hasOwnProperty(k)) {
Expand Down Expand Up @@ -132,7 +132,7 @@ export class AI {

//获取处理后的回复
const raw_reply = await sendChatRequest(ctx, msg, this, messages, "auto");
result = await handleReply(ctx, msg, raw_reply, this.context);
result = await handleReply(ctx, msg, this, raw_reply);

if (!checkRepeat(this.context, result.contextArray.join('')) || result.replyArray.join('').trim() === '') {
break;
Expand Down Expand Up @@ -160,7 +160,8 @@ export class AI {
//发送偷来的图片
const { p } = ConfigManager.image;
if (Math.random() * 100 <= p) {
const file = await this.image.drawImageFile();
const file = await this.imageManager.drawImageFile();

if (file) {
seal.replyToSender(ctx, msg, `[CQ:image,file=${file}]`);
}
Expand Down Expand Up @@ -211,7 +212,7 @@ export class AI {
// 对于function_call前面的内容,发送并添加到上下文中
const match = raw_reply.match(/([\s\S]*)<function(?:_call)?>/);
if (match && match[1].trim()) {
const { contextArray, replyArray, images } = await handleReply(ctx, msg, match[1], this.context);
const { contextArray, replyArray, images } = await handleReply(ctx, msg, this, match[1]);

if (this.stream.id !== id) {
return;
Expand Down Expand Up @@ -267,7 +268,7 @@ export class AI {
}
}

const { contextArray, replyArray, images } = await handleReply(ctx, msg, raw_reply, this.context);
const { contextArray, replyArray, images } = await handleReply(ctx, msg, this, raw_reply);

if (this.stream.id !== id) {
return;
Expand Down Expand Up @@ -345,7 +346,7 @@ export class AIManager {
if (key === "memory") {
return Memory.reviver(value);
}
if (key === "image") {
if (key === "imageManager") {
return ImageManager.reviver(value);
}

Expand Down
11 changes: 9 additions & 2 deletions src/AI/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ToolCall } from "../tool/tool";
import { ConfigManager } from "../config/config";
import { Image } from "./image";
import { Image, ImageManager } from "./image";
import { createCtx, createMsg } from "../utils/utils_seal";
import { levenshteinDistance } from "../utils/utils_string";
import { AI, AIManager } from "./AI";
Expand Down Expand Up @@ -374,7 +374,7 @@ export class Context {
return names;
}

findImage(id: string): Image {
findImage(id: string, im: ImageManager): Image | null {
if (/^[0-9a-z]{6}$/.test(id.trim())) {
const messages = this.messages;
for (let i = messages.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -407,6 +407,13 @@ export class Context {
return new Image(localImages[id]);
}

const savedImage = im.savedImages.find(img => img.id === id);
if (savedImage) {
const filePath = seal.base64ToImage(savedImage.base64);
savedImage.file = filePath;
return savedImage;
}

return null;
}
}
64 changes: 44 additions & 20 deletions src/AI/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,36 @@ export class Image {
id: string;
isUrl: boolean;
file: string;
scenes: string[];
base64: string;
content: string;
weight: number;

constructor(file: string) {
this.id = generateId();
this.isUrl = file.startsWith('http');
this.file = file;
this.scenes = [];
this.base64 = '';
this.content = '';
this.weight = 1;
}
}

export class ImageManager {
imageList: Image[];
stolenImages: Image[];
savedImages: Image[];
stealStatus: boolean;

constructor() {
this.imageList = [];
this.stolenImages = [];
this.savedImages = [];
this.stealStatus = false;
}

static reviver(value: any): ImageManager {
const im = new ImageManager();
const validKeys = ['imageList', 'stealStatus'];
const validKeys = ['stolenImages', 'savedImages', 'stealStatus'];

for (const k of validKeys) {
if (value.hasOwnProperty(k)) {
Expand All @@ -39,9 +47,24 @@ export class ImageManager {
return im;
}

updateImageList(images: Image[]) {
const { maxImageNum } = ConfigManager.image;
this.imageList = this.imageList.concat(images.filter(item => item.isUrl)).slice(-maxImageNum);
updateStolenImages(images: Image[]) {
const { maxStolenImageNum } = ConfigManager.image;
this.stolenImages = this.stolenImages.concat(images.filter(item => item.isUrl)).slice(-maxStolenImageNum);
}

updateSavedImages(images: Image[]) {
const { maxSavedImageNum } = ConfigManager.image;
this.savedImages = this.savedImages.concat(images.filter(item => item.isUrl));

if (this.savedImages.length > maxSavedImageNum) {
this.savedImages = this.savedImages
.sort((a, b) => b.weight - a.weight)
.slice(0, maxSavedImageNum);
}
}

delSavedImage(nameList: string[]) {
this.savedImages = this.savedImages.filter(img => !nameList.includes(img.id));
}

drawLocalImageFile(): string {
Expand Down Expand Up @@ -72,12 +95,12 @@ export class ImageManager {
}

async drawStolenImageFile(): Promise<string> {
if (this.imageList.length == 0) {
if (this.stolenImages.length === 0) {
return '';
}

const index = Math.floor(Math.random() * this.imageList.length);
const image = this.imageList.splice(index, 1)[0];
const index = Math.floor(Math.random() * this.stolenImages.length);
const image = this.stolenImages.splice(index, 1)[0];
const url = image.file;

if (!await ImageManager.checkImageUrl(url)) {
Expand All @@ -88,6 +111,13 @@ export class ImageManager {
return url;
}

drawSavedImageFile(): string {
if (this.savedImages.length === 0) return null;
const index = Math.floor(Math.random() * this.savedImages.length);
const image = this.savedImages[index];
return seal.base64ToImage(image.base64);
}

async drawImageFile(): Promise<string> {
const { localImagePaths } = ConfigManager.image;
const localImages: { [key: string]: string } = localImagePaths.reduce((acc: { [key: string]: string }, path: string) => {
Expand All @@ -108,24 +138,18 @@ export class ImageManager {
}, {});

const values = Object.values(localImages);
if (this.imageList.length == 0 && values.length == 0) {
if (this.stolenImages.length == 0 && values.length == 0 && this.savedImages.length == 0) {
return '';
}

const index = Math.floor(Math.random() * (values.length + this.imageList.length));
const index = Math.floor(Math.random() * (values.length + this.stolenImages.length + this.savedImages.length));

if (index < values.length) {
return values[index];
} else if (index < values.length + this.stolenImages.length) {
return await this.drawStolenImageFile();
} else {
const image = this.imageList.splice(index - values.length, 1)[0];
const url = image.file;

if (!await ImageManager.checkImageUrl(url)) {
await new Promise(resolve => setTimeout(resolve, 500));
return await this.drawImageFile();
}

return url;
return this.drawSavedImageFile();
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/AI/update.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 版本更新日志,格式为 "版本号": "更新内容",版本号格式为 "x.y.z",按照时间顺序从新到旧排列。
export const updateInfo = {
"4.10.1": `- 修复了构建ctx时,isPrivate始终为0的问题`,
"4.10.1": `- 修复了构建ctx时,isPrivate始终为0的问题
- 新增保存图片功能`,
"4.10.0": `- 新增了全局待机模式配置项
- 修改了部分正则和部分默认配置项
- 修复了无法调用内置指令
Expand Down
4 changes: 3 additions & 1 deletion src/config/config_image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class ImageConfig {
seal.ext.registerOptionConfig(ImageConfig.ext, "识别图片时将url转换为base64", "永不", ["永不", "自动", "总是"], "解决大模型无法正常获取QQ图床图片的问题");
seal.ext.registerIntConfig(ImageConfig.ext, "图片最大回复字符数", 500);
seal.ext.registerIntConfig(ImageConfig.ext, "偷取图片存储上限", 50, "每个群聊或私聊单独储存");
seal.ext.registerIntConfig(ImageConfig.ext, "保存图片存储上限", 50, "每个群聊或私聊单独储存");
}

static get() {
Expand All @@ -36,7 +37,8 @@ export class ImageConfig {
defaultPrompt: seal.ext.getStringConfig(ImageConfig.ext, "图片识别默认prompt"),
urlToBase64: seal.ext.getOptionConfig(ImageConfig.ext, "识别图片时将url转换为base64"),
maxChars: seal.ext.getIntConfig(ImageConfig.ext, "图片最大回复字符数"),
maxImageNum: seal.ext.getIntConfig(ImageConfig.ext, "偷取图片存储上限")
maxStolenImageNum: seal.ext.getIntConfig(ImageConfig.ext, "偷取图片存储上限"),
maxSavedImageNum: seal.ext.getIntConfig(ImageConfig.ext, "保存图片存储上限")
}
}
}
6 changes: 3 additions & 3 deletions src/config/config_message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ export class MessageConfig {
- <|img:xxxxxx|>为图片,其中xxxxxx为6位的图片id,如果要发送出现过的图片请使用<|img:xxxxxx|>的格式
{{/if}}
{{else}}
{{#if 本地图片不为空}}
{{#if 可发送图片不为空}}

## 图片相关
{{/if}}
{{/if}}
{{#if 本地图片不为空}}
- 可使用<|img:图片名称|>发送表情包,表情名称有:{{{本地图片名称}}}
{{#if 可发送图片不为空}}
- 可使用<|img:图片名称|>发送表情包,表情名称有:{{{可发送图片列表}}}
{{/if}}
{{#if 开启长期记忆}}

Expand Down
Loading