koishi-plugin-best-cave 2.7.26 → 2.7.28
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/lib/AIManager.d.ts +0 -10
- package/lib/index.js +50 -71
- package/package.json +1 -1
package/lib/AIManager.d.ts
CHANGED
|
@@ -40,15 +40,6 @@ export declare class AIManager {
|
|
|
40
40
|
* @param {any} cave - 主命令的实例,用于挂载子命令。
|
|
41
41
|
*/
|
|
42
42
|
registerCommands(cave: any): void;
|
|
43
|
-
/**
|
|
44
|
-
* @description 递归处理和分析回声洞批次,失败时按 1/5 拆分以定位问题。
|
|
45
|
-
* @param {CaveObject[]} caves - 当前要处理的回声洞对象数组。
|
|
46
|
-
* @param {number} totalCaves - 要分析的回声洞总数。
|
|
47
|
-
* @param {{ count: number }} progress - 用于跟踪总体进度的计数器对象。
|
|
48
|
-
* @returns {Promise<number>} 成功分析的回声洞数量。
|
|
49
|
-
* @private
|
|
50
|
-
*/
|
|
51
|
-
private processCaveBatch;
|
|
52
43
|
/**
|
|
53
44
|
* @description 对新提交的内容执行 AI 驱动的查重检查。
|
|
54
45
|
* @param {StoredElement[]} newElements - 新提交的内容元素数组。
|
|
@@ -78,7 +69,6 @@ export declare class AIManager {
|
|
|
78
69
|
* @param {CaveObject} caveA - 第一个回声洞对象。
|
|
79
70
|
* @param {CaveObject} caveB - 第二个回声洞对象。
|
|
80
71
|
* @returns {Promise<boolean>} 如果内容被 AI 判断为重复,则返回 true,否则返回 false。
|
|
81
|
-
* @throws {Error} 当 AI 请求失败时抛出。
|
|
82
72
|
* @private
|
|
83
73
|
*/
|
|
84
74
|
private isContentDuplicateAI;
|
package/lib/index.js
CHANGED
|
@@ -1154,16 +1154,29 @@ var AIManager = class {
|
|
|
1154
1154
|
if (cavesToAnalyze.length === 0) return "无需分析回声洞";
|
|
1155
1155
|
await session.send(`开始分析 ${cavesToAnalyze.length} 个回声洞...`);
|
|
1156
1156
|
let successCount = 0;
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
for (let i = 0; i < cavesToAnalyze.length; i += batchSize) {
|
|
1161
|
-
const batch = cavesToAnalyze.slice(i, i + batchSize);
|
|
1157
|
+
let failedCount = 0;
|
|
1158
|
+
for (let i = 0; i < cavesToAnalyze.length; i += 25) {
|
|
1159
|
+
const batch = cavesToAnalyze.slice(i, i + 25);
|
|
1162
1160
|
this.logger.info(`[${i + 1}/${cavesToAnalyze.length}] 正在分析 ${batch.length} 个回声洞...`);
|
|
1163
|
-
|
|
1161
|
+
const analysisPromises = batch.map((cave2) => this.analyze([cave2]));
|
|
1162
|
+
const results = await Promise.allSettled(analysisPromises);
|
|
1163
|
+
const successfulAnalyses = [];
|
|
1164
|
+
for (let j = 0; j < results.length; j++) {
|
|
1165
|
+
const result = results[j];
|
|
1166
|
+
const cave2 = batch[j];
|
|
1167
|
+
if (result.status === "fulfilled" && result.value.length > 0) {
|
|
1168
|
+
successfulAnalyses.push(result.value[0]);
|
|
1169
|
+
} else {
|
|
1170
|
+
failedCount++;
|
|
1171
|
+
if (result.status === "rejected") this.logger.error(`分析回声洞(${cave2.id})失败:`, result.reason);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (successfulAnalyses.length > 0) {
|
|
1175
|
+
await this.ctx.database.upsert("cave_meta", successfulAnalyses);
|
|
1176
|
+
successCount += successfulAnalyses.length;
|
|
1177
|
+
}
|
|
1164
1178
|
}
|
|
1165
|
-
|
|
1166
|
-
if (failedCount > 0) return `已分析 ${successCount} 个回声洞(失败 ${failedCount} 个)`;
|
|
1179
|
+
return `已分析 ${successCount} 个回声洞(失败 ${failedCount} 个)`;
|
|
1167
1180
|
} catch (error) {
|
|
1168
1181
|
this.logger.error("分析回声洞失败:", error);
|
|
1169
1182
|
return `操作失败: ${error.message}`;
|
|
@@ -1177,8 +1190,12 @@ var AIManager = class {
|
|
|
1177
1190
|
if (allMeta.length < 2) return "无可比较数据";
|
|
1178
1191
|
const candidatePairs = generateFromLSH(allMeta, (meta) => ({ id: meta.cave, keys: meta.keywords }));
|
|
1179
1192
|
if (candidatePairs.size === 0) return "未发现相似内容";
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1193
|
+
const idsToCompare = /* @__PURE__ */ new Set();
|
|
1194
|
+
candidatePairs.forEach((pairKey) => {
|
|
1195
|
+
pairKey.split("-").map(Number).forEach((id) => idsToCompare.add(id));
|
|
1196
|
+
});
|
|
1197
|
+
const caveData = await this.ctx.database.get("cave", { id: { $in: Array.from(idsToCompare) }, status: "active" });
|
|
1198
|
+
const allCaves = new Map(caveData.map((c) => [c.id, c]));
|
|
1182
1199
|
const comparisonPromises = Array.from(candidatePairs).map(async (pairKey) => {
|
|
1183
1200
|
const [id1, id2] = pairKey.split("-").map(Number);
|
|
1184
1201
|
const cave1 = allCaves.get(id1);
|
|
@@ -1187,7 +1204,7 @@ var AIManager = class {
|
|
|
1187
1204
|
return null;
|
|
1188
1205
|
});
|
|
1189
1206
|
const results = await Promise.all(comparisonPromises);
|
|
1190
|
-
duplicatePairs
|
|
1207
|
+
const duplicatePairs = results.filter(Boolean);
|
|
1191
1208
|
if (duplicatePairs.length === 0) return "未发现高重复性的内容";
|
|
1192
1209
|
const dsu = new DSU();
|
|
1193
1210
|
const allIds = /* @__PURE__ */ new Set();
|
|
@@ -1217,38 +1234,6 @@ var AIManager = class {
|
|
|
1217
1234
|
}
|
|
1218
1235
|
});
|
|
1219
1236
|
}
|
|
1220
|
-
/**
|
|
1221
|
-
* @description 递归处理和分析回声洞批次,失败时按 1/5 拆分以定位问题。
|
|
1222
|
-
* @param {CaveObject[]} caves - 当前要处理的回声洞对象数组。
|
|
1223
|
-
* @param {number} totalCaves - 要分析的回声洞总数。
|
|
1224
|
-
* @param {{ count: number }} progress - 用于跟踪总体进度的计数器对象。
|
|
1225
|
-
* @returns {Promise<number>} 成功分析的回声洞数量。
|
|
1226
|
-
* @private
|
|
1227
|
-
*/
|
|
1228
|
-
async processCaveBatch(caves, totalCaves, progress) {
|
|
1229
|
-
if (caves.length === 0) return 0;
|
|
1230
|
-
this.logger.info(`[${progress.count + 1}/${totalCaves}] 正在分析回声洞(${caves.map((c) => c.id).join("|")})...`);
|
|
1231
|
-
try {
|
|
1232
|
-
const analyses = await this.analyze(caves);
|
|
1233
|
-
if (analyses.length > 0) await this.ctx.database.upsert("cave_meta", analyses);
|
|
1234
|
-
progress.count += caves.length;
|
|
1235
|
-
return analyses.length;
|
|
1236
|
-
} catch (error) {
|
|
1237
|
-
if (caves.length > 1) {
|
|
1238
|
-
const subBatches = [];
|
|
1239
|
-
const subBatchSize = Math.ceil(caves.length / 5);
|
|
1240
|
-
for (let i = 0; i < caves.length; i += subBatchSize) subBatches.push(caves.slice(i, i + subBatchSize));
|
|
1241
|
-
const processingPromises = subBatches.map((subBatch) => this.processCaveBatch(subBatch, totalCaves, progress));
|
|
1242
|
-
const results = await Promise.all(processingPromises);
|
|
1243
|
-
return results.reduce((sum, count) => sum + count, 0);
|
|
1244
|
-
} else {
|
|
1245
|
-
const failedCave = caves[0];
|
|
1246
|
-
progress.count++;
|
|
1247
|
-
this.logger.error(`[${progress.count}/${totalCaves}] 分析回声洞(${failedCave.id})失败:`, error);
|
|
1248
|
-
return 0;
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
1237
|
/**
|
|
1253
1238
|
* @description 对新提交的内容执行 AI 驱动的查重检查。
|
|
1254
1239
|
* @param {StoredElement[]} newElements - 新提交的内容元素数组。
|
|
@@ -1310,12 +1295,14 @@ ${combinedText}` });
|
|
|
1310
1295
|
contentForAI.push(...images);
|
|
1311
1296
|
const userMessage = { role: "user", content: contentForAI };
|
|
1312
1297
|
const response = await this.requestAI([userMessage], this.ANALYSIS_SYSTEM_PROMPT);
|
|
1313
|
-
if (response)
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1298
|
+
if (response) {
|
|
1299
|
+
return {
|
|
1300
|
+
cave: cave.id,
|
|
1301
|
+
keywords: response.keywords || [],
|
|
1302
|
+
description: response.description || "",
|
|
1303
|
+
rating: Math.max(0, Math.min(100, response.rating || 0))
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1319
1306
|
return null;
|
|
1320
1307
|
});
|
|
1321
1308
|
const results = await Promise.all(analysisPromises);
|
|
@@ -1326,7 +1313,6 @@ ${combinedText}` });
|
|
|
1326
1313
|
* @param {CaveObject} caveA - 第一个回声洞对象。
|
|
1327
1314
|
* @param {CaveObject} caveB - 第二个回声洞对象。
|
|
1328
1315
|
* @returns {Promise<boolean>} 如果内容被 AI 判断为重复,则返回 true,否则返回 false。
|
|
1329
|
-
* @throws {Error} 当 AI 请求失败时抛出。
|
|
1330
1316
|
* @private
|
|
1331
1317
|
*/
|
|
1332
1318
|
async isContentDuplicateAI(caveA, caveB) {
|
|
@@ -1379,28 +1365,21 @@ ${combinedText}` });
|
|
|
1379
1365
|
"Content-Type": "application/json",
|
|
1380
1366
|
"Authorization": `Bearer ${this.config.aiApiKey}`
|
|
1381
1367
|
};
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
return JSON.parse(candidate);
|
|
1396
|
-
} catch (parseError) {
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
this.logger.error("解析失败", "原始响应:", JSON.stringify(response, null, 2));
|
|
1400
|
-
throw new Error();
|
|
1401
|
-
} catch (e) {
|
|
1402
|
-
throw e;
|
|
1368
|
+
const response = await this.http.post(fullUrl, payload, { headers, timeout: 6e5 });
|
|
1369
|
+
const content = response?.choices?.[0]?.message?.content;
|
|
1370
|
+
if (!content?.trim()) throw new Error();
|
|
1371
|
+
const candidates = [];
|
|
1372
|
+
const jsonBlockMatch = content.match(/```json\s*([\s\S]*?)\s*```/i);
|
|
1373
|
+
if (jsonBlockMatch && jsonBlockMatch[1]) candidates.push(jsonBlockMatch[1]);
|
|
1374
|
+
candidates.push(content);
|
|
1375
|
+
const firstBrace = content.indexOf("{");
|
|
1376
|
+
const lastBrace = content.lastIndexOf("}");
|
|
1377
|
+
if (firstBrace !== -1 && lastBrace > firstBrace) candidates.push(content.substring(firstBrace, lastBrace + 1));
|
|
1378
|
+
for (const candidate of [...new Set(candidates)]) try {
|
|
1379
|
+
return JSON.parse(candidate);
|
|
1380
|
+
} catch (parseError) {
|
|
1403
1381
|
}
|
|
1382
|
+
this.logger.error("原始响应:", JSON.stringify(response, null, 2));
|
|
1404
1383
|
}
|
|
1405
1384
|
};
|
|
1406
1385
|
|