@lobehub/lobehub 2.1.13 → 2.1.14
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/apps/desktop/src/main/menus/impls/linux.ts +1 -1
- package/apps/desktop/src/main/menus/impls/macOS.ts +8 -8
- package/apps/desktop/src/main/menus/impls/windows.ts +1 -1
- package/changelog/v2.json +9 -0
- package/docs/development/database-schema.dbml +1 -0
- package/locales/en-US/chat.json +2 -0
- package/locales/zh-CN/chat.json +2 -0
- package/package.json +1 -1
- package/packages/database/migrations/0076_add_message_group_index.sql +1 -0
- package/packages/database/migrations/meta/0076_snapshot.json +11270 -0
- package/packages/database/migrations/meta/_journal.json +8 -1
- package/packages/database/src/schemas/message.ts +1 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +1 -2
- package/src/features/Conversation/Messages/CompressedGroup/index.tsx +26 -6
- package/src/features/Conversation/store/slices/message/action/state.ts +25 -0
- package/src/locales/default/chat.ts +3 -0
- package/src/server/routers/lambda/message.ts +37 -6
- package/src/server/services/message/index.ts +19 -0
- package/src/services/message/index.ts +14 -0
|
@@ -532,7 +532,14 @@
|
|
|
532
532
|
"when": 1769362978088,
|
|
533
533
|
"tag": "0075_add_user_memory_persona",
|
|
534
534
|
"breakpoints": true
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
"idx": 76,
|
|
538
|
+
"version": "7",
|
|
539
|
+
"when": 1770179814971,
|
|
540
|
+
"tag": "0076_add_message_group_index",
|
|
541
|
+
"breakpoints": true
|
|
535
542
|
}
|
|
536
543
|
],
|
|
537
544
|
"version": "6"
|
|
538
|
-
}
|
|
545
|
+
}
|
|
@@ -149,6 +149,7 @@ export const messages = pgTable(
|
|
|
149
149
|
index('messages_thread_id_idx').on(table.threadId),
|
|
150
150
|
index('messages_agent_id_idx').on(table.agentId),
|
|
151
151
|
index('messages_group_id_idx').on(table.groupId),
|
|
152
|
+
index('messages_message_group_id_idx').on(table.messageGroupId),
|
|
152
153
|
],
|
|
153
154
|
);
|
|
154
155
|
|
|
@@ -33,7 +33,7 @@ const Nav = memo(() => {
|
|
|
33
33
|
const switchTopic = useChatStore((s) => s.switchTopic);
|
|
34
34
|
const [openNewTopicOrSaveTopic] = useChatStore((s) => [s.openNewTopicOrSaveTopic]);
|
|
35
35
|
|
|
36
|
-
const { mutate
|
|
36
|
+
const { mutate } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
|
|
37
37
|
const handleNewTopic = () => {
|
|
38
38
|
// If in agent sub-route, navigate back to agent chat first
|
|
39
39
|
if (isProfileActive && agentId) {
|
|
@@ -46,7 +46,6 @@ const Nav = memo(() => {
|
|
|
46
46
|
<Flexbox gap={1} paddingInline={4}>
|
|
47
47
|
<NavItem
|
|
48
48
|
icon={MessageSquarePlusIcon}
|
|
49
|
-
loading={isValidating}
|
|
50
49
|
onClick={handleNewTopic}
|
|
51
50
|
title={tTopic('actions.addNewTopic')}
|
|
52
51
|
/>
|
|
@@ -10,9 +10,10 @@ import {
|
|
|
10
10
|
Tabs,
|
|
11
11
|
type TabsProps,
|
|
12
12
|
} from '@lobehub/ui';
|
|
13
|
+
import { App } from 'antd';
|
|
13
14
|
import { createStaticStyles, cx } from 'antd-style';
|
|
14
15
|
import isEqual from 'fast-deep-equal';
|
|
15
|
-
import { ChevronDown, ChevronUp, History, Sparkles } from 'lucide-react';
|
|
16
|
+
import { ChevronDown, ChevronUp, History, Sparkles, Undo2 } from 'lucide-react';
|
|
16
17
|
import { memo, useCallback, useMemo, useState } from 'react';
|
|
17
18
|
import { useTranslation } from 'react-i18next';
|
|
18
19
|
|
|
@@ -66,6 +67,7 @@ export interface CompressedGroupMessageProps {
|
|
|
66
67
|
|
|
67
68
|
const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
|
68
69
|
const { t } = useTranslation('chat');
|
|
70
|
+
const { modal } = App.useApp();
|
|
69
71
|
const [activeTab, setActiveTab] = useState<string>(() => getStoredTab(id));
|
|
70
72
|
|
|
71
73
|
const handleTabChange = useCallback(
|
|
@@ -80,6 +82,16 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
|
|
80
82
|
const toggleCompressedGroupExpanded = useConversationStore(
|
|
81
83
|
(s) => s.toggleCompressedGroupExpanded,
|
|
82
84
|
);
|
|
85
|
+
const cancelCompression = useConversationStore((s) => s.cancelCompression);
|
|
86
|
+
|
|
87
|
+
const handleCancelCompression = useCallback(() => {
|
|
88
|
+
modal.confirm({
|
|
89
|
+
centered: true,
|
|
90
|
+
content: t('compression.cancelConfirm'),
|
|
91
|
+
onOk: () => cancelCompression(id),
|
|
92
|
+
title: t('compression.cancel'),
|
|
93
|
+
});
|
|
94
|
+
}, [id, cancelCompression, modal, t]);
|
|
83
95
|
|
|
84
96
|
const content = message?.content;
|
|
85
97
|
const rawCompressedMessages = (message as UIChatMessage)?.compressedMessages;
|
|
@@ -145,11 +157,19 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
|
|
145
157
|
onChange={handleTabChange}
|
|
146
158
|
variant={'rounded'}
|
|
147
159
|
/>
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
160
|
+
<Flexbox gap={4} horizontal>
|
|
161
|
+
<ActionIcon
|
|
162
|
+
icon={Undo2}
|
|
163
|
+
onClick={handleCancelCompression}
|
|
164
|
+
size={'small'}
|
|
165
|
+
title={t('compression.cancel')}
|
|
166
|
+
/>
|
|
167
|
+
<ActionIcon
|
|
168
|
+
icon={expanded ? ChevronUp : ChevronDown}
|
|
169
|
+
onClick={() => toggleCompressedGroupExpanded(id)}
|
|
170
|
+
size={'small'}
|
|
171
|
+
/>
|
|
172
|
+
</Flexbox>
|
|
153
173
|
</Flexbox>
|
|
154
174
|
)}
|
|
155
175
|
{!showContent ? null : activeTab === 'summary' ? (
|
|
@@ -13,6 +13,11 @@ import { dataSelectors } from '../../data/selectors';
|
|
|
13
13
|
* Handles message state operations like loading, collapsed, etc.
|
|
14
14
|
*/
|
|
15
15
|
export interface MessageStateAction {
|
|
16
|
+
/**
|
|
17
|
+
* Cancel compression and restore original messages
|
|
18
|
+
*/
|
|
19
|
+
cancelCompression: (id: string) => Promise<void>;
|
|
20
|
+
|
|
16
21
|
/**
|
|
17
22
|
* Copy message content to clipboard
|
|
18
23
|
*/
|
|
@@ -50,6 +55,26 @@ export const messageStateSlice: StateCreator<
|
|
|
50
55
|
[],
|
|
51
56
|
MessageStateAction
|
|
52
57
|
> = (set, get) => ({
|
|
58
|
+
cancelCompression: async (id) => {
|
|
59
|
+
const message = dataSelectors.getDisplayMessageById(id)(get());
|
|
60
|
+
if (!message || message.role !== 'compressedGroup') return;
|
|
61
|
+
|
|
62
|
+
const { context, replaceMessages } = get();
|
|
63
|
+
if (!context.agentId || !context.topicId) return;
|
|
64
|
+
|
|
65
|
+
// Call service to cancel compression
|
|
66
|
+
const { messages } = await messageService.cancelCompression({
|
|
67
|
+
agentId: context.agentId,
|
|
68
|
+
groupId: context.groupId,
|
|
69
|
+
messageGroupId: id,
|
|
70
|
+
threadId: context.threadId,
|
|
71
|
+
topicId: context.topicId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Replace messages with restored original messages
|
|
75
|
+
replaceMessages(messages);
|
|
76
|
+
},
|
|
77
|
+
|
|
53
78
|
copyMessage: async (id, content) => {
|
|
54
79
|
const { hooks } = get();
|
|
55
80
|
|
|
@@ -41,6 +41,9 @@ export default {
|
|
|
41
41
|
'chatList.longMessageDetail': 'View Details',
|
|
42
42
|
'clearCurrentMessages': 'Clear current session messages',
|
|
43
43
|
'compressedHistory': 'Compressed History',
|
|
44
|
+
'compression.cancel': 'Uncompress',
|
|
45
|
+
'compression.cancelConfirm':
|
|
46
|
+
'Are you sure you want to uncompress? This will restore the original messages.',
|
|
44
47
|
'compression.history': 'History',
|
|
45
48
|
'compression.summary': 'Summary',
|
|
46
49
|
'confirmClearCurrentMessages':
|
|
@@ -48,7 +48,32 @@ export const messageRouter = router({
|
|
|
48
48
|
return ctx.messageService.addFilesToMessage(id, fileIds, resolved);
|
|
49
49
|
}),
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Cancel compression by deleting the compression group and restoring original messages
|
|
53
|
+
*/
|
|
54
|
+
cancelCompression: messageProcedure
|
|
55
|
+
.input(
|
|
56
|
+
z.object({
|
|
57
|
+
agentId: z.string(),
|
|
58
|
+
groupId: z.string().nullable().optional(),
|
|
59
|
+
messageGroupId: z.string(),
|
|
60
|
+
threadId: z.string().nullable().optional(),
|
|
61
|
+
topicId: z.string(),
|
|
62
|
+
}),
|
|
63
|
+
)
|
|
64
|
+
.mutation(async ({ input, ctx }) => {
|
|
65
|
+
const { messageGroupId, agentId, groupId, threadId, topicId } = input;
|
|
66
|
+
|
|
67
|
+
return ctx.messageService.cancelCompression(messageGroupId, {
|
|
68
|
+
agentId,
|
|
69
|
+
groupId,
|
|
70
|
+
threadId,
|
|
71
|
+
topicId,
|
|
72
|
+
});
|
|
73
|
+
}),
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
count: messageProcedure
|
|
52
77
|
.input(
|
|
53
78
|
z
|
|
54
79
|
.object({
|
|
@@ -62,7 +87,9 @@ export const messageRouter = router({
|
|
|
62
87
|
return ctx.messageModel.count(input);
|
|
63
88
|
}),
|
|
64
89
|
|
|
65
|
-
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
countWords: messageProcedure
|
|
66
93
|
.input(
|
|
67
94
|
z
|
|
68
95
|
.object({
|
|
@@ -76,12 +103,13 @@ export const messageRouter = router({
|
|
|
76
103
|
return ctx.messageModel.countWords(input);
|
|
77
104
|
}),
|
|
78
105
|
|
|
79
|
-
|
|
106
|
+
|
|
107
|
+
/**
|
|
80
108
|
* Create a compression group for old messages
|
|
81
109
|
* Creates a placeholder group, marks messages as compressed
|
|
82
110
|
* Returns messages to summarize for frontend AI generation
|
|
83
111
|
*/
|
|
84
|
-
|
|
112
|
+
createCompressionGroup: messageProcedure
|
|
85
113
|
.input(
|
|
86
114
|
z.object({
|
|
87
115
|
agentId: z.string(),
|
|
@@ -102,7 +130,9 @@ export const messageRouter = router({
|
|
|
102
130
|
});
|
|
103
131
|
}),
|
|
104
132
|
|
|
105
|
-
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
createMessage: messageProcedure
|
|
106
136
|
.input(CreateNewMessageParamsSchema)
|
|
107
137
|
.mutation(async ({ input, ctx }) => {
|
|
108
138
|
// If there's no agentId but has sessionId, resolve agentId from sessionId
|
|
@@ -115,10 +145,11 @@ export const messageRouter = router({
|
|
|
115
145
|
return ctx.messageService.createMessage({ ...input, agentId } as any);
|
|
116
146
|
}),
|
|
117
147
|
|
|
148
|
+
|
|
118
149
|
/**
|
|
119
150
|
* Finalize compression by updating the group with generated summary
|
|
120
151
|
*/
|
|
121
|
-
|
|
152
|
+
finalizeCompression: messageProcedure
|
|
122
153
|
.input(
|
|
123
154
|
z.object({
|
|
124
155
|
agentId: z.string(),
|
|
@@ -359,4 +359,23 @@ export class MessageService {
|
|
|
359
359
|
|
|
360
360
|
return { messages };
|
|
361
361
|
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Cancel compression by deleting the compression group and restoring original messages
|
|
365
|
+
*
|
|
366
|
+
* @param messageGroupId - The compression group ID to cancel
|
|
367
|
+
* @param context - Query options for returning updated messages
|
|
368
|
+
*/
|
|
369
|
+
async cancelCompression(
|
|
370
|
+
messageGroupId: string,
|
|
371
|
+
context: QueryOptions,
|
|
372
|
+
): Promise<{ messages: UIChatMessage[]; success: boolean }> {
|
|
373
|
+
// Delete compression group (this also unmarks messages)
|
|
374
|
+
await this.compressionRepository.deleteCompressionGroup(messageGroupId);
|
|
375
|
+
|
|
376
|
+
// Query updated messages
|
|
377
|
+
const messages = await this.messageModel.query(context, this.getQueryOptions());
|
|
378
|
+
|
|
379
|
+
return { messages, success: true };
|
|
380
|
+
}
|
|
362
381
|
}
|
|
@@ -276,6 +276,20 @@ export class MessageService {
|
|
|
276
276
|
messages: (result.messages || []) as unknown as UIChatMessage[],
|
|
277
277
|
};
|
|
278
278
|
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Cancel compression by deleting the compression group and restoring original messages
|
|
282
|
+
*/
|
|
283
|
+
cancelCompression = async (params: {
|
|
284
|
+
agentId: string;
|
|
285
|
+
groupId?: string | null;
|
|
286
|
+
messageGroupId: string;
|
|
287
|
+
threadId?: string | null;
|
|
288
|
+
topicId: string;
|
|
289
|
+
}): Promise<{ messages: UIChatMessage[] }> => {
|
|
290
|
+
const result = await lambdaClient.message.cancelCompression.mutate(params);
|
|
291
|
+
return { messages: (result.messages || []) as unknown as UIChatMessage[] };
|
|
292
|
+
};
|
|
279
293
|
}
|
|
280
294
|
|
|
281
295
|
export const messageService = new MessageService();
|