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
2 changes: 1 addition & 1 deletion header.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// @name AI骰娘4
// @author 错误、白鱼
// @version 4.10.1
// @description 适用于大部分OpenAI API兼容格式AI的模型插件,测试环境为 Deepseek AI (https://platform.deepseek.com/),用于与 AI 进行对话,并根据特定关键词触发回复。使用.ai help查看使用方法。具体配置查看插件配置项。\nopenai标准下的function calling功能已进行适配,选用模型若不支持该功能,可以开启迁移到提示词工程的开关,即可使用调用函数功能。\n交流答疑QQ群:940049120
// @description 适用于大部分OpenAI API兼容格式AI的模型插件,测试环境为 Deepseek AI (https://platform.deepseek.com/),用于与 AI 进行对话,并根据特定关键词触发回复。使用.ai help查看使用方法。具体配置查看插件配置项。\nopenai标准下的function calling功能已进行适配,选用模型若不支持该功能,可以开启迁移到提示词工程的开关,即可使用调用函数功能。\n交流答疑QQ群:143412516
// @timestamp 1733387279
// 2024-12-05 16:27:59
// @license MIT
Expand Down
8 changes: 3 additions & 5 deletions src/AI/AI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ export class AI {
return;
}

const timeout = setTimeout(() => {
logger.warning(this.id, `处理消息超时`);
}, 60 * 1000);

let result = {
contextArray: [],
replyArray: [],
Expand Down Expand Up @@ -185,7 +181,6 @@ export class AI {
}
}

clearTimeout(timeout);
AIManager.saveAI(this.id);
}

Expand All @@ -196,6 +191,9 @@ export class AI {

const messages = handleMessages(ctx, this);
const id = await startStream(messages);
if (id === '') {
return;
}

this.stream.id = id;
let status = 'processing';
Expand Down
4 changes: 2 additions & 2 deletions src/AI/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ export class Memory {
time: new Date().toLocaleString(),
createTime: Math.floor(Date.now() / 1000),
lastMentionTime: Math.floor(Date.now() / 1000),
keywords: kws,
content: content,
keywords: kws || [],
content: content || '',
weight: 0
};

Expand Down
2 changes: 1 addition & 1 deletion src/config/config_backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class BackendConfig {
seal.ext.registerStringConfig(BackendConfig.ext, "图片转base64", "https://urltobase64.fishwhite.top", '可自行搭建');
seal.ext.registerStringConfig(BackendConfig.ext, "联网搜索", "https://searxng.fishwhite.top", '可自行搭建');
seal.ext.registerStringConfig(BackendConfig.ext, "网页读取", "https://webread.fishwhite.top", '可自行搭建');
seal.ext.registerStringConfig(BackendConfig.ext, "用量图表", "http://localhost:3009", '可自行搭建');
seal.ext.registerStringConfig(BackendConfig.ext, "用量图表", "http://chat.error2913.com", '可自行搭建');
}

static get() {
Expand Down
4 changes: 3 additions & 1 deletion src/config/config_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ export class RequestConfig {
`"temperature":1`,
`"top_p":1`
], "messages,tools,tool_choice不存在时,将会自动替换。具体参数请参考你所使用模型的接口文档");
seal.ext.registerIntConfig(RequestConfig.ext, "请求超时时限/ms", 180000, '');
}

static get() {
return {
url: seal.ext.getStringConfig(RequestConfig.ext, "url地址"),
apiKey: seal.ext.getStringConfig(RequestConfig.ext, "API Key"),
bodyTemplate: seal.ext.getTemplateConfig(RequestConfig.ext, "body")
bodyTemplate: seal.ext.getTemplateConfig(RequestConfig.ext, "body"),
timeout: seal.ext.getIntConfig(RequestConfig.ext, "请求超时时限/ms")
}
}
}
44 changes: 23 additions & 21 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ import { ConfigManager } from "./config/config";
import { handleMessages, parseBody } from "./utils/utils_message";
import { ImageManager } from "./AI/image";
import { logger } from "./logger";
import { withTimeout } from "./utils/utils";

export async function sendChatRequest(ctx: seal.MsgContext, msg: seal.Message, ai: AI, messages: {
role: string,
content: string,
tool_calls?: ToolCall[],
tool_call_id?: string
}[], tool_choice: string): Promise<string> {
const { url, apiKey, bodyTemplate } = ConfigManager.request;
const { url, apiKey, bodyTemplate, timeout } = ConfigManager.request;
const { isTool, usePromptEngineering } = ConfigManager.tool;
const tools = ai.tool.getToolsInfo(msg.messageType);

try {
const bodyObject = parseBody(bodyTemplate, messages, tools, tool_choice);
const time = Date.now();

const data = await fetchData(url, apiKey, bodyObject);
const data = await withTimeout(() => fetchData(url, apiKey, bodyObject), timeout);

if (data.choices && data.choices.length > 0) {
AIManager.updateUsage(data.model, data.usage);
Expand All @@ -44,7 +45,7 @@ export async function sendChatRequest(ctx: seal.MsgContext, msg: seal.Message, a
try {
await ToolManager.handlePromptToolCall(ctx, msg, ai, match[1]);
} catch (e) {
logger.error(`在handlePromptToolCall中出错`, e.message);
logger.error(`在handlePromptToolCall中出错:`, e.message);
return '';
}

Expand All @@ -61,7 +62,7 @@ export async function sendChatRequest(ctx: seal.MsgContext, msg: seal.Message, a
try {
tool_choice = await ToolManager.handleToolCalls(ctx, msg, ai, message.tool_calls);
} catch (e) {
logger.error(`在handleToolCalls中出错`, e.message);
logger.error(`在handleToolCalls中出错:`, e.message);
return '';
}

Expand All @@ -75,8 +76,8 @@ export async function sendChatRequest(ctx: seal.MsgContext, msg: seal.Message, a
} else {
throw new Error(`服务器响应中没有choices或choices为空\n响应体:${JSON.stringify(data, null, 2)}`);
}
} catch (error) {
logger.error("在sendChatRequest中出错", error);
} catch (e) {
logger.error("在sendChatRequest中出错:", e.message);
return '';
}
}
Expand All @@ -89,13 +90,14 @@ export async function sendITTRequest(messages: {
text?: string
}[]
}[], useBase64: boolean): Promise<string> {
const { timeout } = ConfigManager.request;
const { url, apiKey, bodyTemplate, urlToBase64 } = ConfigManager.image;

try {
const bodyObject = parseBody(bodyTemplate, messages, null, null);
const time = Date.now();

const data = await fetchData(url, apiKey, bodyObject);
const data = await withTimeout(() => fetchData(url, apiKey, bodyObject), timeout);

if (data.choices && data.choices.length > 0) {
AIManager.updateUsage(data.model, data.usage);
Expand All @@ -109,8 +111,8 @@ export async function sendITTRequest(messages: {
} else {
throw new Error(`服务器响应中没有choices或choices为空\n响应体:${JSON.stringify(data, null, 2)}`);
}
} catch (error) {
logger.error("在sendITTRequest中请求出错", error);
} catch (e) {
logger.error("在sendITTRequest中请求出错:", e.message);
if (urlToBase64 === '自动' && !useBase64) {
logger.info(`自动尝试使用转换为base64`);

Expand Down Expand Up @@ -173,15 +175,15 @@ export async function fetchData(url: string, apiKey: string, bodyObject: any): P
}
return data;
} catch (e) {
throw new Error(`解析响应体时出错:${e}\n响应体:${text}`);
throw new Error(`解析响应体时出错:${e.message}\n响应体:${text}`);
}
}

export async function startStream(messages: {
role: string,
content: string
}[]): Promise<string> {
const { url, apiKey, bodyTemplate } = ConfigManager.request;
const { url, apiKey, bodyTemplate, timeout } = ConfigManager.request;
const { streamUrl } = ConfigManager.backend;

try {
Expand All @@ -196,7 +198,7 @@ export async function startStream(messages: {
});
logger.info(`请求发送前的上下文:\n`, s);

const response = await fetch(`${streamUrl}/start`, {
const response = await withTimeout(() => fetch(`${streamUrl}/start`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
Expand All @@ -207,7 +209,7 @@ export async function startStream(messages: {
api_key: apiKey,
body_obj: bodyObject
})
});
}), timeout);

// logger.info("响应体", JSON.stringify(response, null, 2));

Expand All @@ -231,8 +233,8 @@ export async function startStream(messages: {
} catch (e) {
throw new Error(`解析响应体时出错:${e}\n响应体:${text}`);
}
} catch (error) {
logger.error("在startStream中出错", error);
} catch (e) {
logger.error("在startStream中出错:", e.message);
return '';
}
}
Expand Down Expand Up @@ -274,8 +276,8 @@ export async function pollStream(id: string, after: number): Promise<{ status: s
} catch (e) {
throw new Error(`解析响应体时出错:${e}\n响应体:${text}`);
}
} catch (error) {
logger.error("在pollStream中出错", error);
} catch (e) {
logger.error("在pollStream中出错:", e.message);
return { status: 'failed', reply: '', nextAfter: 0 };
}
}
Expand Down Expand Up @@ -317,8 +319,8 @@ export async function endStream(id: string): Promise<string> {
} catch (e) {
throw new Error(`解析响应体时出错:${e}\n响应体:${text}`);
}
} catch (error) {
logger.error("在endStream中出错", error);
} catch (e) {
logger.error("在endStream中出错:", e.message);
return '';
}
}
Expand Down Expand Up @@ -360,8 +362,8 @@ export async function get_chart_url(chart_type: string, usage_data: {
} catch (e) {
throw new Error(`解析响应体时出错:${e}\n响应体:${text}`);
}
} catch (error) {
logger.error("在get_chart_url中请求出错", error);
} catch (e) {
logger.error("在get_chart_url中请求出错:", e.message);
return '';
}
}
2 changes: 1 addition & 1 deletion src/tool/tool_memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function registerAddMemory() {
}

//记忆相关处理
ai.memory.addMemory(ctx, keywords, content);
ai.memory.addMemory(ctx, Array.isArray(keywords) ? keywords : [], content);
AIManager.saveAI(ai.id);

return `添加记忆成功`;
Expand Down
5 changes: 3 additions & 2 deletions src/update.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// 版本更新日志,格式为 "版本号": "更新内容",版本号格式为 "x.y.z",按照时间顺序从新到旧排列。
export const updateInfo = {
"4.10.1": `
- 可能修复了非指令无法响应的问题
"4.10.2":`- 新增请求超时相关
- 修复addMemory时,keywords可以为null的问题`,
"4.10.1": `- 可能修复了非指令无法响应的问题
- 修复了构建ctx时,isPrivate始终为0的问题
- 新增保存图片功能
- 重构了定时任务的执行
Expand Down
9 changes: 9 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,13 @@ export async function replyToSender(ctx: seal.MsgContext, msg: seal.Message, ai:
seal.replyToSender(ctx, msg, s);
return '';
}
}

export function withTimeout<T>(asyncFunc: () => Promise<T>, timeoutMs: number): Promise<T> {
return Promise.race([
asyncFunc(),
new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(`操作超时 (${timeoutMs}ms)`)), timeoutMs);
})
]);
}