🐛 fix: fixed compressed group message & open the switch config to control compression config enabled (#11901)

* fix: fixed the compressed cause the parentid not found problem

* feat: add the compressionConfig config as switch

* fix: slove the onboarding modelSelect Crash error

* fix: rollback the modelSelectChange
This commit is contained in:
Shinji-Li
2026-01-27 20:56:48 +08:00
committed by GitHub
parent 888c907a45
commit dc51838b3c
11 changed files with 83 additions and 24 deletions

View File

@@ -446,6 +446,8 @@
"settingImage.defaultCount.desc": "Set the default number of images generated when creating a new task in the image generation panel.",
"settingImage.defaultCount.label": "Default Image Count",
"settingImage.defaultCount.title": "AI Art",
"settingModel.enableContextCompression.desc": "Automatically compress historical messages into summaries when conversation exceeds 64,000 tokens, saving 60-80% token usage",
"settingModel.enableContextCompression.title": "Enable Auto Context Compression",
"settingModel.enableMaxTokens.title": "Enable Max Tokens Limit",
"settingModel.enableReasoningEffort.title": "Enable Reasoning Effort Adjustment",
"settingModel.frequencyPenalty.desc": "The higher the value, the more diverse and rich the vocabulary; the lower the value, the simpler and more straightforward the language.",

View File

@@ -446,6 +446,8 @@
"settingImage.defaultCount.desc": "设置图像生成面板在创建新任务时的默认图片数量。",
"settingImage.defaultCount.label": "默认图片数量",
"settingImage.defaultCount.title": "AI 绘画设置",
"settingModel.enableContextCompression.desc": "当对话消息超过 64,000 tokens 时,自动将历史消息压缩为摘要,节省 60-80% 的 token 用量",
"settingModel.enableContextCompression.title": "开启自动上下文压缩",
"settingModel.enableMaxTokens.title": "开启单次回复限制",
"settingModel.enableReasoningEffort.title": "开启推理强度调整",
"settingModel.frequencyPenalty.desc": "值越大,用词越丰富多样;值越低,用词更朴实简单",

View File

@@ -328,7 +328,7 @@
"remark-gfm": "^4.0.1",
"remark-html": "^16.0.1",
"remove-markdown": "^0.6.3",
"resend": "^6.8.0",
"resend": "6.8.0",
"resolve-accept-language": "^3.1.15",
"rtl-detect": "^1.1.2",
"semver": "^7.7.3",

View File

@@ -283,21 +283,25 @@ export class GeneralChatAgent implements Agent {
switch (context.phase) {
case 'init':
case 'user_input': {
// Check if context compression is needed before calling LLM
const compressionCheck = shouldCompress(state.messages, {
maxWindowToken: this.config.compressionConfig?.maxWindowToken,
});
// Check if context compression is enabled and needed before calling LLM
const compressionEnabled = this.config.compressionConfig?.enabled ?? true; // Default to enabled
if (compressionCheck.needsCompression) {
// Context exceeds threshold, compress ALL messages into a single summary
return {
payload: {
currentTokenCount: compressionCheck.currentTokenCount,
existingSummary: this.findExistingSummary(state.messages),
messages: state.messages,
},
type: 'compress_context',
} as AgentInstructionCompressContext;
if (compressionEnabled) {
const compressionCheck = shouldCompress(state.messages, {
maxWindowToken: this.config.compressionConfig?.maxWindowToken,
});
if (compressionCheck.needsCompression) {
// Context exceeds threshold, compress ALL messages into a single summary
return {
payload: {
currentTokenCount: compressionCheck.currentTokenCount,
existingSummary: this.findExistingSummary(state.messages),
messages: state.messages,
},
type: 'compress_context',
} as AgentInstructionCompressContext;
}
}
// User input received, call LLM to generate response

View File

@@ -69,16 +69,15 @@ export interface GeneralAgentConfig {
};
/**
* Context compression configuration
* Note: Compression checking is always enabled to prevent context overflow.
* When triggered, ALL messages are compressed into a single MessageGroup summary.
* When enabled and triggered, ALL messages are compressed into a single MessageGroup summary.
*/
compressionConfig?: {
/** Whether context compression is enabled (default: true) */
enabled?: boolean;
/** Model's max context window token count (default: 128k) */
maxWindowToken?: number;
};
modelRuntimeConfig?: {
model: string;
provider: string;
/**
* Compression model configuration
* Used for context compression tasks
@@ -87,6 +86,8 @@ export interface GeneralAgentConfig {
model: string;
provider: string;
};
model: string;
provider: string;
};
operationId: string;
userId?: string;

View File

@@ -320,7 +320,23 @@ const Controls = memo<ControlsProps>(({ setUpdating }) => {
: []),
];
const allItems = [...baseItems, ...maxTokensItems];
// Context Compression items
const contextCompressionItems: FormItemProps[] = [
{
children: <Switch />,
label: (
<Flexbox align={'center'} className={styles.label} gap={8} horizontal>
{t('settingModel.enableContextCompression.title')}
<InfoTooltip title={t('settingModel.enableContextCompression.desc')} />
</Flexbox>
),
name: ['chatConfig', 'enableContextCompression'],
tag: 'compression',
valuePropName: 'checked',
},
];
const allItems = [...baseItems, ...maxTokensItems, ...contextCompressionItems];
return (
<Form

View File

@@ -501,6 +501,9 @@ export default {
'Set the default number of images generated when creating a new task in the image generation panel.',
'settingImage.defaultCount.label': 'Default Image Count',
'settingImage.defaultCount.title': 'AI Art',
'settingModel.enableContextCompression.desc':
'Automatically compress historical messages into summaries when conversation exceeds 64,000 tokens, saving 60-80% token usage',
'settingModel.enableContextCompression.title': 'Enable Auto Context Compression',
'settingModel.enableMaxTokens.title': 'Enable Max Tokens Limit',
'settingModel.enableReasoningEffort.title': 'Enable Reasoning Effort Adjustment',
'settingModel.frequencyPenalty.desc':

View File

@@ -126,8 +126,7 @@ export class AgentRuntimeService {
*/
private stepCallbacks: Map<string, StepLifecycleCallbacks> = new Map();
private get baseURL() {
const baseUrl =
process.env.AGENT_RUNTIME_BASE_URL || appEnv.APP_URL || 'http://localhost:3010';
const baseUrl = process.env.AGENT_RUNTIME_BASE_URL || appEnv.APP_URL || 'http://localhost:3010';
return urlJoin(baseUrl, '/api/agent');
}
@@ -840,6 +839,9 @@ export class AgentRuntimeService {
// Create Durable Agent instance
const agent = new GeneralChatAgent({
agentConfig: metadata?.agentConfig,
compressionConfig: {
enabled: metadata?.agentConfig?.chatConfig?.enableContextCompression ?? true,
},
modelRuntimeConfig: metadata?.modelRuntimeConfig,
operationId,
userId: metadata?.userId,

View File

@@ -702,6 +702,9 @@ export const streamingExecutor: StateCreator<
const agent = new GeneralChatAgent({
agentConfig: { maxSteps: 1000 },
compressionConfig: {
enabled: agentConfigData.chatConfig?.enableContextCompression ?? true, // Default to enabled
},
operationId: `${messageKey}/${params.parentMessageId}`,
modelRuntimeConfig,
});

View File

@@ -833,5 +833,26 @@ describe('displayMessageSelectors', () => {
const result = displayMessageSelectors.findLastMessageId('msg-1')(state as ChatStore);
expect(result).toBe('tool-result-id');
});
it('should return lastMessageId for compressedGroup instead of group id', () => {
const compressedGroupMessage = {
id: 'mg_123456',
role: 'compressedGroup',
content: 'Compressed summary',
lastMessageId: 'msg-999',
compressedMessages: [],
pinnedMessages: [],
} as unknown as UIChatMessage;
const state: Partial<ChatStore> = {
activeAgentId: 'test-id',
messagesMap: {
[messageMapKey({ agentId: 'test-id' })]: [compressedGroupMessage],
},
};
const result = displayMessageSelectors.findLastMessageId('mg_123456')(state as ChatStore);
expect(result).toBe('msg-999');
});
});
});

View File

@@ -247,7 +247,7 @@ const findLastBlockId = (block: AssistantContentBlock | undefined): string | und
/**
* Recursively finds the last message ID in a message tree
* Priority: children > tools > self
* Priority: children > tools > compressedGroup.lastMessageId > self
*/
const findLastMessageIdRecursive = (node: UIChatMessage | undefined): string | undefined => {
if (!node) return undefined;
@@ -264,7 +264,12 @@ const findLastMessageIdRecursive = (node: UIChatMessage | undefined): string | u
return lastTool?.result_msg_id;
}
// Priority 3: Return self ID
// Priority 3: For compressedGroup, return lastMessageId instead of group ID
if (node.role === 'compressedGroup' && 'lastMessageId' in node) {
return (node as any).lastMessageId;
}
// Priority 4: Return self ID
return node.id;
};