koishi-plugin-chatluna 1.3.7 → 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,203 +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: [],
3273
- collectWaiters: [],
3274
- state: config.messageQueueDelay > 0 ? "collecting" : "processing"
3281
+ state
3275
3282
  };
3276
- batches.set(conversationId, newBatch);
3277
- if (config.messageQueueDelay > 0) {
3278
- resetBatchTimeout(ctx, config, newBatch, conversationId);
3279
- return await awaitCollectingBatch(newBatch, context);
3280
- }
3281
- newBatch.messages = [];
3282
- return 2 /* CONTINUE */;
3283
- }
3284
- if (batch.userName !== userName) {
3285
- logger6.debug(
3286
- `User mismatch for ${conversationId}, messageId: ${messageId}, waiting for batch completion`
3287
- );
3288
- return await waitForBatchCompletion(
3289
- batch,
3290
- conversationId,
3291
- inputMessage,
3292
- userName,
3293
- context
3294
- );
3295
- }
3296
- if (batch.state === "collecting") {
3283
+ conversation.turns.push(turn);
3297
3284
  logger6.debug(
3298
- `Adding message to batch for ${conversationId}, messageId: ${messageId}, total: ${batch.messages.length + 1}`
3285
+ `Creating new turn for ${conversationId}, messageId: ${messageId}, user: ${userName}, queue: ${conversation.turns.length}`
3299
3286
  );
3300
- batch.messages.push(inputMessage);
3301
- if (config.messageQueueDelay > 0) {
3302
- resetBatchTimeout(ctx, config, batch, conversationId);
3303
- }
3304
- return await awaitCollectingBatch(batch, context);
3305
3287
  }
3306
- logger6.debug(
3307
- `Waiting for batch completion for ${conversationId}, messageId: ${messageId}`
3308
- );
3309
- const status = await awaitBatchCompletion(batch, inputMessage);
3310
- if (status === 1 /* STOP */) {
3311
- return status;
3288
+ turn.messages.push(inputMessage);
3289
+ const statusPromise = awaitTurnStart(turn, context);
3290
+ if (turn.state === "collecting") {
3291
+ resetTurnTimeout(ctx, config, conversationId, turn);
3312
3292
  }
3313
- logger6.debug(
3314
- `Interrupting and merging for ${conversationId}, messageId: ${messageId}`
3315
- );
3316
- return interruptAndMerge(batch, context);
3293
+ tryStartHeadTurn(conversationId, conversation);
3294
+ return await statusPromise;
3317
3295
  }).after("check_room").after("read_chat_message").before("lifecycle-handle_command");
3318
- const completeBatch = /* @__PURE__ */ __name(async (conversationId) => {
3319
- const batch = batches.get(conversationId);
3320
- 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) {
3321
3316
  logger6.debug(
3322
- `Completing batch for ${conversationId}, messages: ${batch.messages.length}`
3323
- );
3324
- if (batch.timeout) {
3325
- batch.timeout();
3326
- }
3327
- const waiters = batch.resolveWaiters;
3328
- batch.resolveWaiters = [];
3329
- waiters.forEach(
3330
- (resolve) => resolve(2 /* CONTINUE */)
3317
+ `Completing turn for ${conversationId}, remaining: ${conversation.turns.length}`
3331
3318
  );
3332
- } else if (batch) {
3333
- logger6.debug(`Cleaning up batch for ${conversationId}`);
3334
- if (batch.timeout) {
3335
- batch.timeout();
3336
- }
3337
- batches.delete(conversationId);
3338
3319
  }
3339
- }, "completeBatch");
3340
- ctx.on(
3341
- "chatluna/after-chat",
3342
- async (conversationId) => await completeBatch(conversationId)
3343
- );
3320
+ }, "completeTurn");
3321
+ ctx.on("chatluna/after-chat", completeTurn);
3344
3322
  ctx.on(
3345
3323
  "chatluna/after-chat-error",
3346
- async (_, conversationId) => await completeBatch(conversationId)
3324
+ (_, conversationId) => completeTurn(conversationId)
3347
3325
  );
3348
3326
  ctx.on("chatluna/clear-chat-history", async (conversationId) => {
3349
- const batch = batches.get(conversationId);
3350
- if (batch) {
3327
+ const conversation = queues.get(conversationId);
3328
+ if (conversation) {
3329
+ const stoppedWaiters = conversation.turns.filter(
3330
+ (turn) => !!turn.starter
3331
+ ).length;
3351
3332
  logger6.debug(
3352
- `Clearing chat history for ${conversationId}, stopping ${batch.resolveWaiters.length} waiters`
3333
+ `Clearing chat history for ${conversationId}, stopping ${stoppedWaiters} waiters`
3353
3334
  );
3354
- if (batch.timeout) {
3355
- batch.timeout();
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
+ }
3356
3344
  }
3357
- batch.resolveWaiters.forEach(
3358
- (resolve) => resolve(1 /* STOP */)
3359
- );
3360
- batch.collectWaiters.forEach(
3361
- (resolve) => resolve(1 /* STOP */)
3362
- );
3363
- batches.delete(conversationId);
3345
+ queues.delete(conversationId);
3364
3346
  }
3365
3347
  });
3366
3348
  }
3367
3349
  __name(apply25, "apply");
3368
- function interruptAndMerge(batch, context) {
3369
- if (batch.messages.length === 0) {
3370
- return 1 /* STOP */;
3350
+ function awaitTurnStart(turn, context) {
3351
+ if (turn.starter) {
3352
+ turn.starter.resolve(1 /* STOP */);
3353
+ turn.starter = void 0;
3371
3354
  }
3372
- context.options.inputMessage = mergeMessages(batch.messages);
3373
- batch.messages = [];
3374
- batch.state = "processing";
3375
- return 2 /* CONTINUE */;
3376
- }
3377
- __name(interruptAndMerge, "interruptAndMerge");
3378
- async function awaitCollectingBatch(batch, context) {
3379
- resolveCollectWaiters(batch, 1 /* STOP */);
3380
- return await new Promise((resolve) => {
3381
- batch.collectWaiters.push((status) => {
3382
- if (status === 1 /* STOP */) {
3383
- resolve(1 /* STOP */);
3384
- return;
3385
- }
3386
- context.options.inputMessage = mergeMessages(batch.messages);
3387
- batch.messages = [];
3388
- batch.state = "processing";
3389
- resolve(2 /* CONTINUE */);
3390
- });
3355
+ return new Promise((resolve) => {
3356
+ turn.starter = { resolve, context };
3391
3357
  });
3392
3358
  }
3393
- __name(awaitCollectingBatch, "awaitCollectingBatch");
3394
- function resolveCollectWaiters(batch, status) {
3395
- const waiters = batch.collectWaiters;
3396
- batch.collectWaiters = [];
3397
- waiters.forEach((resolve) => resolve(status));
3398
- }
3399
- __name(resolveCollectWaiters, "resolveCollectWaiters");
3400
- function resetBatchTimeout(ctx, config, batch, conversationId) {
3401
- if (batch.timeout) {
3402
- batch.timeout();
3359
+ __name(awaitTurnStart, "awaitTurnStart");
3360
+ function resetTurnTimeout(ctx, config, conversationId, turn) {
3361
+ if (turn.timeout) {
3362
+ turn.timeout();
3403
3363
  }
3404
- batch.timeout = ctx.setTimeout(() => {
3405
- if (batches.get(conversationId) === batch) {
3406
- logger6.debug(
3407
- // eslint-disable-next-line max-len
3408
- `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, processing batch with ${batch.messages.length} messages`
3409
- );
3410
- batch.timeout = void 0;
3411
- resolveCollectWaiters(batch, 2 /* CONTINUE */);
3364
+ turn.timeout = ctx.setTimeout(() => {
3365
+ const conversation = queues.get(conversationId);
3366
+ if (!conversation || conversation.turns[0] !== turn) {
3367
+ return;
3412
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);
3413
3379
  }, config.messageQueueDelay * 1e3);
3414
3380
  }
3415
- __name(resetBatchTimeout, "resetBatchTimeout");
3416
- async function awaitBatchCompletion(batch, message) {
3417
- if (batch.resolveWaiters.length === 0) {
3418
- batch.messages = [message];
3419
- } else {
3420
- batch.messages.push(message);
3381
+ __name(resetTurnTimeout, "resetTurnTimeout");
3382
+ function tryStartHeadTurn(conversationId, conversation) {
3383
+ if (conversation.inFlight) {
3384
+ return;
3421
3385
  }
3422
- return await new Promise((resolve) => {
3423
- batch.resolveWaiters.push((status) => {
3424
- resolve(status ?? 2 /* CONTINUE */);
3425
- });
3426
- });
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);
3427
3395
  }
3428
- __name(awaitBatchCompletion, "awaitBatchCompletion");
3429
- async function waitForBatchCompletion(batch, conversationId, message, userName, context) {
3430
- return new Promise((resolve) => {
3431
- batch.resolveWaiters.push((status) => {
3432
- if (status === 1 /* STOP */) {
3433
- resolve(1 /* STOP */);
3434
- return;
3435
- }
3436
- batches.set(conversationId, {
3437
- messages: [message],
3438
- userName,
3439
- resolveWaiters: [
3440
- (nextStatus) => {
3441
- if (nextStatus === 1 /* STOP */) {
3442
- resolve(1 /* STOP */);
3443
- return;
3444
- }
3445
- const newBatch = batches.get(conversationId);
3446
- context.options.inputMessage = mergeMessages(
3447
- newBatch.messages
3448
- );
3449
- newBatch.messages = [];
3450
- batches.delete(conversationId);
3451
- resolve(2 /* CONTINUE */);
3452
- }
3453
- ],
3454
- collectWaiters: [],
3455
- state: "processing"
3456
- });
3457
- });
3458
- });
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 */);
3459
3418
  }
3460
- __name(waitForBatchCompletion, "waitForBatchCompletion");
3419
+ __name(startHeadTurn, "startHeadTurn");
3461
3420
  function mergeMessages(messages) {
3462
3421
  if (messages.length === 1) return messages[0];
3463
3422
  const base = messages[0];
@@ -3627,8 +3586,86 @@ function apply26(ctx, config, chain) {
3627
3586
  )
3628
3587
  );
3629
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
+ });
3630
3618
  }
3631
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");
3632
3669
  async function readImage(ctx, url) {
3633
3670
  if (url.startsWith("data:image") && url.includes("base64")) {
3634
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,203 +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: [],
3268
- collectWaiters: [],
3269
- state: config.messageQueueDelay > 0 ? "collecting" : "processing"
3276
+ state
3270
3277
  };
3271
- batches.set(conversationId, newBatch);
3272
- if (config.messageQueueDelay > 0) {
3273
- resetBatchTimeout(ctx, config, newBatch, conversationId);
3274
- return await awaitCollectingBatch(newBatch, context);
3275
- }
3276
- newBatch.messages = [];
3277
- return 2 /* CONTINUE */;
3278
- }
3279
- if (batch.userName !== userName) {
3280
- logger6.debug(
3281
- `User mismatch for ${conversationId}, messageId: ${messageId}, waiting for batch completion`
3282
- );
3283
- return await waitForBatchCompletion(
3284
- batch,
3285
- conversationId,
3286
- inputMessage,
3287
- userName,
3288
- context
3289
- );
3290
- }
3291
- if (batch.state === "collecting") {
3278
+ conversation.turns.push(turn);
3292
3279
  logger6.debug(
3293
- `Adding message to batch for ${conversationId}, messageId: ${messageId}, total: ${batch.messages.length + 1}`
3280
+ `Creating new turn for ${conversationId}, messageId: ${messageId}, user: ${userName}, queue: ${conversation.turns.length}`
3294
3281
  );
3295
- batch.messages.push(inputMessage);
3296
- if (config.messageQueueDelay > 0) {
3297
- resetBatchTimeout(ctx, config, batch, conversationId);
3298
- }
3299
- return await awaitCollectingBatch(batch, context);
3300
3282
  }
3301
- logger6.debug(
3302
- `Waiting for batch completion for ${conversationId}, messageId: ${messageId}`
3303
- );
3304
- const status = await awaitBatchCompletion(batch, inputMessage);
3305
- if (status === 1 /* STOP */) {
3306
- return status;
3283
+ turn.messages.push(inputMessage);
3284
+ const statusPromise = awaitTurnStart(turn, context);
3285
+ if (turn.state === "collecting") {
3286
+ resetTurnTimeout(ctx, config, conversationId, turn);
3307
3287
  }
3308
- logger6.debug(
3309
- `Interrupting and merging for ${conversationId}, messageId: ${messageId}`
3310
- );
3311
- return interruptAndMerge(batch, context);
3288
+ tryStartHeadTurn(conversationId, conversation);
3289
+ return await statusPromise;
3312
3290
  }).after("check_room").after("read_chat_message").before("lifecycle-handle_command");
3313
- const completeBatch = /* @__PURE__ */ __name(async (conversationId) => {
3314
- const batch = batches.get(conversationId);
3315
- 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) {
3316
3311
  logger6.debug(
3317
- `Completing batch for ${conversationId}, messages: ${batch.messages.length}`
3318
- );
3319
- if (batch.timeout) {
3320
- batch.timeout();
3321
- }
3322
- const waiters = batch.resolveWaiters;
3323
- batch.resolveWaiters = [];
3324
- waiters.forEach(
3325
- (resolve) => resolve(2 /* CONTINUE */)
3312
+ `Completing turn for ${conversationId}, remaining: ${conversation.turns.length}`
3326
3313
  );
3327
- } else if (batch) {
3328
- logger6.debug(`Cleaning up batch for ${conversationId}`);
3329
- if (batch.timeout) {
3330
- batch.timeout();
3331
- }
3332
- batches.delete(conversationId);
3333
3314
  }
3334
- }, "completeBatch");
3335
- ctx.on(
3336
- "chatluna/after-chat",
3337
- async (conversationId) => await completeBatch(conversationId)
3338
- );
3315
+ }, "completeTurn");
3316
+ ctx.on("chatluna/after-chat", completeTurn);
3339
3317
  ctx.on(
3340
3318
  "chatluna/after-chat-error",
3341
- async (_, conversationId) => await completeBatch(conversationId)
3319
+ (_, conversationId) => completeTurn(conversationId)
3342
3320
  );
3343
3321
  ctx.on("chatluna/clear-chat-history", async (conversationId) => {
3344
- const batch = batches.get(conversationId);
3345
- if (batch) {
3322
+ const conversation = queues.get(conversationId);
3323
+ if (conversation) {
3324
+ const stoppedWaiters = conversation.turns.filter(
3325
+ (turn) => !!turn.starter
3326
+ ).length;
3346
3327
  logger6.debug(
3347
- `Clearing chat history for ${conversationId}, stopping ${batch.resolveWaiters.length} waiters`
3328
+ `Clearing chat history for ${conversationId}, stopping ${stoppedWaiters} waiters`
3348
3329
  );
3349
- if (batch.timeout) {
3350
- batch.timeout();
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
+ }
3351
3339
  }
3352
- batch.resolveWaiters.forEach(
3353
- (resolve) => resolve(1 /* STOP */)
3354
- );
3355
- batch.collectWaiters.forEach(
3356
- (resolve) => resolve(1 /* STOP */)
3357
- );
3358
- batches.delete(conversationId);
3340
+ queues.delete(conversationId);
3359
3341
  }
3360
3342
  });
3361
3343
  }
3362
3344
  __name(apply25, "apply");
3363
- function interruptAndMerge(batch, context) {
3364
- if (batch.messages.length === 0) {
3365
- return 1 /* STOP */;
3345
+ function awaitTurnStart(turn, context) {
3346
+ if (turn.starter) {
3347
+ turn.starter.resolve(1 /* STOP */);
3348
+ turn.starter = void 0;
3366
3349
  }
3367
- context.options.inputMessage = mergeMessages(batch.messages);
3368
- batch.messages = [];
3369
- batch.state = "processing";
3370
- return 2 /* CONTINUE */;
3371
- }
3372
- __name(interruptAndMerge, "interruptAndMerge");
3373
- async function awaitCollectingBatch(batch, context) {
3374
- resolveCollectWaiters(batch, 1 /* STOP */);
3375
- return await new Promise((resolve) => {
3376
- batch.collectWaiters.push((status) => {
3377
- if (status === 1 /* STOP */) {
3378
- resolve(1 /* STOP */);
3379
- return;
3380
- }
3381
- context.options.inputMessage = mergeMessages(batch.messages);
3382
- batch.messages = [];
3383
- batch.state = "processing";
3384
- resolve(2 /* CONTINUE */);
3385
- });
3350
+ return new Promise((resolve) => {
3351
+ turn.starter = { resolve, context };
3386
3352
  });
3387
3353
  }
3388
- __name(awaitCollectingBatch, "awaitCollectingBatch");
3389
- function resolveCollectWaiters(batch, status) {
3390
- const waiters = batch.collectWaiters;
3391
- batch.collectWaiters = [];
3392
- waiters.forEach((resolve) => resolve(status));
3393
- }
3394
- __name(resolveCollectWaiters, "resolveCollectWaiters");
3395
- function resetBatchTimeout(ctx, config, batch, conversationId) {
3396
- if (batch.timeout) {
3397
- batch.timeout();
3354
+ __name(awaitTurnStart, "awaitTurnStart");
3355
+ function resetTurnTimeout(ctx, config, conversationId, turn) {
3356
+ if (turn.timeout) {
3357
+ turn.timeout();
3398
3358
  }
3399
- batch.timeout = ctx.setTimeout(() => {
3400
- if (batches.get(conversationId) === batch) {
3401
- logger6.debug(
3402
- // eslint-disable-next-line max-len
3403
- `Delay timeout (${config.messageQueueDelay}s) for ${conversationId}, processing batch with ${batch.messages.length} messages`
3404
- );
3405
- batch.timeout = void 0;
3406
- resolveCollectWaiters(batch, 2 /* CONTINUE */);
3359
+ turn.timeout = ctx.setTimeout(() => {
3360
+ const conversation = queues.get(conversationId);
3361
+ if (!conversation || conversation.turns[0] !== turn) {
3362
+ return;
3407
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);
3408
3374
  }, config.messageQueueDelay * 1e3);
3409
3375
  }
3410
- __name(resetBatchTimeout, "resetBatchTimeout");
3411
- async function awaitBatchCompletion(batch, message) {
3412
- if (batch.resolveWaiters.length === 0) {
3413
- batch.messages = [message];
3414
- } else {
3415
- batch.messages.push(message);
3376
+ __name(resetTurnTimeout, "resetTurnTimeout");
3377
+ function tryStartHeadTurn(conversationId, conversation) {
3378
+ if (conversation.inFlight) {
3379
+ return;
3416
3380
  }
3417
- return await new Promise((resolve) => {
3418
- batch.resolveWaiters.push((status) => {
3419
- resolve(status ?? 2 /* CONTINUE */);
3420
- });
3421
- });
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);
3422
3390
  }
3423
- __name(awaitBatchCompletion, "awaitBatchCompletion");
3424
- async function waitForBatchCompletion(batch, conversationId, message, userName, context) {
3425
- return new Promise((resolve) => {
3426
- batch.resolveWaiters.push((status) => {
3427
- if (status === 1 /* STOP */) {
3428
- resolve(1 /* STOP */);
3429
- return;
3430
- }
3431
- batches.set(conversationId, {
3432
- messages: [message],
3433
- userName,
3434
- resolveWaiters: [
3435
- (nextStatus) => {
3436
- if (nextStatus === 1 /* STOP */) {
3437
- resolve(1 /* STOP */);
3438
- return;
3439
- }
3440
- const newBatch = batches.get(conversationId);
3441
- context.options.inputMessage = mergeMessages(
3442
- newBatch.messages
3443
- );
3444
- newBatch.messages = [];
3445
- batches.delete(conversationId);
3446
- resolve(2 /* CONTINUE */);
3447
- }
3448
- ],
3449
- collectWaiters: [],
3450
- state: "processing"
3451
- });
3452
- });
3453
- });
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 */);
3454
3413
  }
3455
- __name(waitForBatchCompletion, "waitForBatchCompletion");
3414
+ __name(startHeadTurn, "startHeadTurn");
3456
3415
  function mergeMessages(messages) {
3457
3416
  if (messages.length === 1) return messages[0];
3458
3417
  const base = messages[0];
@@ -3626,8 +3585,86 @@ function apply26(ctx, config, chain) {
3626
3585
  )
3627
3586
  );
3628
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
+ });
3629
3617
  }
3630
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");
3631
3668
  async function readImage(ctx, url) {
3632
3669
  if (url.startsWith("data:image") && url.includes("base64")) {
3633
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.7",
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
  },