koishi-plugin-best-cave 2.7.8 → 2.7.10

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.
Files changed (2) hide show
  1. package/lib/index.js +47 -40
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -722,10 +722,11 @@ var HashManager = class {
722
722
  if (requireAdmin(session, this.config)) return requireAdmin(session, this.config);
723
723
  try {
724
724
  const allCaves = await this.ctx.database.get("cave", { status: "active" });
725
- if (allCaves.length === 0) return "无需补全回声洞哈希";
726
- await session.send(`开始补全 ${allCaves.length} 个回声洞的哈希...`);
727
- const existingHashes = await this.ctx.database.get("cave_hash", {});
728
- const existingHashSet = new Set(existingHashes.map((h4) => `${h4.cave}-${h4.hash}-${h4.type}`));
725
+ const existingHashes = await this.ctx.database.get("cave_hash", {}, { fields: ["cave"] });
726
+ const hashedCaveIds = new Set(existingHashes.map((h4) => h4.cave));
727
+ const cavesToProcess = allCaves.filter((cave2) => !hashedCaveIds.has(cave2.id));
728
+ if (cavesToProcess.length === 0) return "无需补全回声洞哈希";
729
+ await session.send(`开始补全 ${cavesToProcess.length} 个回声洞的哈希...`);
729
730
  let hashesToInsert = [];
730
731
  let processedCaveCount = 0;
731
732
  let totalHashesGenerated = 0;
@@ -734,19 +735,15 @@ var HashManager = class {
734
735
  if (hashesToInsert.length === 0) return;
735
736
  await this.ctx.database.upsert("cave_hash", hashesToInsert);
736
737
  totalHashesGenerated += hashesToInsert.length;
737
- this.logger.info(`[${processedCaveCount}/${allCaves.length}] 正在导入 ${hashesToInsert.length} 条回声洞哈希...`);
738
+ this.logger.info(`[${processedCaveCount}/${cavesToProcess.length}] 正在导入 ${hashesToInsert.length} 条回声洞哈希...`);
738
739
  hashesToInsert = [];
739
740
  }, "flushBatch");
740
- for (const cave2 of allCaves) {
741
+ for (const cave2 of cavesToProcess) {
741
742
  processedCaveCount++;
742
743
  try {
743
744
  const newHashesForCave = await this.generateAllHashesForCave(cave2);
744
- for (const hashObj of newHashesForCave) {
745
- const uniqueKey = `${hashObj.cave}-${hashObj.hash}-${hashObj.type}`;
746
- if (!existingHashSet.has(uniqueKey)) {
747
- hashesToInsert.push(hashObj);
748
- existingHashSet.add(uniqueKey);
749
- }
745
+ if (newHashesForCave.length > 0) {
746
+ hashesToInsert.push(...newHashesForCave);
750
747
  }
751
748
  if (hashesToInsert.length >= 100) await flushBatch();
752
749
  } catch (error) {
@@ -755,9 +752,10 @@ var HashManager = class {
755
752
  }
756
753
  }
757
754
  await flushBatch();
758
- return `已补全 ${allCaves.length} 个回声洞的 ${totalHashesGenerated} 条哈希(失败 ${errorCount} 条)`;
755
+ const successCount = processedCaveCount - errorCount;
756
+ return `已补全 ${successCount} 个回声洞的 ${totalHashesGenerated} 条哈希(失败 ${errorCount} 条)`;
759
757
  } catch (error) {
760
- this.logger.error("生成哈希失败:", error);
758
+ this.logger.error("补全哈希失败:", error);
761
759
  return `操作失败: ${error.message}`;
762
760
  }
763
761
  });
@@ -821,13 +819,13 @@ var HashManager = class {
821
819
  if (requireAdmin(session, this.config)) return requireAdmin(session, this.config);
822
820
  let cavesToProcess;
823
821
  try {
822
+ await session.send("正在修复,请稍候...");
824
823
  if (ids.length === 0) {
825
- await session.send("正在修复,请稍候...");
826
824
  cavesToProcess = await this.ctx.database.get("cave", { status: "active" });
827
825
  } else {
828
826
  cavesToProcess = await this.ctx.database.get("cave", { id: { $in: ids }, status: "active" });
829
827
  }
830
- if (!cavesToProcess.length) return "无可修复回声洞";
828
+ if (!cavesToProcess.length) return "无可修复的回声洞";
831
829
  let fixedFiles = 0;
832
830
  let errorCount = 0;
833
831
  const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
@@ -1101,10 +1099,13 @@ var AIManager = class {
1101
1099
  const formatContent = /* @__PURE__ */ __name((elements) => elements.filter((el) => el.type === "text").map((el) => el.content).join(" "), "formatContent");
1102
1100
  const userMessage = {
1103
1101
  role: "user",
1104
- content: JSON.stringify({
1105
- new_content: { text: formatContent(newElements) },
1106
- existing_contents: potentialDuplicates.map((cave) => ({ id: cave.id, text: formatContent(cave.elements) }))
1107
- })
1102
+ content: [{
1103
+ type: "input_text",
1104
+ text: JSON.stringify({
1105
+ new_content: { text: formatContent(newElements) },
1106
+ existing_contents: potentialDuplicates.map((cave) => ({ id: cave.id, text: formatContent(cave.elements) }))
1107
+ })
1108
+ }]
1108
1109
  };
1109
1110
  const response = await this.requestAI([userMessage], this.config.aiCheckPrompt, this.config.aiCheckSchema);
1110
1111
  return {
@@ -1148,7 +1149,7 @@ var AIManager = class {
1148
1149
  async getAnalysis(elements, mediaToSave, mediaBuffers) {
1149
1150
  const userContent = [];
1150
1151
  const combinedText = elements.filter((el) => el.type === "text" && el.content).map((el) => el.content).join("\n");
1151
- if (combinedText.trim()) userContent.push({ type: "text", text: combinedText });
1152
+ if (combinedText.trim()) userContent.push({ type: "input_text", text: combinedText });
1152
1153
  const mediaMap = new Map(mediaBuffers?.map((m) => [m.fileName, m.buffer]));
1153
1154
  const imageElements = elements.filter((el) => el.type === "image" && el.file);
1154
1155
  for (const el of imageElements) {
@@ -1165,8 +1166,8 @@ var AIManager = class {
1165
1166
  if (buffer) {
1166
1167
  const mimeType = path3.extname(el.file).toLowerCase() === ".png" ? "image/png" : "image/jpeg";
1167
1168
  userContent.push({
1168
- type: "image_url",
1169
- image_url: { url: `data:${mimeType};base64,${buffer.toString("base64")}` }
1169
+ type: "input_image",
1170
+ image_url: `data:${mimeType};base64,${buffer.toString("base64")}`
1170
1171
  });
1171
1172
  }
1172
1173
  } catch (error) {
@@ -1197,22 +1198,28 @@ var AIManager = class {
1197
1198
  this.rateLimitResetTime = Date.now() + 6e4;
1198
1199
  this.requestCount = 0;
1199
1200
  }
1200
- let schema = JSON.parse(schemaString);
1201
- const toolName = "extract_data";
1201
+ let schema;
1202
+ try {
1203
+ schema = JSON.parse(schemaString);
1204
+ } catch (error) {
1205
+ this.logger.error("解析 JSON Schema 失败:", error);
1206
+ throw new Error("无效的 JSON Schema 配置");
1207
+ }
1202
1208
  const payload = {
1203
1209
  model: this.config.aiModel,
1204
- messages: [{ role: "system", content: systemPrompt }, ...messages],
1205
- tools: [{
1206
- type: "function",
1207
- function: {
1208
- name: toolName,
1209
- description: "根据提供的内容提取或分析信息。",
1210
- parameters: schema
1210
+ input: [
1211
+ { role: "system", content: systemPrompt },
1212
+ ...messages
1213
+ ],
1214
+ text: {
1215
+ format: {
1216
+ type: "json_schema",
1217
+ name: "extracted_data",
1218
+ schema
1211
1219
  }
1212
- }],
1213
- tool_choice: { type: "function", function: { name: toolName } }
1220
+ }
1214
1221
  };
1215
- const fullUrl = `${this.config.aiEndpoint.replace(/\/$/, "")}/chat/completions`;
1222
+ const fullUrl = `${this.config.aiEndpoint.replace(/\/$/, "")}/responses`;
1216
1223
  const headers = {
1217
1224
  "Content-Type": "application/json",
1218
1225
  "Authorization": `Bearer ${this.config.aiApiKey}`
@@ -1220,9 +1227,9 @@ var AIManager = class {
1220
1227
  try {
1221
1228
  this.requestCount++;
1222
1229
  const response = await this.http.post(fullUrl, payload, { headers, timeout: 9e4 });
1223
- const toolCall = response.choices?.[0]?.message?.tool_calls?.[0];
1224
- if (toolCall?.function?.arguments) {
1225
- return JSON.parse(toolCall.function.arguments);
1230
+ const responseText = response.output?.[0]?.content?.[0]?.text;
1231
+ if (responseText) {
1232
+ return JSON.parse(responseText);
1226
1233
  } else {
1227
1234
  this.logger.error("AI 响应格式不正确:", JSON.stringify(response));
1228
1235
  throw new Error("AI 响应格式不正确");
@@ -1262,8 +1269,8 @@ var Config = import_koishi3.Schema.intersect([
1262
1269
  import_koishi3.Schema.object({
1263
1270
  enablePend: import_koishi3.Schema.boolean().default(false).description("启用审核"),
1264
1271
  enableSimilarity: import_koishi3.Schema.boolean().default(false).description("启用查重"),
1265
- textThreshold: import_koishi3.Schema.number().min(0).max(100).step(0.01).default(90).description("文本相似度阈值 (%)"),
1266
- imageThreshold: import_koishi3.Schema.number().min(0).max(100).step(0.01).default(90).description("图片相似度阈值 (%)")
1272
+ textThreshold: import_koishi3.Schema.number().min(0).max(100).step(0.01).default(95).description("文本相似度阈值 (%)"),
1273
+ imageThreshold: import_koishi3.Schema.number().min(0).max(100).step(0.01).default(95).description("图片相似度阈值 (%)")
1267
1274
  }).description("复核配置"),
1268
1275
  import_koishi3.Schema.object({
1269
1276
  enableAI: import_koishi3.Schema.boolean().default(false).description("启用 AI"),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-best-cave",
3
3
  "description": "功能强大、高度可定制的回声洞。支持丰富的媒体类型、内容查重、人工审核、用户昵称、数据迁移以及本地/S3 双重文件存储后端。",
4
- "version": "2.7.8",
4
+ "version": "2.7.10",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],