mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix: fix cannot uncompressed messages (#12086)
* support uncompressed * fix open new
This commit is contained in:
@@ -32,6 +32,8 @@
|
||||
"chatList.longMessageDetail": "View Details",
|
||||
"clearCurrentMessages": "Clear current session messages",
|
||||
"compressedHistory": "Compressed History",
|
||||
"compression.cancel": "Uncompress",
|
||||
"compression.cancelConfirm": "Are you sure you want to uncompress? This will restore the original messages.",
|
||||
"compression.history": "History",
|
||||
"compression.summary": "Summary",
|
||||
"confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be retrieved. Please confirm your action.",
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"chatList.longMessageDetail": "查看详情",
|
||||
"clearCurrentMessages": "清空当前会话消息",
|
||||
"compressedHistory": "压缩历史",
|
||||
"compression.cancel": "取消压缩",
|
||||
"compression.cancelConfirm": "确定要取消压缩吗?这将恢复原始消息。",
|
||||
"compression.history": "历史记录",
|
||||
"compression.summary": "摘要",
|
||||
"confirmClearCurrentMessages": "确认清空当前会话消息吗?清空后无法恢复",
|
||||
|
||||
@@ -149,6 +149,7 @@ export const messages = pgTable(
|
||||
index('messages_thread_id_idx').on(table.threadId),
|
||||
index('messages_agent_id_idx').on(table.agentId),
|
||||
index('messages_group_id_idx').on(table.groupId),
|
||||
index('messages_message_group_id_idx').on(table.messageGroupId),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const Nav = memo(() => {
|
||||
const switchTopic = useChatStore((s) => s.switchTopic);
|
||||
const [openNewTopicOrSaveTopic] = useChatStore((s) => [s.openNewTopicOrSaveTopic]);
|
||||
|
||||
const { mutate, isValidating } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
|
||||
const { mutate } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
|
||||
const handleNewTopic = () => {
|
||||
// If in agent sub-route, navigate back to agent chat first
|
||||
if (isProfileActive && agentId) {
|
||||
@@ -46,7 +46,6 @@ const Nav = memo(() => {
|
||||
<Flexbox gap={1} paddingInline={4}>
|
||||
<NavItem
|
||||
icon={MessageSquarePlusIcon}
|
||||
loading={isValidating}
|
||||
onClick={handleNewTopic}
|
||||
title={tTopic('actions.addNewTopic')}
|
||||
/>
|
||||
|
||||
@@ -10,9 +10,10 @@ import {
|
||||
Tabs,
|
||||
type TabsProps,
|
||||
} from '@lobehub/ui';
|
||||
import { App } from 'antd';
|
||||
import { createStaticStyles, cx } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { ChevronDown, ChevronUp, History, Sparkles } from 'lucide-react';
|
||||
import { ChevronDown, ChevronUp, History, Sparkles, Undo2 } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -66,6 +67,7 @@ export interface CompressedGroupMessageProps {
|
||||
|
||||
const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
||||
const { t } = useTranslation('chat');
|
||||
const { modal } = App.useApp();
|
||||
const [activeTab, setActiveTab] = useState<string>(() => getStoredTab(id));
|
||||
|
||||
const handleTabChange = useCallback(
|
||||
@@ -80,6 +82,16 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
||||
const toggleCompressedGroupExpanded = useConversationStore(
|
||||
(s) => s.toggleCompressedGroupExpanded,
|
||||
);
|
||||
const cancelCompression = useConversationStore((s) => s.cancelCompression);
|
||||
|
||||
const handleCancelCompression = useCallback(() => {
|
||||
modal.confirm({
|
||||
centered: true,
|
||||
content: t('compression.cancelConfirm'),
|
||||
onOk: () => cancelCompression(id),
|
||||
title: t('compression.cancel'),
|
||||
});
|
||||
}, [id, cancelCompression, modal, t]);
|
||||
|
||||
const content = message?.content;
|
||||
const rawCompressedMessages = (message as UIChatMessage)?.compressedMessages;
|
||||
@@ -145,11 +157,19 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
|
||||
onChange={handleTabChange}
|
||||
variant={'rounded'}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={expanded ? ChevronUp : ChevronDown}
|
||||
onClick={() => toggleCompressedGroupExpanded(id)}
|
||||
size={'small'}
|
||||
/>
|
||||
<Flexbox gap={4} horizontal>
|
||||
<ActionIcon
|
||||
icon={Undo2}
|
||||
onClick={handleCancelCompression}
|
||||
size={'small'}
|
||||
title={t('compression.cancel')}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={expanded ? ChevronUp : ChevronDown}
|
||||
onClick={() => toggleCompressedGroupExpanded(id)}
|
||||
size={'small'}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
)}
|
||||
{!showContent ? null : activeTab === 'summary' ? (
|
||||
|
||||
@@ -13,6 +13,11 @@ import { dataSelectors } from '../../data/selectors';
|
||||
* Handles message state operations like loading, collapsed, etc.
|
||||
*/
|
||||
export interface MessageStateAction {
|
||||
/**
|
||||
* Cancel compression and restore original messages
|
||||
*/
|
||||
cancelCompression: (id: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Copy message content to clipboard
|
||||
*/
|
||||
@@ -50,6 +55,26 @@ export const messageStateSlice: StateCreator<
|
||||
[],
|
||||
MessageStateAction
|
||||
> = (set, get) => ({
|
||||
cancelCompression: async (id) => {
|
||||
const message = dataSelectors.getDisplayMessageById(id)(get());
|
||||
if (!message || message.role !== 'compressedGroup') return;
|
||||
|
||||
const { context, replaceMessages } = get();
|
||||
if (!context.agentId || !context.topicId) return;
|
||||
|
||||
// Call service to cancel compression
|
||||
const { messages } = await messageService.cancelCompression({
|
||||
agentId: context.agentId,
|
||||
groupId: context.groupId,
|
||||
messageGroupId: id,
|
||||
threadId: context.threadId,
|
||||
topicId: context.topicId,
|
||||
});
|
||||
|
||||
// Replace messages with restored original messages
|
||||
replaceMessages(messages);
|
||||
},
|
||||
|
||||
copyMessage: async (id, content) => {
|
||||
const { hooks } = get();
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ export default {
|
||||
'chatList.longMessageDetail': 'View Details',
|
||||
'clearCurrentMessages': 'Clear current session messages',
|
||||
'compressedHistory': 'Compressed History',
|
||||
'compression.cancel': 'Uncompress',
|
||||
'compression.cancelConfirm':
|
||||
'Are you sure you want to uncompress? This will restore the original messages.',
|
||||
'compression.history': 'History',
|
||||
'compression.summary': 'Summary',
|
||||
'confirmClearCurrentMessages':
|
||||
|
||||
@@ -48,7 +48,32 @@ export const messageRouter = router({
|
||||
return ctx.messageService.addFilesToMessage(id, fileIds, resolved);
|
||||
}),
|
||||
|
||||
count: messageProcedure
|
||||
/**
|
||||
* Cancel compression by deleting the compression group and restoring original messages
|
||||
*/
|
||||
cancelCompression: messageProcedure
|
||||
.input(
|
||||
z.object({
|
||||
agentId: z.string(),
|
||||
groupId: z.string().nullable().optional(),
|
||||
messageGroupId: z.string(),
|
||||
threadId: z.string().nullable().optional(),
|
||||
topicId: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { messageGroupId, agentId, groupId, threadId, topicId } = input;
|
||||
|
||||
return ctx.messageService.cancelCompression(messageGroupId, {
|
||||
agentId,
|
||||
groupId,
|
||||
threadId,
|
||||
topicId,
|
||||
});
|
||||
}),
|
||||
|
||||
|
||||
count: messageProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
@@ -62,7 +87,9 @@ export const messageRouter = router({
|
||||
return ctx.messageModel.count(input);
|
||||
}),
|
||||
|
||||
countWords: messageProcedure
|
||||
|
||||
|
||||
countWords: messageProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
@@ -76,12 +103,13 @@ export const messageRouter = router({
|
||||
return ctx.messageModel.countWords(input);
|
||||
}),
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Create a compression group for old messages
|
||||
* Creates a placeholder group, marks messages as compressed
|
||||
* Returns messages to summarize for frontend AI generation
|
||||
*/
|
||||
createCompressionGroup: messageProcedure
|
||||
createCompressionGroup: messageProcedure
|
||||
.input(
|
||||
z.object({
|
||||
agentId: z.string(),
|
||||
@@ -102,7 +130,9 @@ export const messageRouter = router({
|
||||
});
|
||||
}),
|
||||
|
||||
createMessage: messageProcedure
|
||||
|
||||
|
||||
createMessage: messageProcedure
|
||||
.input(CreateNewMessageParamsSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
// If there's no agentId but has sessionId, resolve agentId from sessionId
|
||||
@@ -115,10 +145,11 @@ export const messageRouter = router({
|
||||
return ctx.messageService.createMessage({ ...input, agentId } as any);
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* Finalize compression by updating the group with generated summary
|
||||
*/
|
||||
finalizeCompression: messageProcedure
|
||||
finalizeCompression: messageProcedure
|
||||
.input(
|
||||
z.object({
|
||||
agentId: z.string(),
|
||||
|
||||
@@ -359,4 +359,23 @@ export class MessageService {
|
||||
|
||||
return { messages };
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel compression by deleting the compression group and restoring original messages
|
||||
*
|
||||
* @param messageGroupId - The compression group ID to cancel
|
||||
* @param context - Query options for returning updated messages
|
||||
*/
|
||||
async cancelCompression(
|
||||
messageGroupId: string,
|
||||
context: QueryOptions,
|
||||
): Promise<{ messages: UIChatMessage[]; success: boolean }> {
|
||||
// Delete compression group (this also unmarks messages)
|
||||
await this.compressionRepository.deleteCompressionGroup(messageGroupId);
|
||||
|
||||
// Query updated messages
|
||||
const messages = await this.messageModel.query(context, this.getQueryOptions());
|
||||
|
||||
return { messages, success: true };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +276,20 @@ export class MessageService {
|
||||
messages: (result.messages || []) as unknown as UIChatMessage[],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel compression by deleting the compression group and restoring original messages
|
||||
*/
|
||||
cancelCompression = async (params: {
|
||||
agentId: string;
|
||||
groupId?: string | null;
|
||||
messageGroupId: string;
|
||||
threadId?: string | null;
|
||||
topicId: string;
|
||||
}): Promise<{ messages: UIChatMessage[] }> => {
|
||||
const result = await lambdaClient.message.cancelCompression.mutate(params);
|
||||
return { messages: (result.messages || []) as unknown as UIChatMessage[] };
|
||||
};
|
||||
}
|
||||
|
||||
export const messageService = new MessageService();
|
||||
|
||||
Reference in New Issue
Block a user