🐛 fix: fix cannot uncompressed messages (#12086)

* support uncompressed
* fix open new
This commit is contained in:
Arvin Xu
2026-02-04 12:25:28 +08:00
committed by GitHub
parent 8aba59bffd
commit ccfaec2fdb
10 changed files with 130 additions and 14 deletions

View File

@@ -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.",

View File

@@ -32,6 +32,8 @@
"chatList.longMessageDetail": "查看详情",
"clearCurrentMessages": "清空当前会话消息",
"compressedHistory": "压缩历史",
"compression.cancel": "取消压缩",
"compression.cancelConfirm": "确定要取消压缩吗?这将恢复原始消息。",
"compression.history": "历史记录",
"compression.summary": "摘要",
"confirmClearCurrentMessages": "确认清空当前会话消息吗?清空后无法恢复",

View File

@@ -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),
],
);

View File

@@ -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')}
/>

View File

@@ -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' ? (

View File

@@ -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();

View File

@@ -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':

View File

@@ -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(),

View File

@@ -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 };
}
}

View File

@@ -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();