@lobehub/lobehub 2.0.0-next.95 → 2.0.0-next.96
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/locales/ar/common.json +21 -0
- package/locales/ar/hotkey.json +4 -0
- package/locales/bg-BG/common.json +21 -0
- package/locales/bg-BG/hotkey.json +4 -0
- package/locales/de-DE/common.json +21 -0
- package/locales/de-DE/hotkey.json +4 -0
- package/locales/en-US/common.json +21 -0
- package/locales/en-US/hotkey.json +4 -0
- package/locales/es-ES/common.json +21 -0
- package/locales/es-ES/hotkey.json +4 -0
- package/locales/fa-IR/common.json +21 -0
- package/locales/fa-IR/hotkey.json +4 -0
- package/locales/fr-FR/common.json +21 -0
- package/locales/fr-FR/hotkey.json +4 -0
- package/locales/it-IT/common.json +21 -0
- package/locales/it-IT/hotkey.json +4 -0
- package/locales/ja-JP/common.json +21 -0
- package/locales/ja-JP/hotkey.json +4 -0
- package/locales/ko-KR/common.json +21 -0
- package/locales/ko-KR/hotkey.json +4 -0
- package/locales/nl-NL/common.json +21 -0
- package/locales/nl-NL/hotkey.json +4 -0
- package/locales/pl-PL/common.json +21 -0
- package/locales/pl-PL/hotkey.json +4 -0
- package/locales/pt-BR/common.json +21 -0
- package/locales/pt-BR/hotkey.json +4 -0
- package/locales/ru-RU/common.json +21 -0
- package/locales/ru-RU/hotkey.json +4 -0
- package/locales/tr-TR/common.json +21 -0
- package/locales/tr-TR/hotkey.json +4 -0
- package/locales/vi-VN/common.json +21 -0
- package/locales/vi-VN/hotkey.json +4 -0
- package/locales/zh-CN/common.json +21 -0
- package/locales/zh-CN/hotkey.json +4 -0
- package/locales/zh-TW/common.json +21 -0
- package/locales/zh-TW/hotkey.json +4 -0
- package/package.json +3 -1
- package/packages/const/src/hotkeys.ts +6 -0
- package/packages/conversation-flow/src/__tests__/indexing.test.ts +513 -0
- package/packages/conversation-flow/src/__tests__/structuring.test.ts +600 -0
- package/packages/types/src/hotkey.ts +1 -0
- package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +41 -8
- package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +6 -4
- package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/index.tsx +16 -4
- package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +15 -3
- package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +23 -15
- package/src/layout/GlobalProvider/Cmdk.tsx +470 -0
- package/src/layout/GlobalProvider/CmdkLazy.tsx +17 -0
- package/src/layout/GlobalProvider/index.tsx +2 -0
- package/src/locales/default/common.ts +21 -0
- package/src/locales/default/hotkey.ts +4 -0
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
"desc": "Geçerli oturumun mesajlarını ve yüklenen dosyaları temizle",
|
|
8
8
|
"title": "Oturum mesajlarını temizle"
|
|
9
9
|
},
|
|
10
|
+
"commandPalette": {
|
|
11
|
+
"desc": "Genel komut panelini açarak işlevlere hızlı erişim sağlayın",
|
|
12
|
+
"title": "Komut Paneli"
|
|
13
|
+
},
|
|
10
14
|
"deleteAndRegenerateMessage": {
|
|
11
15
|
"desc": "Son mesajı sil ve yeniden oluştur",
|
|
12
16
|
"title": "Sil ve Yeniden Oluştur"
|
|
@@ -135,6 +135,27 @@
|
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
"close": "Đóng",
|
|
138
|
+
"cmdk": {
|
|
139
|
+
"about": "Giới thiệu",
|
|
140
|
+
"communitySupport": "Hỗ trợ cộng đồng",
|
|
141
|
+
"discover": "Khám phá",
|
|
142
|
+
"knowledgeBase": "Cơ sở kiến thức",
|
|
143
|
+
"navigate": "Điều hướng",
|
|
144
|
+
"newAgent": "Tạo trợ lý mới",
|
|
145
|
+
"noResults": "Không tìm thấy kết quả phù hợp",
|
|
146
|
+
"openSettings": "Mở cài đặt",
|
|
147
|
+
"painting": "Vẽ bằng AI",
|
|
148
|
+
"searchPlaceholder": "Nhập lệnh hoặc tìm kiếm...",
|
|
149
|
+
"settings": "Cài đặt",
|
|
150
|
+
"starOnGitHub": "Đánh giá sao trên GitHub",
|
|
151
|
+
"submitIssue": "Gửi vấn đề",
|
|
152
|
+
"theme": "Giao diện",
|
|
153
|
+
"themeAuto": "Theo hệ thống",
|
|
154
|
+
"themeDark": "Chế độ tối",
|
|
155
|
+
"themeLight": "Chế độ sáng",
|
|
156
|
+
"toOpen": "Mở",
|
|
157
|
+
"toSelect": "Chọn"
|
|
158
|
+
},
|
|
138
159
|
"confirm": "Xác nhận",
|
|
139
160
|
"contact": "Liên hệ chúng tôi",
|
|
140
161
|
"copy": "Sao chép",
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
"desc": "Xóa tất cả tin nhắn và tệp đã tải lên trong cuộc trò chuyện hiện tại",
|
|
8
8
|
"title": "Xóa tin nhắn cuộc trò chuyện"
|
|
9
9
|
},
|
|
10
|
+
"commandPalette": {
|
|
11
|
+
"desc": "Mở bảng lệnh toàn cục để truy cập nhanh các chức năng",
|
|
12
|
+
"title": "Bảng lệnh"
|
|
13
|
+
},
|
|
10
14
|
"deleteAndRegenerateMessage": {
|
|
11
15
|
"desc": "Xoá tin nhắn cuối cùng và tạo lại",
|
|
12
16
|
"title": "Xoá và tạo lại"
|
|
@@ -135,6 +135,27 @@
|
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
"close": "关闭",
|
|
138
|
+
"cmdk": {
|
|
139
|
+
"about": "关于",
|
|
140
|
+
"communitySupport": "社区支持",
|
|
141
|
+
"discover": "发现",
|
|
142
|
+
"knowledgeBase": "知识库",
|
|
143
|
+
"navigate": "导航",
|
|
144
|
+
"newAgent": "新建助手",
|
|
145
|
+
"noResults": "未找到相关结果",
|
|
146
|
+
"openSettings": "打开设置",
|
|
147
|
+
"painting": "AI 绘画",
|
|
148
|
+
"searchPlaceholder": "输入命令或搜索...",
|
|
149
|
+
"settings": "设置",
|
|
150
|
+
"starOnGitHub": "在 GitHub 上给我们 Star",
|
|
151
|
+
"submitIssue": "提交问题",
|
|
152
|
+
"theme": "主题",
|
|
153
|
+
"themeAuto": "跟随系统",
|
|
154
|
+
"themeDark": "深色模式",
|
|
155
|
+
"themeLight": "浅色模式",
|
|
156
|
+
"toOpen": "打开",
|
|
157
|
+
"toSelect": "选择"
|
|
158
|
+
},
|
|
138
159
|
"confirm": "确认",
|
|
139
160
|
"contact": "联系我们",
|
|
140
161
|
"copy": "复制",
|
|
@@ -135,6 +135,27 @@
|
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
"close": "關閉",
|
|
138
|
+
"cmdk": {
|
|
139
|
+
"about": "關於",
|
|
140
|
+
"communitySupport": "社群支援",
|
|
141
|
+
"discover": "探索",
|
|
142
|
+
"knowledgeBase": "知識庫",
|
|
143
|
+
"navigate": "導覽",
|
|
144
|
+
"newAgent": "新增助手",
|
|
145
|
+
"noResults": "未找到相關結果",
|
|
146
|
+
"openSettings": "開啟設定",
|
|
147
|
+
"painting": "AI 繪圖",
|
|
148
|
+
"searchPlaceholder": "輸入指令或搜尋...",
|
|
149
|
+
"settings": "設定",
|
|
150
|
+
"starOnGitHub": "在 GitHub 上給我們星標",
|
|
151
|
+
"submitIssue": "提交問題",
|
|
152
|
+
"theme": "主題",
|
|
153
|
+
"themeAuto": "依系統設定",
|
|
154
|
+
"themeDark": "深色模式",
|
|
155
|
+
"themeLight": "淺色模式",
|
|
156
|
+
"toOpen": "開啟",
|
|
157
|
+
"toSelect": "選取"
|
|
158
|
+
},
|
|
138
159
|
"confirm": "確認",
|
|
139
160
|
"contact": "聯繫我們",
|
|
140
161
|
"copy": "複製",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.96",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -197,6 +197,7 @@
|
|
|
197
197
|
"antd-style": "^3.7.1",
|
|
198
198
|
"brotli-wasm": "^3.0.1",
|
|
199
199
|
"chroma-js": "^3.1.2",
|
|
200
|
+
"cmdk": "^1.1.1",
|
|
200
201
|
"cookie": "^1.0.2",
|
|
201
202
|
"countries-and-timezones": "^3.8.0",
|
|
202
203
|
"dayjs": "^1.11.19",
|
|
@@ -256,6 +257,7 @@
|
|
|
256
257
|
"pwa-install-handler": "^2.6.3",
|
|
257
258
|
"query-string": "^9.3.1",
|
|
258
259
|
"random-words": "^2.0.1",
|
|
260
|
+
"rc-util": "^5.44.4",
|
|
259
261
|
"react": "19.2.0",
|
|
260
262
|
"react-confetti": "^6.4.0",
|
|
261
263
|
"react-diff-view": "^3.3.2",
|
|
@@ -15,6 +15,12 @@ export type HotkeyRegistration = HotkeyItem[];
|
|
|
15
15
|
// mod is the command key on Mac, alt is the ctrl key on Windows
|
|
16
16
|
export const HOTKEYS_REGISTRATION: HotkeyRegistration = [
|
|
17
17
|
// basic
|
|
18
|
+
{
|
|
19
|
+
group: HotkeyGroupEnum.Essential,
|
|
20
|
+
id: HotkeyEnum.CommandPalette,
|
|
21
|
+
keys: combineKeys([KeyEnum.Mod, 'j']),
|
|
22
|
+
scopes: [HotkeyScopeEnum.Global],
|
|
23
|
+
},
|
|
18
24
|
{
|
|
19
25
|
group: HotkeyGroupEnum.Essential,
|
|
20
26
|
id: HotkeyEnum.Search,
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { buildHelperMaps } from '../indexing';
|
|
4
|
+
import type { Message, MessageGroupMetadata } from '../types';
|
|
5
|
+
|
|
6
|
+
describe('buildHelperMaps', () => {
|
|
7
|
+
describe('messageMap', () => {
|
|
8
|
+
it('should build messageMap for O(1) access', () => {
|
|
9
|
+
const messages: Message[] = [
|
|
10
|
+
{
|
|
11
|
+
content: 'Hello',
|
|
12
|
+
createdAt: 1000,
|
|
13
|
+
id: 'msg-1',
|
|
14
|
+
meta: {},
|
|
15
|
+
role: 'user',
|
|
16
|
+
updatedAt: 1000,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
content: 'Hi there',
|
|
20
|
+
createdAt: 2000,
|
|
21
|
+
id: 'msg-2',
|
|
22
|
+
meta: {},
|
|
23
|
+
parentId: 'msg-1',
|
|
24
|
+
role: 'assistant',
|
|
25
|
+
updatedAt: 2000,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const result = buildHelperMaps(messages);
|
|
30
|
+
|
|
31
|
+
expect(result.messageMap.size).toBe(2);
|
|
32
|
+
expect(result.messageMap.get('msg-1')).toEqual(messages[0]);
|
|
33
|
+
expect(result.messageMap.get('msg-2')).toEqual(messages[1]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle empty messages array', () => {
|
|
37
|
+
const result = buildHelperMaps([]);
|
|
38
|
+
|
|
39
|
+
expect(result.messageMap.size).toBe(0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle single message', () => {
|
|
43
|
+
const messages: Message[] = [
|
|
44
|
+
{
|
|
45
|
+
content: 'Single message',
|
|
46
|
+
createdAt: 1000,
|
|
47
|
+
id: 'msg-1',
|
|
48
|
+
meta: {},
|
|
49
|
+
role: 'user',
|
|
50
|
+
updatedAt: 1000,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const result = buildHelperMaps(messages);
|
|
55
|
+
|
|
56
|
+
expect(result.messageMap.size).toBe(1);
|
|
57
|
+
expect(result.messageMap.get('msg-1')).toEqual(messages[0]);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('childrenMap', () => {
|
|
62
|
+
it('should build childrenMap for parent-child relationships', () => {
|
|
63
|
+
const messages: Message[] = [
|
|
64
|
+
{
|
|
65
|
+
content: 'Root',
|
|
66
|
+
createdAt: 1000,
|
|
67
|
+
id: 'msg-1',
|
|
68
|
+
meta: {},
|
|
69
|
+
role: 'user',
|
|
70
|
+
updatedAt: 1000,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
content: 'Child 1',
|
|
74
|
+
createdAt: 2000,
|
|
75
|
+
id: 'msg-2',
|
|
76
|
+
meta: {},
|
|
77
|
+
parentId: 'msg-1',
|
|
78
|
+
role: 'assistant',
|
|
79
|
+
updatedAt: 2000,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
content: 'Child 2',
|
|
83
|
+
createdAt: 3000,
|
|
84
|
+
id: 'msg-3',
|
|
85
|
+
meta: {},
|
|
86
|
+
parentId: 'msg-1',
|
|
87
|
+
role: 'assistant',
|
|
88
|
+
updatedAt: 3000,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const result = buildHelperMaps(messages);
|
|
93
|
+
|
|
94
|
+
expect(result.childrenMap.get(null)).toEqual(['msg-1']);
|
|
95
|
+
expect(result.childrenMap.get('msg-1')).toEqual(['msg-2', 'msg-3']);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle messages with no parent (root messages)', () => {
|
|
99
|
+
const messages: Message[] = [
|
|
100
|
+
{
|
|
101
|
+
content: 'Root 1',
|
|
102
|
+
createdAt: 1000,
|
|
103
|
+
id: 'msg-1',
|
|
104
|
+
meta: {},
|
|
105
|
+
role: 'user',
|
|
106
|
+
updatedAt: 1000,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
content: 'Root 2',
|
|
110
|
+
createdAt: 2000,
|
|
111
|
+
id: 'msg-2',
|
|
112
|
+
meta: {},
|
|
113
|
+
role: 'user',
|
|
114
|
+
updatedAt: 2000,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const result = buildHelperMaps(messages);
|
|
119
|
+
|
|
120
|
+
expect(result.childrenMap.get(null)).toEqual(['msg-1', 'msg-2']);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should handle deeply nested parent-child relationships', () => {
|
|
124
|
+
const messages: Message[] = [
|
|
125
|
+
{
|
|
126
|
+
content: 'Level 0',
|
|
127
|
+
createdAt: 1000,
|
|
128
|
+
id: 'msg-1',
|
|
129
|
+
meta: {},
|
|
130
|
+
role: 'user',
|
|
131
|
+
updatedAt: 1000,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
content: 'Level 1',
|
|
135
|
+
createdAt: 2000,
|
|
136
|
+
id: 'msg-2',
|
|
137
|
+
meta: {},
|
|
138
|
+
parentId: 'msg-1',
|
|
139
|
+
role: 'assistant',
|
|
140
|
+
updatedAt: 2000,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
content: 'Level 2',
|
|
144
|
+
createdAt: 3000,
|
|
145
|
+
id: 'msg-3',
|
|
146
|
+
meta: {},
|
|
147
|
+
parentId: 'msg-2',
|
|
148
|
+
role: 'user',
|
|
149
|
+
updatedAt: 3000,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
content: 'Level 3',
|
|
153
|
+
createdAt: 4000,
|
|
154
|
+
id: 'msg-4',
|
|
155
|
+
meta: {},
|
|
156
|
+
parentId: 'msg-3',
|
|
157
|
+
role: 'assistant',
|
|
158
|
+
updatedAt: 4000,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
const result = buildHelperMaps(messages);
|
|
163
|
+
|
|
164
|
+
expect(result.childrenMap.get(null)).toEqual(['msg-1']);
|
|
165
|
+
expect(result.childrenMap.get('msg-1')).toEqual(['msg-2']);
|
|
166
|
+
expect(result.childrenMap.get('msg-2')).toEqual(['msg-3']);
|
|
167
|
+
expect(result.childrenMap.get('msg-3')).toEqual(['msg-4']);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should handle branching conversations', () => {
|
|
171
|
+
const messages: Message[] = [
|
|
172
|
+
{
|
|
173
|
+
content: 'Root',
|
|
174
|
+
createdAt: 1000,
|
|
175
|
+
id: 'msg-1',
|
|
176
|
+
meta: {},
|
|
177
|
+
role: 'user',
|
|
178
|
+
updatedAt: 1000,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
content: 'Branch 1',
|
|
182
|
+
createdAt: 2000,
|
|
183
|
+
id: 'msg-2',
|
|
184
|
+
meta: {},
|
|
185
|
+
parentId: 'msg-1',
|
|
186
|
+
role: 'assistant',
|
|
187
|
+
updatedAt: 2000,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
content: 'Branch 2',
|
|
191
|
+
createdAt: 3000,
|
|
192
|
+
id: 'msg-3',
|
|
193
|
+
meta: {},
|
|
194
|
+
parentId: 'msg-1',
|
|
195
|
+
role: 'assistant',
|
|
196
|
+
updatedAt: 3000,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
content: 'Sub-branch 1',
|
|
200
|
+
createdAt: 4000,
|
|
201
|
+
id: 'msg-4',
|
|
202
|
+
meta: {},
|
|
203
|
+
parentId: 'msg-2',
|
|
204
|
+
role: 'user',
|
|
205
|
+
updatedAt: 4000,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
content: 'Sub-branch 2',
|
|
209
|
+
createdAt: 5000,
|
|
210
|
+
id: 'msg-5',
|
|
211
|
+
meta: {},
|
|
212
|
+
parentId: 'msg-2',
|
|
213
|
+
role: 'user',
|
|
214
|
+
updatedAt: 5000,
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
const result = buildHelperMaps(messages);
|
|
219
|
+
|
|
220
|
+
expect(result.childrenMap.get(null)).toEqual(['msg-1']);
|
|
221
|
+
expect(result.childrenMap.get('msg-1')).toEqual(['msg-2', 'msg-3']);
|
|
222
|
+
expect(result.childrenMap.get('msg-2')).toEqual(['msg-4', 'msg-5']);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('threadMap', () => {
|
|
227
|
+
it('should build threadMap for messages with threadId', () => {
|
|
228
|
+
const messages: Message[] = [
|
|
229
|
+
{
|
|
230
|
+
content: 'Main message',
|
|
231
|
+
createdAt: 1000,
|
|
232
|
+
id: 'msg-1',
|
|
233
|
+
meta: {},
|
|
234
|
+
role: 'user',
|
|
235
|
+
updatedAt: 1000,
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
content: 'Thread message 1',
|
|
239
|
+
createdAt: 2000,
|
|
240
|
+
id: 'msg-2',
|
|
241
|
+
meta: {},
|
|
242
|
+
parentId: 'msg-1',
|
|
243
|
+
role: 'assistant',
|
|
244
|
+
threadId: 'thread-1',
|
|
245
|
+
updatedAt: 2000,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
content: 'Thread message 2',
|
|
249
|
+
createdAt: 3000,
|
|
250
|
+
id: 'msg-3',
|
|
251
|
+
meta: {},
|
|
252
|
+
parentId: 'msg-2',
|
|
253
|
+
role: 'user',
|
|
254
|
+
threadId: 'thread-1',
|
|
255
|
+
updatedAt: 3000,
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
const result = buildHelperMaps(messages);
|
|
260
|
+
|
|
261
|
+
expect(result.threadMap.size).toBe(1);
|
|
262
|
+
expect(result.threadMap.get('thread-1')).toHaveLength(2);
|
|
263
|
+
expect(result.threadMap.get('thread-1')).toEqual([messages[1], messages[2]]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should handle multiple threads', () => {
|
|
267
|
+
const messages: Message[] = [
|
|
268
|
+
{
|
|
269
|
+
content: 'Thread 1 - msg 1',
|
|
270
|
+
createdAt: 1000,
|
|
271
|
+
id: 'msg-1',
|
|
272
|
+
meta: {},
|
|
273
|
+
role: 'user',
|
|
274
|
+
threadId: 'thread-1',
|
|
275
|
+
updatedAt: 1000,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
content: 'Thread 1 - msg 2',
|
|
279
|
+
createdAt: 2000,
|
|
280
|
+
id: 'msg-2',
|
|
281
|
+
meta: {},
|
|
282
|
+
role: 'assistant',
|
|
283
|
+
threadId: 'thread-1',
|
|
284
|
+
updatedAt: 2000,
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
content: 'Thread 2 - msg 1',
|
|
288
|
+
createdAt: 3000,
|
|
289
|
+
id: 'msg-3',
|
|
290
|
+
meta: {},
|
|
291
|
+
role: 'user',
|
|
292
|
+
threadId: 'thread-2',
|
|
293
|
+
updatedAt: 3000,
|
|
294
|
+
},
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
const result = buildHelperMaps(messages);
|
|
298
|
+
|
|
299
|
+
expect(result.threadMap.size).toBe(2);
|
|
300
|
+
expect(result.threadMap.get('thread-1')).toEqual([messages[0], messages[1]]);
|
|
301
|
+
expect(result.threadMap.get('thread-2')).toEqual([messages[2]]);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should not include messages without threadId in threadMap', () => {
|
|
305
|
+
const messages: Message[] = [
|
|
306
|
+
{
|
|
307
|
+
content: 'No thread',
|
|
308
|
+
createdAt: 1000,
|
|
309
|
+
id: 'msg-1',
|
|
310
|
+
meta: {},
|
|
311
|
+
role: 'user',
|
|
312
|
+
updatedAt: 1000,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
content: 'With thread',
|
|
316
|
+
createdAt: 2000,
|
|
317
|
+
id: 'msg-2',
|
|
318
|
+
meta: {},
|
|
319
|
+
role: 'assistant',
|
|
320
|
+
threadId: 'thread-1',
|
|
321
|
+
updatedAt: 2000,
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
const result = buildHelperMaps(messages);
|
|
326
|
+
|
|
327
|
+
expect(result.threadMap.size).toBe(1);
|
|
328
|
+
expect(result.threadMap.get('thread-1')).toEqual([messages[1]]);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should handle empty threadMap when no messages have threadId', () => {
|
|
332
|
+
const messages: Message[] = [
|
|
333
|
+
{
|
|
334
|
+
content: 'Message 1',
|
|
335
|
+
createdAt: 1000,
|
|
336
|
+
id: 'msg-1',
|
|
337
|
+
meta: {},
|
|
338
|
+
role: 'user',
|
|
339
|
+
updatedAt: 1000,
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
content: 'Message 2',
|
|
343
|
+
createdAt: 2000,
|
|
344
|
+
id: 'msg-2',
|
|
345
|
+
meta: {},
|
|
346
|
+
role: 'assistant',
|
|
347
|
+
updatedAt: 2000,
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
const result = buildHelperMaps(messages);
|
|
352
|
+
|
|
353
|
+
expect(result.threadMap.size).toBe(0);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('messageGroupMap', () => {
|
|
358
|
+
it('should build messageGroupMap from provided metadata', () => {
|
|
359
|
+
const messages: Message[] = [
|
|
360
|
+
{
|
|
361
|
+
content: 'Message',
|
|
362
|
+
createdAt: 1000,
|
|
363
|
+
id: 'msg-1',
|
|
364
|
+
meta: {},
|
|
365
|
+
role: 'user',
|
|
366
|
+
updatedAt: 1000,
|
|
367
|
+
},
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
const messageGroups: MessageGroupMetadata[] = [
|
|
371
|
+
{
|
|
372
|
+
id: 'group-1',
|
|
373
|
+
mode: 'compare',
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: 'group-2',
|
|
377
|
+
mode: 'summary',
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
|
|
381
|
+
const result = buildHelperMaps(messages, messageGroups);
|
|
382
|
+
|
|
383
|
+
expect(result.messageGroupMap.size).toBe(2);
|
|
384
|
+
expect(result.messageGroupMap.get('group-1')).toEqual(messageGroups[0]);
|
|
385
|
+
expect(result.messageGroupMap.get('group-2')).toEqual(messageGroups[1]);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should handle empty messageGroupMap when no metadata provided', () => {
|
|
389
|
+
const messages: Message[] = [
|
|
390
|
+
{
|
|
391
|
+
content: 'Message',
|
|
392
|
+
createdAt: 1000,
|
|
393
|
+
id: 'msg-1',
|
|
394
|
+
meta: {},
|
|
395
|
+
role: 'user',
|
|
396
|
+
updatedAt: 1000,
|
|
397
|
+
},
|
|
398
|
+
];
|
|
399
|
+
|
|
400
|
+
const result = buildHelperMaps(messages);
|
|
401
|
+
|
|
402
|
+
expect(result.messageGroupMap.size).toBe(0);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should handle empty messageGroupMap when empty array provided', () => {
|
|
406
|
+
const messages: Message[] = [
|
|
407
|
+
{
|
|
408
|
+
content: 'Message',
|
|
409
|
+
createdAt: 1000,
|
|
410
|
+
id: 'msg-1',
|
|
411
|
+
meta: {},
|
|
412
|
+
role: 'user',
|
|
413
|
+
updatedAt: 1000,
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
|
|
417
|
+
const result = buildHelperMaps(messages, []);
|
|
418
|
+
|
|
419
|
+
expect(result.messageGroupMap.size).toBe(0);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe('integration scenarios', () => {
|
|
424
|
+
it('should build all maps correctly in complex conversation', () => {
|
|
425
|
+
const messages: Message[] = [
|
|
426
|
+
{
|
|
427
|
+
content: 'Root message',
|
|
428
|
+
createdAt: 1000,
|
|
429
|
+
id: 'msg-1',
|
|
430
|
+
meta: {},
|
|
431
|
+
role: 'user',
|
|
432
|
+
updatedAt: 1000,
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
content: 'Assistant response',
|
|
436
|
+
createdAt: 2000,
|
|
437
|
+
groupId: 'group-1',
|
|
438
|
+
id: 'msg-2',
|
|
439
|
+
meta: {},
|
|
440
|
+
parentId: 'msg-1',
|
|
441
|
+
role: 'assistant',
|
|
442
|
+
updatedAt: 2000,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
content: 'Thread message',
|
|
446
|
+
createdAt: 3000,
|
|
447
|
+
id: 'msg-3',
|
|
448
|
+
meta: {},
|
|
449
|
+
parentId: 'msg-2',
|
|
450
|
+
role: 'user',
|
|
451
|
+
threadId: 'thread-1',
|
|
452
|
+
updatedAt: 3000,
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
content: 'Branch message',
|
|
456
|
+
createdAt: 4000,
|
|
457
|
+
id: 'msg-4',
|
|
458
|
+
meta: {},
|
|
459
|
+
parentId: 'msg-1',
|
|
460
|
+
role: 'assistant',
|
|
461
|
+
updatedAt: 4000,
|
|
462
|
+
},
|
|
463
|
+
];
|
|
464
|
+
|
|
465
|
+
const messageGroups: MessageGroupMetadata[] = [
|
|
466
|
+
{
|
|
467
|
+
id: 'group-1',
|
|
468
|
+
mode: 'compare',
|
|
469
|
+
},
|
|
470
|
+
];
|
|
471
|
+
|
|
472
|
+
const result = buildHelperMaps(messages, messageGroups);
|
|
473
|
+
|
|
474
|
+
// Verify messageMap
|
|
475
|
+
expect(result.messageMap.size).toBe(4);
|
|
476
|
+
|
|
477
|
+
// Verify childrenMap
|
|
478
|
+
expect(result.childrenMap.get(null)).toEqual(['msg-1']);
|
|
479
|
+
expect(result.childrenMap.get('msg-1')).toEqual(['msg-2', 'msg-4']);
|
|
480
|
+
expect(result.childrenMap.get('msg-2')).toEqual(['msg-3']);
|
|
481
|
+
|
|
482
|
+
// Verify threadMap
|
|
483
|
+
expect(result.threadMap.size).toBe(1);
|
|
484
|
+
expect(result.threadMap.get('thread-1')).toEqual([messages[2]]);
|
|
485
|
+
|
|
486
|
+
// Verify messageGroupMap
|
|
487
|
+
expect(result.messageGroupMap.size).toBe(1);
|
|
488
|
+
expect(result.messageGroupMap.get('group-1')).toEqual(messageGroups[0]);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('should handle large number of messages efficiently', () => {
|
|
492
|
+
const messages: Message[] = Array.from({ length: 1000 }, (_, i) => ({
|
|
493
|
+
content: `Message ${i}`,
|
|
494
|
+
createdAt: i,
|
|
495
|
+
id: `msg-${i}`,
|
|
496
|
+
meta: {},
|
|
497
|
+
parentId: i > 0 ? `msg-${i - 1}` : undefined,
|
|
498
|
+
role: i % 2 === 0 ? ('user' as const) : ('assistant' as const),
|
|
499
|
+
updatedAt: i,
|
|
500
|
+
}));
|
|
501
|
+
|
|
502
|
+
const startTime = performance.now();
|
|
503
|
+
const result = buildHelperMaps(messages);
|
|
504
|
+
const endTime = performance.now();
|
|
505
|
+
|
|
506
|
+
const executionTime = endTime - startTime;
|
|
507
|
+
|
|
508
|
+
expect(result.messageMap.size).toBe(1000);
|
|
509
|
+
expect(result.childrenMap.size).toBe(1000);
|
|
510
|
+
expect(executionTime).toBeLessThan(50); // Should be fast
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
});
|