@lobehub/lobehub 2.0.0-next.29 → 2.0.0-next.30
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/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/database/src/models/__tests__/message.test.ts +0 -129
- package/packages/database/src/models/message.ts +0 -49
- package/packages/utils/src/server/auth.ts +6 -6
- package/packages/utils/src/server/geo.ts +9 -9
- package/packages/utils/src/server/xor.ts +7 -7
- package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +783 -2
- package/src/server/routers/lambda/message.ts +17 -84
- package/src/server/services/message/__tests__/index.test.ts +348 -0
- package/src/server/services/message/index.ts +159 -0
- package/src/services/message/index.ts +1 -0
|
@@ -101,7 +101,7 @@ describe('Message Router Integration Tests', () => {
|
|
|
101
101
|
|
|
102
102
|
// 先创建 thread
|
|
103
103
|
const { threads } = await import('@/database/schemas');
|
|
104
|
-
const [thread] = await serverDB
|
|
104
|
+
const [thread] = (await serverDB
|
|
105
105
|
.insert(threads)
|
|
106
106
|
.values({
|
|
107
107
|
userId,
|
|
@@ -109,7 +109,7 @@ describe('Message Router Integration Tests', () => {
|
|
|
109
109
|
sourceMessageId: 'msg-source',
|
|
110
110
|
type: 'continuation', // type is required
|
|
111
111
|
})
|
|
112
|
-
.returning();
|
|
112
|
+
.returning()) as any;
|
|
113
113
|
|
|
114
114
|
const result = await caller.createNewMessage({
|
|
115
115
|
content: 'Test message in thread',
|
|
@@ -316,6 +316,74 @@ describe('Message Router Integration Tests', () => {
|
|
|
316
316
|
expect(page1Ids).not.toEqual(page2Ids);
|
|
317
317
|
}
|
|
318
318
|
});
|
|
319
|
+
|
|
320
|
+
it('should return messages filtered by groupId', async () => {
|
|
321
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
322
|
+
|
|
323
|
+
// 首先创建一个 chat_group
|
|
324
|
+
const { chatGroups } = await import('@/database/schemas');
|
|
325
|
+
const [chatGroup] = await serverDB
|
|
326
|
+
.insert(chatGroups)
|
|
327
|
+
.values({
|
|
328
|
+
userId,
|
|
329
|
+
title: 'Test Chat Group',
|
|
330
|
+
})
|
|
331
|
+
.returning();
|
|
332
|
+
|
|
333
|
+
// 创建消息并设置 groupId
|
|
334
|
+
const msg1 = await caller.createNewMessage({
|
|
335
|
+
content: 'Message 1 in group',
|
|
336
|
+
role: 'assistant',
|
|
337
|
+
sessionId: testSessionId,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
await serverDB
|
|
341
|
+
.update(messages)
|
|
342
|
+
.set({ groupId: chatGroup.id })
|
|
343
|
+
.where(eq(messages.id, msg1.id));
|
|
344
|
+
|
|
345
|
+
// 创建不在 group 中的消息
|
|
346
|
+
await caller.createNewMessage({
|
|
347
|
+
content: 'Message without group',
|
|
348
|
+
role: 'user',
|
|
349
|
+
sessionId: testSessionId,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// 查询 group 中的消息
|
|
353
|
+
const result = await caller.getMessages({
|
|
354
|
+
sessionId: testSessionId,
|
|
355
|
+
groupId: chatGroup.id,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
expect(result).toHaveLength(1);
|
|
359
|
+
expect(result[0].id).toBe(msg1.id);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should support useGroup parameter', async () => {
|
|
363
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
364
|
+
|
|
365
|
+
// 创建多个消息
|
|
366
|
+
await caller.createNewMessage({
|
|
367
|
+
content: 'Message 1',
|
|
368
|
+
role: 'assistant',
|
|
369
|
+
sessionId: testSessionId,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
await caller.createNewMessage({
|
|
373
|
+
content: 'Message 2',
|
|
374
|
+
role: 'assistant',
|
|
375
|
+
sessionId: testSessionId,
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// useGroup 参数应该影响消息分组展示
|
|
379
|
+
const result = await caller.getMessages({
|
|
380
|
+
sessionId: testSessionId,
|
|
381
|
+
useGroup: true,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
expect(result).toBeDefined();
|
|
385
|
+
expect(Array.isArray(result)).toBe(true);
|
|
386
|
+
});
|
|
319
387
|
});
|
|
320
388
|
|
|
321
389
|
describe('removeMessages', () => {
|
|
@@ -346,6 +414,160 @@ describe('Message Router Integration Tests', () => {
|
|
|
346
414
|
|
|
347
415
|
expect(remainingMessages).toHaveLength(0);
|
|
348
416
|
});
|
|
417
|
+
|
|
418
|
+
it('should return message list when sessionId is provided', async () => {
|
|
419
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
420
|
+
|
|
421
|
+
// 创建消息
|
|
422
|
+
const msg1Result = await caller.createNewMessage({
|
|
423
|
+
content: 'Message 1',
|
|
424
|
+
role: 'user',
|
|
425
|
+
sessionId: testSessionId,
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
const msg2Result = await caller.createNewMessage({
|
|
429
|
+
content: 'Message 2',
|
|
430
|
+
role: 'user',
|
|
431
|
+
sessionId: testSessionId,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const msg3Result = await caller.createNewMessage({
|
|
435
|
+
content: 'Message 3',
|
|
436
|
+
role: 'user',
|
|
437
|
+
sessionId: testSessionId,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// 删除消息并返回列表
|
|
441
|
+
const result = await caller.removeMessages({
|
|
442
|
+
ids: [msg1Result.id],
|
|
443
|
+
sessionId: testSessionId,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
expect(result.success).toBe(true);
|
|
447
|
+
expect(result.messages).toBeDefined();
|
|
448
|
+
expect(result.messages).toHaveLength(2);
|
|
449
|
+
expect(result.messages?.map((m) => m.id)).toContain(msg2Result.id);
|
|
450
|
+
expect(result.messages?.map((m) => m.id)).toContain(msg3Result.id);
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
describe('removeMessage', () => {
|
|
455
|
+
it('should remove a single message', async () => {
|
|
456
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
457
|
+
|
|
458
|
+
const msgResult = await caller.createNewMessage({
|
|
459
|
+
content: 'Message to remove',
|
|
460
|
+
role: 'user',
|
|
461
|
+
sessionId: testSessionId,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
await caller.removeMessage({ id: msgResult.id });
|
|
465
|
+
|
|
466
|
+
// 验证消息已删除
|
|
467
|
+
const deletedMessage = await serverDB
|
|
468
|
+
.select()
|
|
469
|
+
.from(messages)
|
|
470
|
+
.where(eq(messages.id, msgResult.id));
|
|
471
|
+
|
|
472
|
+
expect(deletedMessage).toHaveLength(0);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should return message list when sessionId is provided', async () => {
|
|
476
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
477
|
+
|
|
478
|
+
const msg1Result = await caller.createNewMessage({
|
|
479
|
+
content: 'Message 1',
|
|
480
|
+
role: 'user',
|
|
481
|
+
sessionId: testSessionId,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
const msg2Result = await caller.createNewMessage({
|
|
485
|
+
content: 'Message 2',
|
|
486
|
+
role: 'user',
|
|
487
|
+
sessionId: testSessionId,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
const result = await caller.removeMessage({
|
|
491
|
+
id: msg1Result.id,
|
|
492
|
+
sessionId: testSessionId,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
expect(result.success).toBe(true);
|
|
496
|
+
expect(result.messages).toBeDefined();
|
|
497
|
+
expect(result.messages).toHaveLength(1);
|
|
498
|
+
expect(result.messages?.[0].id).toBe(msg2Result.id);
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
describe('removeAllMessages', () => {
|
|
503
|
+
it('should remove all messages for the user', async () => {
|
|
504
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
505
|
+
|
|
506
|
+
// 创建多个 session 和消息
|
|
507
|
+
await caller.createNewMessage({
|
|
508
|
+
content: 'Message 1',
|
|
509
|
+
role: 'user',
|
|
510
|
+
sessionId: testSessionId,
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const [anotherSession] = await serverDB
|
|
514
|
+
.insert(sessions)
|
|
515
|
+
.values({
|
|
516
|
+
userId,
|
|
517
|
+
type: 'agent',
|
|
518
|
+
})
|
|
519
|
+
.returning();
|
|
520
|
+
|
|
521
|
+
await caller.createNewMessage({
|
|
522
|
+
content: 'Message 2',
|
|
523
|
+
role: 'user',
|
|
524
|
+
sessionId: anotherSession.id,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// 删除所有消息
|
|
528
|
+
await caller.removeAllMessages();
|
|
529
|
+
|
|
530
|
+
// 验证所有消息已删除
|
|
531
|
+
const remainingMessages = await serverDB
|
|
532
|
+
.select()
|
|
533
|
+
.from(messages)
|
|
534
|
+
.where(eq(messages.userId, userId));
|
|
535
|
+
|
|
536
|
+
expect(remainingMessages).toHaveLength(0);
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
describe('removeMessageQuery', () => {
|
|
541
|
+
it('should remove message query', async () => {
|
|
542
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
543
|
+
|
|
544
|
+
const msgResult = await caller.createNewMessage({
|
|
545
|
+
content: 'Message with query',
|
|
546
|
+
role: 'user',
|
|
547
|
+
sessionId: testSessionId,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// 创建一个 message query 记录,使用 UUID
|
|
551
|
+
const { messageQueries } = await import('@/database/schemas');
|
|
552
|
+
const [queryRecord] = await serverDB
|
|
553
|
+
.insert(messageQueries)
|
|
554
|
+
.values({
|
|
555
|
+
messageId: msgResult.id,
|
|
556
|
+
userId,
|
|
557
|
+
userQuery: 'test query',
|
|
558
|
+
})
|
|
559
|
+
.returning();
|
|
560
|
+
|
|
561
|
+
await caller.removeMessageQuery({ id: queryRecord.id });
|
|
562
|
+
|
|
563
|
+
// 验证消息查询已删除
|
|
564
|
+
const deletedQuery = await serverDB
|
|
565
|
+
.select()
|
|
566
|
+
.from(messageQueries)
|
|
567
|
+
.where(eq(messageQueries.id, queryRecord.id));
|
|
568
|
+
|
|
569
|
+
expect(deletedQuery).toHaveLength(0);
|
|
570
|
+
});
|
|
349
571
|
});
|
|
350
572
|
|
|
351
573
|
describe('removeMessagesByAssistant', () => {
|
|
@@ -414,6 +636,43 @@ describe('Message Router Integration Tests', () => {
|
|
|
414
636
|
});
|
|
415
637
|
});
|
|
416
638
|
|
|
639
|
+
describe('removeMessagesByGroup', () => {
|
|
640
|
+
it('should call removeMessagesByGroup endpoint', async () => {
|
|
641
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
642
|
+
|
|
643
|
+
// 首先创建一个 chat_group
|
|
644
|
+
const { chatGroups } = await import('@/database/schemas');
|
|
645
|
+
const [chatGroup] = await serverDB
|
|
646
|
+
.insert(chatGroups)
|
|
647
|
+
.values({
|
|
648
|
+
userId,
|
|
649
|
+
title: 'Test Chat Group for Delete',
|
|
650
|
+
})
|
|
651
|
+
.returning();
|
|
652
|
+
|
|
653
|
+
// 创建消息并设置 groupId
|
|
654
|
+
const msg1 = await caller.createNewMessage({
|
|
655
|
+
content: 'Message 1 in group',
|
|
656
|
+
role: 'assistant',
|
|
657
|
+
sessionId: testSessionId,
|
|
658
|
+
topicId: testTopicId,
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
await serverDB
|
|
662
|
+
.update(messages)
|
|
663
|
+
.set({ groupId: chatGroup.id })
|
|
664
|
+
.where(eq(messages.id, msg1.id));
|
|
665
|
+
|
|
666
|
+
// 调用删除接口(不会抛出错误即为成功)
|
|
667
|
+
await expect(
|
|
668
|
+
caller.removeMessagesByGroup({
|
|
669
|
+
groupId: chatGroup.id,
|
|
670
|
+
topicId: testTopicId,
|
|
671
|
+
}),
|
|
672
|
+
).resolves.not.toThrow();
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
|
|
417
676
|
describe('update', () => {
|
|
418
677
|
it('should update message content', async () => {
|
|
419
678
|
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
@@ -438,6 +697,36 @@ describe('Message Router Integration Tests', () => {
|
|
|
438
697
|
|
|
439
698
|
expect(updatedMessage.content).toBe('Updated content');
|
|
440
699
|
});
|
|
700
|
+
|
|
701
|
+
it('should update message and return message list when sessionId is provided', async () => {
|
|
702
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
703
|
+
|
|
704
|
+
const msg1 = await caller.createNewMessage({
|
|
705
|
+
content: 'Message 1',
|
|
706
|
+
role: 'user',
|
|
707
|
+
sessionId: testSessionId,
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
const msg2 = await caller.createNewMessage({
|
|
711
|
+
content: 'Message 2',
|
|
712
|
+
role: 'user',
|
|
713
|
+
sessionId: testSessionId,
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
const result = await caller.update({
|
|
717
|
+
id: msg1.id,
|
|
718
|
+
sessionId: testSessionId,
|
|
719
|
+
value: {
|
|
720
|
+
content: 'Updated Message 1',
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
expect(result).toBeDefined();
|
|
725
|
+
// The update method returns the updated message list
|
|
726
|
+
const messages = await caller.getMessages({ sessionId: testSessionId });
|
|
727
|
+
expect(messages).toHaveLength(2);
|
|
728
|
+
expect(messages.find((m) => m.id === msg1.id)?.content).toBe('Updated Message 1');
|
|
729
|
+
});
|
|
441
730
|
});
|
|
442
731
|
|
|
443
732
|
describe('searchMessages', () => {
|
|
@@ -465,6 +754,458 @@ describe('Message Router Integration Tests', () => {
|
|
|
465
754
|
});
|
|
466
755
|
});
|
|
467
756
|
|
|
757
|
+
describe('updateMessagePlugin', () => {
|
|
758
|
+
it('should update message plugin state', async () => {
|
|
759
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
760
|
+
|
|
761
|
+
const msg = await caller.createNewMessage({
|
|
762
|
+
content: 'Message with plugin',
|
|
763
|
+
role: 'assistant',
|
|
764
|
+
sessionId: testSessionId,
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
// 先创建一个 plugin 记录
|
|
768
|
+
const { messagePlugins } = await import('@/database/schemas');
|
|
769
|
+
await serverDB.insert(messagePlugins).values({
|
|
770
|
+
id: msg.id,
|
|
771
|
+
userId,
|
|
772
|
+
toolCallId: 'test-tool-call',
|
|
773
|
+
type: 'default',
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
await caller.updateMessagePlugin({
|
|
777
|
+
id: msg.id,
|
|
778
|
+
value: {
|
|
779
|
+
state: { key: 'value' },
|
|
780
|
+
},
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
const [updatedPlugin] = await serverDB
|
|
784
|
+
.select()
|
|
785
|
+
.from(messagePlugins)
|
|
786
|
+
.where(eq(messagePlugins.id, msg.id));
|
|
787
|
+
|
|
788
|
+
expect(updatedPlugin).toBeDefined();
|
|
789
|
+
expect(updatedPlugin.state).toBeDefined();
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
describe('updateMessageRAG', () => {
|
|
794
|
+
it('should update message RAG information', async () => {
|
|
795
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
796
|
+
|
|
797
|
+
const msg = await caller.createNewMessage({
|
|
798
|
+
content: 'Message with RAG',
|
|
799
|
+
role: 'assistant',
|
|
800
|
+
sessionId: testSessionId,
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// 创建必要的依赖: chunks -> messageQueries -> messageQueryChunks
|
|
804
|
+
const { chunks, messageQueries, messageQueryChunks } = await import('@/database/schemas');
|
|
805
|
+
|
|
806
|
+
// 1. 创建 chunk
|
|
807
|
+
const [chunk] = await serverDB
|
|
808
|
+
.insert(chunks)
|
|
809
|
+
.values({
|
|
810
|
+
userId,
|
|
811
|
+
text: 'test chunk content',
|
|
812
|
+
})
|
|
813
|
+
.returning();
|
|
814
|
+
|
|
815
|
+
// 2. 创建 message query
|
|
816
|
+
const [query] = await serverDB
|
|
817
|
+
.insert(messageQueries)
|
|
818
|
+
.values({
|
|
819
|
+
messageId: msg.id,
|
|
820
|
+
userId,
|
|
821
|
+
userQuery: 'test query',
|
|
822
|
+
})
|
|
823
|
+
.returning();
|
|
824
|
+
|
|
825
|
+
// 3. 调用 updateMessageRAG
|
|
826
|
+
await caller.updateMessageRAG({
|
|
827
|
+
id: msg.id,
|
|
828
|
+
value: {
|
|
829
|
+
fileChunks: [{ id: chunk.id, similarity: 0.95 }],
|
|
830
|
+
ragQueryId: query.id,
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
// 验证 messageQueryChunks 记录已创建
|
|
835
|
+
const [queryChunk] = await serverDB
|
|
836
|
+
.select()
|
|
837
|
+
.from(messageQueryChunks)
|
|
838
|
+
.where(eq(messageQueryChunks.messageId, msg.id));
|
|
839
|
+
|
|
840
|
+
expect(queryChunk).toBeDefined();
|
|
841
|
+
expect(queryChunk.chunkId).toBe(chunk.id);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('should return message list when sessionId is provided', async () => {
|
|
845
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
846
|
+
|
|
847
|
+
const msg1 = await caller.createNewMessage({
|
|
848
|
+
content: 'Message 1',
|
|
849
|
+
role: 'assistant',
|
|
850
|
+
sessionId: testSessionId,
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
await caller.createNewMessage({
|
|
854
|
+
content: 'Message 2',
|
|
855
|
+
role: 'assistant',
|
|
856
|
+
sessionId: testSessionId,
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// 创建必要的依赖: chunks -> messageQueries
|
|
860
|
+
const { chunks, messageQueries } = await import('@/database/schemas');
|
|
861
|
+
const [chunk] = await serverDB
|
|
862
|
+
.insert(chunks)
|
|
863
|
+
.values({
|
|
864
|
+
userId,
|
|
865
|
+
text: 'test chunk content',
|
|
866
|
+
})
|
|
867
|
+
.returning();
|
|
868
|
+
|
|
869
|
+
// 创建 query (需要 queryId)
|
|
870
|
+
const [query] = await serverDB
|
|
871
|
+
.insert(messageQueries)
|
|
872
|
+
.values({
|
|
873
|
+
messageId: msg1.id,
|
|
874
|
+
userId,
|
|
875
|
+
userQuery: 'test query',
|
|
876
|
+
})
|
|
877
|
+
.returning();
|
|
878
|
+
|
|
879
|
+
const result = await caller.updateMessageRAG({
|
|
880
|
+
id: msg1.id,
|
|
881
|
+
sessionId: testSessionId,
|
|
882
|
+
value: {
|
|
883
|
+
fileChunks: [{ id: chunk.id, similarity: 0.95 }],
|
|
884
|
+
ragQueryId: query.id,
|
|
885
|
+
},
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
expect(result.success).toBe(true);
|
|
889
|
+
expect(result.messages).toBeDefined();
|
|
890
|
+
expect(result.messages).toHaveLength(2);
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
describe('updateMetadata', () => {
|
|
895
|
+
it('should update message metadata', async () => {
|
|
896
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
897
|
+
|
|
898
|
+
const msg = await caller.createNewMessage({
|
|
899
|
+
content: 'Message with metadata',
|
|
900
|
+
role: 'user',
|
|
901
|
+
sessionId: testSessionId,
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
await caller.updateMetadata({
|
|
905
|
+
id: msg.id,
|
|
906
|
+
value: { customKey: 'customValue' },
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
const [updatedMessage] = await serverDB
|
|
910
|
+
.select()
|
|
911
|
+
.from(messages)
|
|
912
|
+
.where(eq(messages.id, msg.id));
|
|
913
|
+
|
|
914
|
+
expect(updatedMessage).toBeDefined();
|
|
915
|
+
// Verify the message still exists after update
|
|
916
|
+
expect(updatedMessage.id).toBe(msg.id);
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
describe('updatePluginError', () => {
|
|
921
|
+
it('should update plugin error state', async () => {
|
|
922
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
923
|
+
|
|
924
|
+
const msg = await caller.createNewMessage({
|
|
925
|
+
content: 'Message with plugin error',
|
|
926
|
+
role: 'assistant',
|
|
927
|
+
sessionId: testSessionId,
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
// 先创建一个 plugin 记录
|
|
931
|
+
const { messagePlugins } = await import('@/database/schemas');
|
|
932
|
+
await serverDB.insert(messagePlugins).values({
|
|
933
|
+
id: msg.id,
|
|
934
|
+
userId,
|
|
935
|
+
toolCallId: 'test-tool-call-error',
|
|
936
|
+
type: 'default',
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
await caller.updatePluginError({
|
|
940
|
+
id: msg.id,
|
|
941
|
+
value: { message: 'Plugin error occurred' },
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
const [updatedPlugin] = await serverDB
|
|
945
|
+
.select()
|
|
946
|
+
.from(messagePlugins)
|
|
947
|
+
.where(eq(messagePlugins.id, msg.id));
|
|
948
|
+
|
|
949
|
+
expect(updatedPlugin).toBeDefined();
|
|
950
|
+
expect(updatedPlugin.error).toBeDefined();
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it('should return message list when sessionId is provided', async () => {
|
|
954
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
955
|
+
|
|
956
|
+
const msg1 = await caller.createNewMessage({
|
|
957
|
+
content: 'Message 1',
|
|
958
|
+
role: 'assistant',
|
|
959
|
+
sessionId: testSessionId,
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
// 先创建一个 plugin 记录
|
|
963
|
+
const { messagePlugins } = await import('@/database/schemas');
|
|
964
|
+
await serverDB.insert(messagePlugins).values({
|
|
965
|
+
id: msg1.id,
|
|
966
|
+
userId,
|
|
967
|
+
toolCallId: 'test-tool-call-error-2',
|
|
968
|
+
type: 'default',
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
await caller.createNewMessage({
|
|
972
|
+
content: 'Message 2',
|
|
973
|
+
role: 'assistant',
|
|
974
|
+
sessionId: testSessionId,
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
const result = await caller.updatePluginError({
|
|
978
|
+
id: msg1.id,
|
|
979
|
+
sessionId: testSessionId,
|
|
980
|
+
value: { message: 'Error' },
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
expect(result.success).toBe(true);
|
|
984
|
+
expect(result.messages).toBeDefined();
|
|
985
|
+
expect(result.messages).toHaveLength(2);
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
describe('updatePluginState', () => {
|
|
990
|
+
it('should update plugin state', async () => {
|
|
991
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
992
|
+
|
|
993
|
+
const msg = await caller.createNewMessage({
|
|
994
|
+
content: 'Message with plugin state',
|
|
995
|
+
role: 'assistant',
|
|
996
|
+
sessionId: testSessionId,
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// 先创建一个 plugin 记录
|
|
1000
|
+
const { messagePlugins } = await import('@/database/schemas');
|
|
1001
|
+
await serverDB.insert(messagePlugins).values({
|
|
1002
|
+
id: msg.id,
|
|
1003
|
+
userId,
|
|
1004
|
+
toolCallId: 'test-tool-call-state',
|
|
1005
|
+
type: 'default',
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
const result = await caller.updatePluginState({
|
|
1009
|
+
id: msg.id,
|
|
1010
|
+
sessionId: testSessionId,
|
|
1011
|
+
value: { stateKey: 'stateValue' },
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
expect(result).toBeDefined();
|
|
1015
|
+
});
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
describe('updateTTS', () => {
|
|
1019
|
+
it('should update TTS information', async () => {
|
|
1020
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1021
|
+
|
|
1022
|
+
const msg = await caller.createNewMessage({
|
|
1023
|
+
content: 'Message with TTS',
|
|
1024
|
+
role: 'assistant',
|
|
1025
|
+
sessionId: testSessionId,
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// 创建 file 记录
|
|
1029
|
+
const { files } = await import('@/database/schemas');
|
|
1030
|
+
const [file] = await serverDB
|
|
1031
|
+
.insert(files)
|
|
1032
|
+
.values({
|
|
1033
|
+
userId,
|
|
1034
|
+
name: 'audio.mp3',
|
|
1035
|
+
fileType: 'audio/mpeg',
|
|
1036
|
+
size: 1024,
|
|
1037
|
+
url: '/files/audio.mp3',
|
|
1038
|
+
})
|
|
1039
|
+
.returning();
|
|
1040
|
+
|
|
1041
|
+
await caller.updateTTS({
|
|
1042
|
+
id: msg.id,
|
|
1043
|
+
value: {
|
|
1044
|
+
file: file.id,
|
|
1045
|
+
voice: 'en-US-neural',
|
|
1046
|
+
contentMd5: 'abc123',
|
|
1047
|
+
},
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
const { messageTTS } = await import('@/database/schemas');
|
|
1051
|
+
const [ttsRecord] = await serverDB.select().from(messageTTS).where(eq(messageTTS.id, msg.id));
|
|
1052
|
+
|
|
1053
|
+
expect(ttsRecord).toBeDefined();
|
|
1054
|
+
expect(ttsRecord.voice).toBe('en-US-neural');
|
|
1055
|
+
expect(ttsRecord.fileId).toBe(file.id);
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
it('should delete TTS when value is false', async () => {
|
|
1059
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1060
|
+
|
|
1061
|
+
const msg = await caller.createNewMessage({
|
|
1062
|
+
content: 'Message with TTS to delete',
|
|
1063
|
+
role: 'assistant',
|
|
1064
|
+
sessionId: testSessionId,
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
// 创建 file 记录
|
|
1068
|
+
const { files } = await import('@/database/schemas');
|
|
1069
|
+
const [file] = await serverDB
|
|
1070
|
+
.insert(files)
|
|
1071
|
+
.values({
|
|
1072
|
+
userId,
|
|
1073
|
+
name: 'audio-delete.mp3',
|
|
1074
|
+
fileType: 'audio/mpeg',
|
|
1075
|
+
size: 1024,
|
|
1076
|
+
url: '/files/audio-delete.mp3',
|
|
1077
|
+
})
|
|
1078
|
+
.returning();
|
|
1079
|
+
|
|
1080
|
+
// First add TTS
|
|
1081
|
+
await caller.updateTTS({
|
|
1082
|
+
id: msg.id,
|
|
1083
|
+
value: {
|
|
1084
|
+
file: file.id,
|
|
1085
|
+
voice: 'en-US-neural',
|
|
1086
|
+
},
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
// Then delete it
|
|
1090
|
+
await caller.updateTTS({
|
|
1091
|
+
id: msg.id,
|
|
1092
|
+
value: false,
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
const { messageTTS } = await import('@/database/schemas');
|
|
1096
|
+
const [ttsRecord] = await serverDB.select().from(messageTTS).where(eq(messageTTS.id, msg.id));
|
|
1097
|
+
|
|
1098
|
+
expect(ttsRecord).toBeUndefined();
|
|
1099
|
+
});
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
describe('updateTranslate', () => {
|
|
1103
|
+
it('should update translation information', async () => {
|
|
1104
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1105
|
+
|
|
1106
|
+
const msg = await caller.createNewMessage({
|
|
1107
|
+
content: 'Hello world',
|
|
1108
|
+
role: 'user',
|
|
1109
|
+
sessionId: testSessionId,
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
await caller.updateTranslate({
|
|
1113
|
+
id: msg.id,
|
|
1114
|
+
value: {
|
|
1115
|
+
content: '你好世界',
|
|
1116
|
+
from: 'en',
|
|
1117
|
+
to: 'zh',
|
|
1118
|
+
},
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
const { messageTranslates } = await import('@/database/schemas');
|
|
1122
|
+
const [translateRecord] = await serverDB
|
|
1123
|
+
.select()
|
|
1124
|
+
.from(messageTranslates)
|
|
1125
|
+
.where(eq(messageTranslates.id, msg.id));
|
|
1126
|
+
|
|
1127
|
+
expect(translateRecord).toBeDefined();
|
|
1128
|
+
expect(translateRecord.to).toBe('zh');
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
it('should delete translation when value is false', async () => {
|
|
1132
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1133
|
+
|
|
1134
|
+
const msg = await caller.createNewMessage({
|
|
1135
|
+
content: 'Hello world',
|
|
1136
|
+
role: 'user',
|
|
1137
|
+
sessionId: testSessionId,
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
// First add translation
|
|
1141
|
+
await caller.updateTranslate({
|
|
1142
|
+
id: msg.id,
|
|
1143
|
+
value: {
|
|
1144
|
+
content: '你好世界',
|
|
1145
|
+
to: 'zh',
|
|
1146
|
+
},
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
// Then delete it
|
|
1150
|
+
await caller.updateTranslate({
|
|
1151
|
+
id: msg.id,
|
|
1152
|
+
value: false,
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
const [updatedMessage] = await serverDB
|
|
1156
|
+
.select()
|
|
1157
|
+
.from(messages)
|
|
1158
|
+
.where(eq(messages.id, msg.id));
|
|
1159
|
+
|
|
1160
|
+
expect(updatedMessage).toBeDefined();
|
|
1161
|
+
});
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
describe('getHeatmaps', () => {
|
|
1165
|
+
it('should get message heatmaps', async () => {
|
|
1166
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1167
|
+
|
|
1168
|
+
// 创建一些消息
|
|
1169
|
+
await caller.createNewMessage({
|
|
1170
|
+
content: 'Message 1',
|
|
1171
|
+
role: 'user',
|
|
1172
|
+
sessionId: testSessionId,
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
await caller.createNewMessage({
|
|
1176
|
+
content: 'Message 2',
|
|
1177
|
+
role: 'assistant',
|
|
1178
|
+
sessionId: testSessionId,
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
const heatmaps = await caller.getHeatmaps();
|
|
1182
|
+
|
|
1183
|
+
expect(heatmaps).toBeDefined();
|
|
1184
|
+
expect(Array.isArray(heatmaps)).toBe(true);
|
|
1185
|
+
});
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
describe('rankModels', () => {
|
|
1189
|
+
it('should get model usage ranking', async () => {
|
|
1190
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1191
|
+
|
|
1192
|
+
// 创建带有模型信息的消息
|
|
1193
|
+
const msg = await caller.createNewMessage({
|
|
1194
|
+
content: 'Message from AI',
|
|
1195
|
+
role: 'assistant',
|
|
1196
|
+
sessionId: testSessionId,
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
// 添加模型信息
|
|
1200
|
+
await serverDB.update(messages).set({ model: 'gpt-4' }).where(eq(messages.id, msg.id));
|
|
1201
|
+
|
|
1202
|
+
const ranking = await caller.rankModels();
|
|
1203
|
+
|
|
1204
|
+
expect(ranking).toBeDefined();
|
|
1205
|
+
expect(Array.isArray(ranking)).toBe(true);
|
|
1206
|
+
});
|
|
1207
|
+
});
|
|
1208
|
+
|
|
468
1209
|
describe('count and statistics', () => {
|
|
469
1210
|
it('should count messages', async () => {
|
|
470
1211
|
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
@@ -487,6 +1228,26 @@ describe('Message Router Integration Tests', () => {
|
|
|
487
1228
|
expect(count).toBe(2);
|
|
488
1229
|
});
|
|
489
1230
|
|
|
1231
|
+
it('should count messages with date range', async () => {
|
|
1232
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1233
|
+
|
|
1234
|
+
await caller.createNewMessage({
|
|
1235
|
+
content: 'Message 1',
|
|
1236
|
+
role: 'user',
|
|
1237
|
+
sessionId: testSessionId,
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
const startDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
1241
|
+
const endDate = new Date().toISOString();
|
|
1242
|
+
|
|
1243
|
+
const count = await caller.count({
|
|
1244
|
+
startDate,
|
|
1245
|
+
endDate,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
expect(count).toBeGreaterThanOrEqual(1);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
490
1251
|
it('should count words', async () => {
|
|
491
1252
|
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
492
1253
|
|
|
@@ -500,5 +1261,25 @@ describe('Message Router Integration Tests', () => {
|
|
|
500
1261
|
|
|
501
1262
|
expect(wordCount).toBeGreaterThan(0);
|
|
502
1263
|
});
|
|
1264
|
+
|
|
1265
|
+
it('should count words with date range', async () => {
|
|
1266
|
+
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
1267
|
+
|
|
1268
|
+
await caller.createNewMessage({
|
|
1269
|
+
content: 'Hello world test message',
|
|
1270
|
+
role: 'user',
|
|
1271
|
+
sessionId: testSessionId,
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
const startDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
1275
|
+
const endDate = new Date().toISOString();
|
|
1276
|
+
|
|
1277
|
+
const wordCount = await caller.countWords({
|
|
1278
|
+
startDate,
|
|
1279
|
+
endDate,
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
expect(wordCount).toBeGreaterThan(0);
|
|
1283
|
+
});
|
|
503
1284
|
});
|
|
504
1285
|
});
|