koishi-plugin-chatluna 1.3.6 → 1.3.8

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/index.cjs CHANGED
@@ -3249,7 +3249,7 @@ __name(apply24, "apply");
3249
3249
  var import_logger3 = require("koishi-plugin-chatluna/utils/logger");
3250
3250
  var import_crypto4 = require("crypto");
3251
3251
  var logger6;
3252
- var batches = /* @__PURE__ */ new Map();
3252
+ var queues = /* @__PURE__ */ new Map();
3253
3253
  function apply25(ctx, config, chain) {
3254
3254
  logger6 = (0, import_logger3.createLogger)(ctx);
3255
3255
  chain.middleware("message_delay", async (session, context) => {
@@ -3261,171 +3261,162 @@ function apply25(ctx, config, chain) {
3261
3261
  const conversationId = room.conversationId;
3262
3262
  const userName = inputMessage.name || "unknown";
3263
3263
  const messageId = context.options.messageId;
3264
- const batch = batches.get(conversationId);
3265
- if (!batch) {
3264
+ const conversation = queues.get(conversationId) ?? {
3265
+ turns: [],
3266
+ inFlight: false
3267
+ };
3268
+ queues.set(conversationId, conversation);
3269
+ const tailTurn = conversation.turns[conversation.turns.length - 1];
3270
+ let turn;
3271
+ if (tailTurn && tailTurn.state !== "processing" && tailTurn.userName === userName) {
3272
+ turn = tailTurn;
3266
3273
  logger6.debug(
3267
- `Creating new batch for ${conversationId}, messageId: ${messageId}`
3274
+ `Joining turn for ${conversationId}, messageId: ${messageId}, user: ${userName}, total: ${turn.messages.length + 1}`
3268
3275
  );
3269
- const newBatch = {
3270
- messages: [inputMessage],
3276
+ } else {
3277
+ const state = conversation.turns.length === 0 && config.messageQueueDelay > 0 ? "collecting" : "waiting";
3278
+ turn = {
3279
+ messages: [],
3271
3280
  userName,
3272
- resolveWaiters: []
3281
+ state
3273
3282
  };
3274
- batches.set(conversationId, newBatch);
3275
- if (config.messageQueueDelay > 0) {
3276
- return await new Promise(
3277
- (resolve) => {
3278
- newBatch.processorResolve = resolve;
3279
- newBatch.timeout = ctx.setTimeout(() => {
3280
- if (batches.get(conversationId) === newBatch) {
3281
- logger6.debug(
3282
- // eslint-disable-next-line max-len
3283
- `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, processing batch with ${newBatch.messages.length} messages`
3284
- );
3285
- context.options.inputMessage = mergeMessages(newBatch.messages);
3286
- batches.delete(conversationId);
3287
- resolve(2 /* CONTINUE */);
3288
- }
3289
- }, config.messageQueueDelay * 1e3);
3290
- }
3291
- );
3292
- }
3293
- return 2 /* CONTINUE */;
3294
- }
3295
- if (batch.userName !== userName) {
3283
+ conversation.turns.push(turn);
3296
3284
  logger6.debug(
3297
- `User mismatch for ${conversationId}, messageId: ${messageId}, waiting for batch completion`
3298
- );
3299
- return await waitForBatchCompletion(
3300
- batch,
3301
- conversationId,
3302
- inputMessage,
3303
- userName,
3304
- context
3285
+ `Creating new turn for ${conversationId}, messageId: ${messageId}, user: ${userName}, queue: ${conversation.turns.length}`
3305
3286
  );
3306
3287
  }
3307
- if (batch.resolveWaiters.length === 0) {
3308
- logger6.debug(
3309
- `Adding message to batch for ${conversationId}, messageId: ${messageId}, total: ${batch.messages.length + 1}`
3310
- );
3311
- batch.messages.push(inputMessage);
3312
- if (config.messageQueueDelay > 0 && batch.timeout && batch.processorResolve) {
3313
- batch.timeout();
3314
- batch.timeout = ctx.setTimeout(() => {
3315
- if (batches.get(conversationId) === batch && batch.processorResolve) {
3316
- logger6.debug(
3317
- `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, processing batch with ${batch.messages.length} messages`
3318
- );
3319
- context.options.inputMessage = mergeMessages(
3320
- batch.messages
3321
- );
3322
- batches.delete(conversationId);
3323
- batch.processorResolve(
3324
- 2 /* CONTINUE */
3325
- );
3326
- }
3327
- }, config.messageQueueDelay * 1e3);
3328
- }
3329
- return 1 /* STOP */;
3288
+ turn.messages.push(inputMessage);
3289
+ const statusPromise = awaitTurnStart(turn, context);
3290
+ if (turn.state === "collecting") {
3291
+ resetTurnTimeout(ctx, config, conversationId, turn);
3330
3292
  }
3331
- logger6.debug(
3332
- `Interrupting and merging for ${conversationId}, messageId: ${messageId}`
3333
- );
3334
- return await interruptAndMerge(batch, inputMessage, context);
3293
+ tryStartHeadTurn(conversationId, conversation);
3294
+ return await statusPromise;
3335
3295
  }).after("check_room").after("read_chat_message").before("lifecycle-handle_command");
3336
- const completeBatch = /* @__PURE__ */ __name(async (conversationId) => {
3337
- const batch = batches.get(conversationId);
3338
- if (batch?.resolveWaiters.length > 0) {
3296
+ const completeTurn = /* @__PURE__ */ __name(async (conversationId) => {
3297
+ const conversation = queues.get(conversationId);
3298
+ if (!conversation) {
3299
+ return;
3300
+ }
3301
+ const current = conversation.turns.shift();
3302
+ conversation.inFlight = false;
3303
+ if (current?.timeout) {
3304
+ current.timeout();
3305
+ }
3306
+ if (current?.starter) {
3307
+ current.starter.resolve(1 /* STOP */);
3308
+ current.starter = void 0;
3309
+ }
3310
+ if (conversation.turns.length === 0) {
3311
+ queues.delete(conversationId);
3312
+ return;
3313
+ }
3314
+ tryStartHeadTurn(conversationId, conversation);
3315
+ if (current) {
3339
3316
  logger6.debug(
3340
- `Completing batch for ${conversationId}, messages: ${batch.messages.length}`
3317
+ `Completing turn for ${conversationId}, remaining: ${conversation.turns.length}`
3341
3318
  );
3342
- if (batch.timeout) {
3343
- batch.timeout();
3344
- }
3345
- batch.resolveWaiters.forEach((resolve) => resolve());
3346
- batches.delete(conversationId);
3347
- } else if (batch) {
3348
- logger6.debug(`Cleaning up batch for ${conversationId}`);
3349
- if (batch.timeout) {
3350
- batch.timeout();
3351
- }
3352
- if (batch.processorResolve) {
3353
- batch.processorResolve(1 /* STOP */);
3354
- }
3355
- batches.delete(conversationId);
3356
3319
  }
3357
- }, "completeBatch");
3358
- ctx.on(
3359
- "chatluna/after-chat",
3360
- async (conversationId) => await completeBatch(conversationId)
3361
- );
3320
+ }, "completeTurn");
3321
+ ctx.on("chatluna/after-chat", completeTurn);
3362
3322
  ctx.on(
3363
3323
  "chatluna/after-chat-error",
3364
- async (_, conversationId) => await completeBatch(conversationId)
3324
+ (_, conversationId) => completeTurn(conversationId)
3365
3325
  );
3366
3326
  ctx.on("chatluna/clear-chat-history", async (conversationId) => {
3367
- const batch = batches.get(conversationId);
3368
- if (batch) {
3327
+ const conversation = queues.get(conversationId);
3328
+ if (conversation) {
3329
+ const stoppedWaiters = conversation.turns.filter(
3330
+ (turn) => !!turn.starter
3331
+ ).length;
3369
3332
  logger6.debug(
3370
- `Clearing chat history for ${conversationId}, stopping ${batch.resolveWaiters.length} waiters`
3371
- );
3372
- if (batch.timeout) {
3373
- batch.timeout();
3374
- }
3375
- batch.resolveWaiters.forEach(
3376
- (resolve) => resolve(1 /* STOP */)
3333
+ `Clearing chat history for ${conversationId}, stopping ${stoppedWaiters} waiters`
3377
3334
  );
3378
- if (batch.processorResolve) {
3379
- batch.processorResolve(1 /* STOP */);
3335
+ for (const turn of conversation.turns) {
3336
+ if (turn.timeout) {
3337
+ turn.timeout();
3338
+ turn.timeout = void 0;
3339
+ }
3340
+ if (turn.starter) {
3341
+ turn.starter.resolve(1 /* STOP */);
3342
+ turn.starter = void 0;
3343
+ }
3380
3344
  }
3381
- batches.delete(conversationId);
3345
+ queues.delete(conversationId);
3382
3346
  }
3383
3347
  });
3384
3348
  }
3385
3349
  __name(apply25, "apply");
3386
- async function interruptAndMerge(batch, message, context) {
3387
- const oldWaiters = batch.resolveWaiters;
3388
- const oldProcessor = batch.processorResolve;
3389
- batch.resolveWaiters = [];
3390
- batch.processorResolve = void 0;
3391
- batch.messages.push(message);
3392
- if (batch.timeout) {
3393
- batch.timeout();
3394
- batch.timeout = void 0;
3395
- }
3396
- oldWaiters.forEach((resolve) => resolve(1 /* STOP */));
3397
- if (oldProcessor) {
3398
- oldProcessor(1 /* STOP */);
3350
+ function awaitTurnStart(turn, context) {
3351
+ if (turn.starter) {
3352
+ turn.starter.resolve(1 /* STOP */);
3353
+ turn.starter = void 0;
3399
3354
  }
3400
3355
  return new Promise((resolve) => {
3401
- batch.resolveWaiters.push(() => {
3402
- context.options.inputMessage = mergeMessages(batch.messages);
3403
- resolve(2 /* CONTINUE */);
3404
- });
3356
+ turn.starter = { resolve, context };
3405
3357
  });
3406
3358
  }
3407
- __name(interruptAndMerge, "interruptAndMerge");
3408
- async function waitForBatchCompletion(batch, conversationId, message, userName, context) {
3409
- return new Promise((resolve) => {
3410
- batch.resolveWaiters.push(() => {
3411
- batches.set(conversationId, {
3412
- messages: [message],
3413
- userName,
3414
- resolveWaiters: [
3415
- () => {
3416
- const newBatch = batches.get(conversationId);
3417
- context.options.inputMessage = mergeMessages(
3418
- newBatch.messages
3419
- );
3420
- batches.delete(conversationId);
3421
- resolve(2 /* CONTINUE */);
3422
- }
3423
- ]
3424
- });
3425
- });
3426
- });
3359
+ __name(awaitTurnStart, "awaitTurnStart");
3360
+ function resetTurnTimeout(ctx, config, conversationId, turn) {
3361
+ if (turn.timeout) {
3362
+ turn.timeout();
3363
+ }
3364
+ turn.timeout = ctx.setTimeout(() => {
3365
+ const conversation = queues.get(conversationId);
3366
+ if (!conversation || conversation.turns[0] !== turn) {
3367
+ return;
3368
+ }
3369
+ turn.state = "waiting";
3370
+ turn.timeout = void 0;
3371
+ if (conversation.inFlight) {
3372
+ return;
3373
+ }
3374
+ logger6.debug(
3375
+ // eslint-disable-next-line max-len
3376
+ `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, starting turn with ${turn.messages.length} messages`
3377
+ );
3378
+ startHeadTurn(conversationId, conversation, turn);
3379
+ }, config.messageQueueDelay * 1e3);
3427
3380
  }
3428
- __name(waitForBatchCompletion, "waitForBatchCompletion");
3381
+ __name(resetTurnTimeout, "resetTurnTimeout");
3382
+ function tryStartHeadTurn(conversationId, conversation) {
3383
+ if (conversation.inFlight) {
3384
+ return;
3385
+ }
3386
+ const head = conversation.turns[0];
3387
+ if (!head) {
3388
+ queues.delete(conversationId);
3389
+ return;
3390
+ }
3391
+ if (head.state === "collecting") {
3392
+ return;
3393
+ }
3394
+ startHeadTurn(conversationId, conversation, head);
3395
+ }
3396
+ __name(tryStartHeadTurn, "tryStartHeadTurn");
3397
+ function startHeadTurn(conversationId, conversation, head) {
3398
+ if (conversation.inFlight) {
3399
+ return;
3400
+ }
3401
+ if (conversation.turns[0] !== head) {
3402
+ return;
3403
+ }
3404
+ if (!head.starter) {
3405
+ return;
3406
+ }
3407
+ if (head.timeout) {
3408
+ head.timeout();
3409
+ head.timeout = void 0;
3410
+ }
3411
+ const starter = head.starter;
3412
+ head.starter = void 0;
3413
+ conversation.inFlight = true;
3414
+ head.state = "processing";
3415
+ starter.context.options.inputMessage = mergeMessages(head.messages);
3416
+ head.messages = [];
3417
+ starter.resolve(2 /* CONTINUE */);
3418
+ }
3419
+ __name(startHeadTurn, "startHeadTurn");
3429
3420
  function mergeMessages(messages) {
3430
3421
  if (messages.length === 1) return messages[0];
3431
3422
  const base = messages[0];
@@ -3595,8 +3586,86 @@ function apply26(ctx, config, chain) {
3595
3586
  )
3596
3587
  );
3597
3588
  });
3589
+ ctx.inject(["chatluna_storage"], (ctx2) => {
3590
+ logger7.debug("chatluna_storage service loaded.");
3591
+ ctx2.effect(
3592
+ () => ctx2.chatluna.messageTransformer.intercept(
3593
+ "file",
3594
+ async (session, element, message) => {
3595
+ const fileName = element.attrs["file"] ?? element.attrs["name"];
3596
+ const src = element.attrs["src"];
3597
+ let buffer;
3598
+ if (src.startsWith("http")) {
3599
+ buffer = await readFile(ctx2, src);
3600
+ } else {
3601
+ buffer = await readPlatformFile(ctx2, session, element);
3602
+ }
3603
+ if (!buffer?.buffer) {
3604
+ logger7.warn(
3605
+ `Failed to read file for element: ${element.toString()}`
3606
+ );
3607
+ return;
3608
+ }
3609
+ const file = await ctx2.chatluna_storage.createTempFile(
3610
+ buffer.buffer,
3611
+ fileName
3612
+ );
3613
+ addMessageContent(message, `File: ${file.name} ${file.url}`);
3614
+ }
3615
+ )
3616
+ );
3617
+ });
3598
3618
  }
3599
3619
  __name(apply26, "apply");
3620
+ async function readPlatformFile(ctx, session, element) {
3621
+ const fileId = element.attrs["fileId"];
3622
+ let fileUrl;
3623
+ if (session.platform === "onebot") {
3624
+ const bot = session.bot;
3625
+ if (session.isDirect) {
3626
+ fileUrl = await bot.internal.getPrivateFileUrl(
3627
+ session.userId,
3628
+ fileId
3629
+ );
3630
+ } else {
3631
+ fileUrl = await bot.internal.getGroupFileUrl(
3632
+ session.guildId,
3633
+ fileId,
3634
+ element["busId"]
3635
+ );
3636
+ }
3637
+ }
3638
+ if (!fileUrl) {
3639
+ logger7.warn(`Failed to get file URL for element: ${element.toString()}`);
3640
+ return;
3641
+ }
3642
+ return await readFile(ctx, fileUrl);
3643
+ }
3644
+ __name(readPlatformFile, "readPlatformFile");
3645
+ async function readFile(ctx, url) {
3646
+ try {
3647
+ const response = await ctx.http(url, {
3648
+ responseType: "arraybuffer",
3649
+ method: "get",
3650
+ headers: {
3651
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
3652
+ }
3653
+ });
3654
+ const buffer = Buffer.from(response.data);
3655
+ const base64 = buffer.toString("base64");
3656
+ return {
3657
+ base64Source: base64,
3658
+ buffer
3659
+ };
3660
+ } catch (error) {
3661
+ logger7.error(`Failed to read file from ${url}:`, error);
3662
+ return {
3663
+ base64Source: null,
3664
+ buffer: null
3665
+ };
3666
+ }
3667
+ }
3668
+ __name(readFile, "readFile");
3600
3669
  async function readImage(ctx, url) {
3601
3670
  if (url.startsWith("data:image") && url.includes("base64")) {
3602
3671
  const buffer = Buffer.from(url.split(",")[1], "base64");
package/lib/index.mjs CHANGED
@@ -3244,7 +3244,7 @@ __name(apply24, "apply");
3244
3244
  import { createLogger as createLogger3 } from "koishi-plugin-chatluna/utils/logger";
3245
3245
  import { randomUUID } from "crypto";
3246
3246
  var logger6;
3247
- var batches = /* @__PURE__ */ new Map();
3247
+ var queues = /* @__PURE__ */ new Map();
3248
3248
  function apply25(ctx, config, chain) {
3249
3249
  logger6 = createLogger3(ctx);
3250
3250
  chain.middleware("message_delay", async (session, context) => {
@@ -3256,171 +3256,162 @@ function apply25(ctx, config, chain) {
3256
3256
  const conversationId = room.conversationId;
3257
3257
  const userName = inputMessage.name || "unknown";
3258
3258
  const messageId = context.options.messageId;
3259
- const batch = batches.get(conversationId);
3260
- if (!batch) {
3259
+ const conversation = queues.get(conversationId) ?? {
3260
+ turns: [],
3261
+ inFlight: false
3262
+ };
3263
+ queues.set(conversationId, conversation);
3264
+ const tailTurn = conversation.turns[conversation.turns.length - 1];
3265
+ let turn;
3266
+ if (tailTurn && tailTurn.state !== "processing" && tailTurn.userName === userName) {
3267
+ turn = tailTurn;
3261
3268
  logger6.debug(
3262
- `Creating new batch for ${conversationId}, messageId: ${messageId}`
3269
+ `Joining turn for ${conversationId}, messageId: ${messageId}, user: ${userName}, total: ${turn.messages.length + 1}`
3263
3270
  );
3264
- const newBatch = {
3265
- messages: [inputMessage],
3271
+ } else {
3272
+ const state = conversation.turns.length === 0 && config.messageQueueDelay > 0 ? "collecting" : "waiting";
3273
+ turn = {
3274
+ messages: [],
3266
3275
  userName,
3267
- resolveWaiters: []
3276
+ state
3268
3277
  };
3269
- batches.set(conversationId, newBatch);
3270
- if (config.messageQueueDelay > 0) {
3271
- return await new Promise(
3272
- (resolve) => {
3273
- newBatch.processorResolve = resolve;
3274
- newBatch.timeout = ctx.setTimeout(() => {
3275
- if (batches.get(conversationId) === newBatch) {
3276
- logger6.debug(
3277
- // eslint-disable-next-line max-len
3278
- `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, processing batch with ${newBatch.messages.length} messages`
3279
- );
3280
- context.options.inputMessage = mergeMessages(newBatch.messages);
3281
- batches.delete(conversationId);
3282
- resolve(2 /* CONTINUE */);
3283
- }
3284
- }, config.messageQueueDelay * 1e3);
3285
- }
3286
- );
3287
- }
3288
- return 2 /* CONTINUE */;
3289
- }
3290
- if (batch.userName !== userName) {
3278
+ conversation.turns.push(turn);
3291
3279
  logger6.debug(
3292
- `User mismatch for ${conversationId}, messageId: ${messageId}, waiting for batch completion`
3293
- );
3294
- return await waitForBatchCompletion(
3295
- batch,
3296
- conversationId,
3297
- inputMessage,
3298
- userName,
3299
- context
3280
+ `Creating new turn for ${conversationId}, messageId: ${messageId}, user: ${userName}, queue: ${conversation.turns.length}`
3300
3281
  );
3301
3282
  }
3302
- if (batch.resolveWaiters.length === 0) {
3303
- logger6.debug(
3304
- `Adding message to batch for ${conversationId}, messageId: ${messageId}, total: ${batch.messages.length + 1}`
3305
- );
3306
- batch.messages.push(inputMessage);
3307
- if (config.messageQueueDelay > 0 && batch.timeout && batch.processorResolve) {
3308
- batch.timeout();
3309
- batch.timeout = ctx.setTimeout(() => {
3310
- if (batches.get(conversationId) === batch && batch.processorResolve) {
3311
- logger6.debug(
3312
- `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, processing batch with ${batch.messages.length} messages`
3313
- );
3314
- context.options.inputMessage = mergeMessages(
3315
- batch.messages
3316
- );
3317
- batches.delete(conversationId);
3318
- batch.processorResolve(
3319
- 2 /* CONTINUE */
3320
- );
3321
- }
3322
- }, config.messageQueueDelay * 1e3);
3323
- }
3324
- return 1 /* STOP */;
3283
+ turn.messages.push(inputMessage);
3284
+ const statusPromise = awaitTurnStart(turn, context);
3285
+ if (turn.state === "collecting") {
3286
+ resetTurnTimeout(ctx, config, conversationId, turn);
3325
3287
  }
3326
- logger6.debug(
3327
- `Interrupting and merging for ${conversationId}, messageId: ${messageId}`
3328
- );
3329
- return await interruptAndMerge(batch, inputMessage, context);
3288
+ tryStartHeadTurn(conversationId, conversation);
3289
+ return await statusPromise;
3330
3290
  }).after("check_room").after("read_chat_message").before("lifecycle-handle_command");
3331
- const completeBatch = /* @__PURE__ */ __name(async (conversationId) => {
3332
- const batch = batches.get(conversationId);
3333
- if (batch?.resolveWaiters.length > 0) {
3291
+ const completeTurn = /* @__PURE__ */ __name(async (conversationId) => {
3292
+ const conversation = queues.get(conversationId);
3293
+ if (!conversation) {
3294
+ return;
3295
+ }
3296
+ const current = conversation.turns.shift();
3297
+ conversation.inFlight = false;
3298
+ if (current?.timeout) {
3299
+ current.timeout();
3300
+ }
3301
+ if (current?.starter) {
3302
+ current.starter.resolve(1 /* STOP */);
3303
+ current.starter = void 0;
3304
+ }
3305
+ if (conversation.turns.length === 0) {
3306
+ queues.delete(conversationId);
3307
+ return;
3308
+ }
3309
+ tryStartHeadTurn(conversationId, conversation);
3310
+ if (current) {
3334
3311
  logger6.debug(
3335
- `Completing batch for ${conversationId}, messages: ${batch.messages.length}`
3312
+ `Completing turn for ${conversationId}, remaining: ${conversation.turns.length}`
3336
3313
  );
3337
- if (batch.timeout) {
3338
- batch.timeout();
3339
- }
3340
- batch.resolveWaiters.forEach((resolve) => resolve());
3341
- batches.delete(conversationId);
3342
- } else if (batch) {
3343
- logger6.debug(`Cleaning up batch for ${conversationId}`);
3344
- if (batch.timeout) {
3345
- batch.timeout();
3346
- }
3347
- if (batch.processorResolve) {
3348
- batch.processorResolve(1 /* STOP */);
3349
- }
3350
- batches.delete(conversationId);
3351
3314
  }
3352
- }, "completeBatch");
3353
- ctx.on(
3354
- "chatluna/after-chat",
3355
- async (conversationId) => await completeBatch(conversationId)
3356
- );
3315
+ }, "completeTurn");
3316
+ ctx.on("chatluna/after-chat", completeTurn);
3357
3317
  ctx.on(
3358
3318
  "chatluna/after-chat-error",
3359
- async (_, conversationId) => await completeBatch(conversationId)
3319
+ (_, conversationId) => completeTurn(conversationId)
3360
3320
  );
3361
3321
  ctx.on("chatluna/clear-chat-history", async (conversationId) => {
3362
- const batch = batches.get(conversationId);
3363
- if (batch) {
3322
+ const conversation = queues.get(conversationId);
3323
+ if (conversation) {
3324
+ const stoppedWaiters = conversation.turns.filter(
3325
+ (turn) => !!turn.starter
3326
+ ).length;
3364
3327
  logger6.debug(
3365
- `Clearing chat history for ${conversationId}, stopping ${batch.resolveWaiters.length} waiters`
3366
- );
3367
- if (batch.timeout) {
3368
- batch.timeout();
3369
- }
3370
- batch.resolveWaiters.forEach(
3371
- (resolve) => resolve(1 /* STOP */)
3328
+ `Clearing chat history for ${conversationId}, stopping ${stoppedWaiters} waiters`
3372
3329
  );
3373
- if (batch.processorResolve) {
3374
- batch.processorResolve(1 /* STOP */);
3330
+ for (const turn of conversation.turns) {
3331
+ if (turn.timeout) {
3332
+ turn.timeout();
3333
+ turn.timeout = void 0;
3334
+ }
3335
+ if (turn.starter) {
3336
+ turn.starter.resolve(1 /* STOP */);
3337
+ turn.starter = void 0;
3338
+ }
3375
3339
  }
3376
- batches.delete(conversationId);
3340
+ queues.delete(conversationId);
3377
3341
  }
3378
3342
  });
3379
3343
  }
3380
3344
  __name(apply25, "apply");
3381
- async function interruptAndMerge(batch, message, context) {
3382
- const oldWaiters = batch.resolveWaiters;
3383
- const oldProcessor = batch.processorResolve;
3384
- batch.resolveWaiters = [];
3385
- batch.processorResolve = void 0;
3386
- batch.messages.push(message);
3387
- if (batch.timeout) {
3388
- batch.timeout();
3389
- batch.timeout = void 0;
3390
- }
3391
- oldWaiters.forEach((resolve) => resolve(1 /* STOP */));
3392
- if (oldProcessor) {
3393
- oldProcessor(1 /* STOP */);
3345
+ function awaitTurnStart(turn, context) {
3346
+ if (turn.starter) {
3347
+ turn.starter.resolve(1 /* STOP */);
3348
+ turn.starter = void 0;
3394
3349
  }
3395
3350
  return new Promise((resolve) => {
3396
- batch.resolveWaiters.push(() => {
3397
- context.options.inputMessage = mergeMessages(batch.messages);
3398
- resolve(2 /* CONTINUE */);
3399
- });
3351
+ turn.starter = { resolve, context };
3400
3352
  });
3401
3353
  }
3402
- __name(interruptAndMerge, "interruptAndMerge");
3403
- async function waitForBatchCompletion(batch, conversationId, message, userName, context) {
3404
- return new Promise((resolve) => {
3405
- batch.resolveWaiters.push(() => {
3406
- batches.set(conversationId, {
3407
- messages: [message],
3408
- userName,
3409
- resolveWaiters: [
3410
- () => {
3411
- const newBatch = batches.get(conversationId);
3412
- context.options.inputMessage = mergeMessages(
3413
- newBatch.messages
3414
- );
3415
- batches.delete(conversationId);
3416
- resolve(2 /* CONTINUE */);
3417
- }
3418
- ]
3419
- });
3420
- });
3421
- });
3354
+ __name(awaitTurnStart, "awaitTurnStart");
3355
+ function resetTurnTimeout(ctx, config, conversationId, turn) {
3356
+ if (turn.timeout) {
3357
+ turn.timeout();
3358
+ }
3359
+ turn.timeout = ctx.setTimeout(() => {
3360
+ const conversation = queues.get(conversationId);
3361
+ if (!conversation || conversation.turns[0] !== turn) {
3362
+ return;
3363
+ }
3364
+ turn.state = "waiting";
3365
+ turn.timeout = void 0;
3366
+ if (conversation.inFlight) {
3367
+ return;
3368
+ }
3369
+ logger6.debug(
3370
+ // eslint-disable-next-line max-len
3371
+ `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, starting turn with ${turn.messages.length} messages`
3372
+ );
3373
+ startHeadTurn(conversationId, conversation, turn);
3374
+ }, config.messageQueueDelay * 1e3);
3422
3375
  }
3423
- __name(waitForBatchCompletion, "waitForBatchCompletion");
3376
+ __name(resetTurnTimeout, "resetTurnTimeout");
3377
+ function tryStartHeadTurn(conversationId, conversation) {
3378
+ if (conversation.inFlight) {
3379
+ return;
3380
+ }
3381
+ const head = conversation.turns[0];
3382
+ if (!head) {
3383
+ queues.delete(conversationId);
3384
+ return;
3385
+ }
3386
+ if (head.state === "collecting") {
3387
+ return;
3388
+ }
3389
+ startHeadTurn(conversationId, conversation, head);
3390
+ }
3391
+ __name(tryStartHeadTurn, "tryStartHeadTurn");
3392
+ function startHeadTurn(conversationId, conversation, head) {
3393
+ if (conversation.inFlight) {
3394
+ return;
3395
+ }
3396
+ if (conversation.turns[0] !== head) {
3397
+ return;
3398
+ }
3399
+ if (!head.starter) {
3400
+ return;
3401
+ }
3402
+ if (head.timeout) {
3403
+ head.timeout();
3404
+ head.timeout = void 0;
3405
+ }
3406
+ const starter = head.starter;
3407
+ head.starter = void 0;
3408
+ conversation.inFlight = true;
3409
+ head.state = "processing";
3410
+ starter.context.options.inputMessage = mergeMessages(head.messages);
3411
+ head.messages = [];
3412
+ starter.resolve(2 /* CONTINUE */);
3413
+ }
3414
+ __name(startHeadTurn, "startHeadTurn");
3424
3415
  function mergeMessages(messages) {
3425
3416
  if (messages.length === 1) return messages[0];
3426
3417
  const base = messages[0];
@@ -3594,8 +3585,86 @@ function apply26(ctx, config, chain) {
3594
3585
  )
3595
3586
  );
3596
3587
  });
3588
+ ctx.inject(["chatluna_storage"], (ctx2) => {
3589
+ logger7.debug("chatluna_storage service loaded.");
3590
+ ctx2.effect(
3591
+ () => ctx2.chatluna.messageTransformer.intercept(
3592
+ "file",
3593
+ async (session, element, message) => {
3594
+ const fileName = element.attrs["file"] ?? element.attrs["name"];
3595
+ const src = element.attrs["src"];
3596
+ let buffer;
3597
+ if (src.startsWith("http")) {
3598
+ buffer = await readFile(ctx2, src);
3599
+ } else {
3600
+ buffer = await readPlatformFile(ctx2, session, element);
3601
+ }
3602
+ if (!buffer?.buffer) {
3603
+ logger7.warn(
3604
+ `Failed to read file for element: ${element.toString()}`
3605
+ );
3606
+ return;
3607
+ }
3608
+ const file = await ctx2.chatluna_storage.createTempFile(
3609
+ buffer.buffer,
3610
+ fileName
3611
+ );
3612
+ addMessageContent(message, `File: ${file.name} ${file.url}`);
3613
+ }
3614
+ )
3615
+ );
3616
+ });
3597
3617
  }
3598
3618
  __name(apply26, "apply");
3619
+ async function readPlatformFile(ctx, session, element) {
3620
+ const fileId = element.attrs["fileId"];
3621
+ let fileUrl;
3622
+ if (session.platform === "onebot") {
3623
+ const bot = session.bot;
3624
+ if (session.isDirect) {
3625
+ fileUrl = await bot.internal.getPrivateFileUrl(
3626
+ session.userId,
3627
+ fileId
3628
+ );
3629
+ } else {
3630
+ fileUrl = await bot.internal.getGroupFileUrl(
3631
+ session.guildId,
3632
+ fileId,
3633
+ element["busId"]
3634
+ );
3635
+ }
3636
+ }
3637
+ if (!fileUrl) {
3638
+ logger7.warn(`Failed to get file URL for element: ${element.toString()}`);
3639
+ return;
3640
+ }
3641
+ return await readFile(ctx, fileUrl);
3642
+ }
3643
+ __name(readPlatformFile, "readPlatformFile");
3644
+ async function readFile(ctx, url) {
3645
+ try {
3646
+ const response = await ctx.http(url, {
3647
+ responseType: "arraybuffer",
3648
+ method: "get",
3649
+ headers: {
3650
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
3651
+ }
3652
+ });
3653
+ const buffer = Buffer.from(response.data);
3654
+ const base64 = buffer.toString("base64");
3655
+ return {
3656
+ base64Source: base64,
3657
+ buffer
3658
+ };
3659
+ } catch (error) {
3660
+ logger7.error(`Failed to read file from ${url}:`, error);
3661
+ return {
3662
+ base64Source: null,
3663
+ buffer: null
3664
+ };
3665
+ }
3666
+ }
3667
+ __name(readFile, "readFile");
3599
3668
  async function readImage(ctx, url) {
3600
3669
  if (url.startsWith("data:image") && url.includes("base64")) {
3601
3670
  const buffer = Buffer.from(url.split(",")[1], "base64");
@@ -22,4 +22,4 @@ export type CreateOpenAIAgentParams = {
22
22
  };
23
23
  export declare function createOpenAIAgent({ llm, tools, prompt }: CreateOpenAIAgentParams): RunnableSequence<{
24
24
  steps: AgentStep[];
25
- }, AgentAction | AgentFinish | AgentAction[]>;
25
+ }, AgentAction | AgentAction[] | AgentFinish>;
@@ -12,7 +12,7 @@ export declare class OpenAIFunctionsAgentOutputParser extends AgentActionOutputP
12
12
  lc_namespace: string[];
13
13
  static lc_name(): string;
14
14
  parse(text: string): Promise<AgentAction | AgentFinish>;
15
- parseResult(generations: ChatGeneration[]): Promise<FunctionsAgentAction | AgentFinish>;
15
+ parseResult(generations: ChatGeneration[]): Promise<AgentFinish | FunctionsAgentAction>;
16
16
  /**
17
17
  * Parses the output message into a FunctionsAgentAction or AgentFinish
18
18
  * object.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chatluna",
3
3
  "description": "chatluna for koishi",
4
- "version": "1.3.6",
4
+ "version": "1.3.8",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "lib/index.mjs",
7
7
  "typings": "lib/index.d.ts",
@@ -226,8 +226,8 @@
226
226
  "langchain"
227
227
  ],
228
228
  "dependencies": {
229
- "@chatluna/shared-prompt-renderer": "^1.0.3",
230
- "@langchain/core": "0.3.62",
229
+ "@chatluna/shared-prompt-renderer": "^1.0.4",
230
+ "@langchain/core": "^0.3.80",
231
231
  "@vue/reactivity": "^3.6.0-alpha.2",
232
232
  "decimal.js": "^10.6.0",
233
233
  "he": "^1.2.0",
@@ -254,11 +254,12 @@
254
254
  "@types/js-yaml": "^4.0.9",
255
255
  "@types/qrcode": "^1.5.5",
256
256
  "@types/useragent": "^2",
257
- "atsc": "^2.1.0"
257
+ "atsc": "^2.1.0",
258
+ "koishi-plugin-adapter-onebot": "^6.8.0"
258
259
  },
259
260
  "peerDependencies": {
260
261
  "koishi": "^4.18.9",
261
- "koishi-plugin-chatluna-storage-service": "^0.0.11"
262
+ "koishi-plugin-chatluna-storage-service": "^1.0.1"
262
263
  },
263
264
  "peerDependenciesMeta": {
264
265
  "koishi-plugin-chatluna-storage-service": {
@@ -266,16 +267,16 @@
266
267
  }
267
268
  },
268
269
  "resolutions": {
269
- "@langchain/core": "0.3.62",
270
+ "@langchain/core": "^0.3.80",
270
271
  "js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.21"
271
272
  },
272
273
  "overrides": {
273
- "@langchain/core": "0.3.62",
274
+ "@langchain/core": "^0.3.80",
274
275
  "js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.21"
275
276
  },
276
277
  "pnpm": {
277
278
  "overrides": {
278
- "@langchain/core": "0.3.62",
279
+ "@langchain/core": "^0.3.80",
279
280
  "js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.21"
280
281
  }
281
282
  },