Skip to content

Commit 353ee10

Browse files
carolin913tdesign-botuyarn
authored
feat(chat-engine): AGUI activity and bundle optimization (#4119)
* feat(chatengine): update cherrymarkdown and support agui activity event * feat(chatmarkdown): chunk optimize * feat(chatengine): add useagentactivity hooks and demos * feat(chatengine): change endpoint * feat(chatengine): fix type error * chore(sync common): sync common * feat: sync common * feat: change endpoint and fix lint * chore: fix lint * chore: fix lint * chore: version * chore: stash changelog [ci skip] * chore: previous CHANGELOG update * chore: update common --------- Co-authored-by: tdesign-bot <tdesign@tencent.com> Co-authored-by: wū yāng <uyarnchen@gmail.com>
1 parent 9332330 commit 353ee10

32 files changed

+4211
-252
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,11 @@ pnpm-lock.yaml
3939

4040
## analysis files
4141
bundle-analysis
42+
packages/**/chat-engine/core
43+
packages/**/chat-engine/docs
44+
packages/**/chatbot/docs
45+
*.zip
46+
.codebuddy
47+
mock-server
48+
openspec
49+
.fue
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
import React, { useState, useRef, useMemo } from 'react';
2+
import {
3+
ChatList,
4+
ChatSender,
5+
ChatMessage,
6+
isActivityContent,
7+
useChat,
8+
useAgentActivity,
9+
ActivityRenderer,
10+
} from '@tdesign-react/chat';
11+
import { Card, Space, Tag } from 'tdesign-react';
12+
import { CheckCircleFilledIcon, TimeFilledIcon, CloseCircleFilledIcon } from 'tdesign-icons-react';
13+
import type { ActivityComponentProps } from '@tdesign-react/chat';
14+
15+
/**
16+
* Activity 纯增量模式验证示例
17+
*
18+
* 验证没有 ACTIVITY_SNAPSHOT,只有 ACTIVITY_DELTA 的情况下的处理逻辑
19+
* 基于 text.txt 中的真实数据进行测试
20+
*/
21+
22+
// ==================== Activity 组件定义 ====================
23+
24+
/**
25+
* 节点生命周期内容类型
26+
*/
27+
interface NodeLifecycleNode {
28+
nodeId: string;
29+
phase: 'start' | 'complete';
30+
}
31+
32+
interface NodeInterrupt {
33+
nodeId: string;
34+
key: string;
35+
prompt: string;
36+
checkpointId: string;
37+
lineageId: string;
38+
}
39+
40+
interface NodeLifecycleContent {
41+
node?: NodeLifecycleNode;
42+
interrupt?: NodeInterrupt;
43+
}
44+
45+
/**
46+
* 节点生命周期 Activity 组件
47+
* 展示节点的执行状态和中断信息
48+
*/
49+
const NodeLifecycleActivity: React.FC<ActivityComponentProps<NodeLifecycleContent>> = ({ content }) => {
50+
const { node, interrupt } = content;
51+
52+
// 获取节点状态图标
53+
const getNodeIcon = (phase: string) => {
54+
switch (phase) {
55+
case 'complete':
56+
return <CheckCircleFilledIcon style={{ color: '#52c41a', fontSize: 18 }} />;
57+
case 'start':
58+
return <TimeFilledIcon style={{ color: '#1890ff', fontSize: 18 }} />;
59+
default:
60+
return (
61+
<span
62+
style={{
63+
display: 'inline-block',
64+
width: 18,
65+
height: 18,
66+
borderRadius: '50%',
67+
border: '2px solid #d9d9d9',
68+
background: '#fff',
69+
}}
70+
/>
71+
);
72+
}
73+
};
74+
75+
// 获取状态标签主题
76+
const getStatusTheme = (phase: string) => {
77+
if (phase === 'complete') return 'success';
78+
if (phase === 'start') return 'primary';
79+
return 'default';
80+
};
81+
82+
// 获取状态文本
83+
const getStatusText = (phase: string) => {
84+
if (phase === 'complete') return '已完成';
85+
if (phase === 'start') return '执行中';
86+
return '未知';
87+
};
88+
89+
return (
90+
<Card bordered style={{ marginTop: 8, width: '100%' }}>
91+
<Space direction="vertical" style={{ width: '100%' }}>
92+
{/* 标题 */}
93+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
94+
<span style={{ fontSize: 16, fontWeight: 600 }}>节点生命周期</span>
95+
<Tag theme="primary" variant="light">
96+
纯增量模式
97+
</Tag>
98+
</div>
99+
100+
{/* 节点信息 */}
101+
{node && (
102+
<div style={{ padding: '12px', background: '#f8f9fa', borderRadius: '6px' }}>
103+
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 8 }}>
104+
{getNodeIcon(node.phase)}
105+
<span style={{ fontSize: 14, fontWeight: 500 }}>节点: {node.nodeId}</span>
106+
<Tag size="small" theme={getStatusTheme(node.phase)}>
107+
{getStatusText(node.phase)}
108+
</Tag>
109+
</div>
110+
<div style={{ fontSize: 12, color: '#666' }}>
111+
阶段: {node.phase}
112+
</div>
113+
</div>
114+
)}
115+
116+
{/* 中断信息 */}
117+
{interrupt && (
118+
<div style={{ padding: '12px', background: '#fff2e8', borderRadius: '6px', border: '1px solid #ffbb96' }}>
119+
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 8 }}>
120+
<CloseCircleFilledIcon style={{ color: '#fa8c16', fontSize: 18 }} />
121+
<span style={{ fontSize: 14, fontWeight: 500 }}>节点中断: {interrupt.nodeId}</span>
122+
<Tag size="small" theme="warning">
123+
需要确认
124+
</Tag>
125+
</div>
126+
<div style={{ fontSize: 12, color: '#666', marginBottom: 4 }}>
127+
键: {interrupt.key}
128+
</div>
129+
<div style={{ fontSize: 12, color: '#666', marginBottom: 4 }}>
130+
检查点ID: {interrupt.checkpointId}
131+
</div>
132+
<div style={{ fontSize: 12, color: '#666', marginBottom: 4 }}>
133+
提示: {interrupt.prompt}
134+
</div>
135+
</div>
136+
)}
137+
138+
{/* 调试信息 */}
139+
<details style={{ fontSize: 12, color: '#999' }}>
140+
<summary style={{ cursor: 'pointer', userSelect: 'none' }}>调试信息</summary>
141+
<pre style={{ marginTop: 8, padding: 8, background: '#f5f5f5', borderRadius: 4, overflow: 'auto' }}>
142+
{JSON.stringify(content, null, 2)}
143+
</pre>
144+
</details>
145+
</Space>
146+
</Card>
147+
);
148+
};
149+
150+
// ==================== 主组件 ====================
151+
152+
const ActivityDeltaTest: React.FC = () => {
153+
const listRef = useRef<any>(null);
154+
const inputRef = useRef<any>(null);
155+
const [inputValue, setInputValue] = useState<string>('测试纯增量模式');
156+
157+
// 注册 Activity 组件
158+
useAgentActivity([
159+
{
160+
activityType: 'graph.node.lifecycle',
161+
component: NodeLifecycleActivity as React.FC<ActivityComponentProps>,
162+
description: '节点生命周期展示',
163+
},
164+
{
165+
activityType: 'graph.node.interrupt',
166+
component: NodeLifecycleActivity as React.FC<ActivityComponentProps>,
167+
description: '节点中断展示',
168+
},
169+
]);
170+
171+
// 聊天配置 - 使用新的 endpoint
172+
const { chatEngine, messages, status } = useChat({
173+
defaultMessages: [],
174+
chatServiceConfig: {
175+
endpoint: 'https://1257786608-9i9j1kpa67.ap-guangzhou.tencentscf.com/sse/agui-activity-delta-test',
176+
protocol: 'agui',
177+
stream: true,
178+
onRequest: (params) => ({
179+
method: 'POST',
180+
headers: {
181+
'Content-Type': 'application/json',
182+
},
183+
body: JSON.stringify({
184+
prompt: params.prompt,
185+
}),
186+
}),
187+
},
188+
});
189+
190+
const senderLoading = useMemo(() => status === 'pending' || status === 'streaming', [status]);
191+
192+
// 消息配置
193+
const messageProps = {
194+
user: {
195+
variant: 'base' as const,
196+
placement: 'right' as const,
197+
},
198+
assistant: {
199+
placement: 'left' as const,
200+
},
201+
};
202+
203+
// 渲染消息内容
204+
const renderMessageContent = (item: any, index: number) => {
205+
if (isActivityContent(item)) {
206+
return (
207+
<div slot={`${item.type}-${index}`} key={`activity-${index}`}>
208+
<ActivityRenderer activity={item.data} />
209+
</div>
210+
);
211+
}
212+
213+
return null;
214+
};
215+
216+
const renderMsgContents = (message: any) => {
217+
if (Array.isArray(message.content)) {
218+
return <>{message.content.map((item: any, index: number) => renderMessageContent(item, index))}</>;
219+
}
220+
return null;
221+
};
222+
223+
const sendHandler = async (e: any) => {
224+
const { value } = e.detail;
225+
await chatEngine.sendUserMessage({ prompt: value });
226+
setInputValue('');
227+
};
228+
229+
return (
230+
<Space direction='vertical' style={{ width: '100%' }}>
231+
<div style={{ marginBottom: '16px', padding: '12px', background: '#f5f5f5', borderRadius: '4px' }}>
232+
<div style={{ marginBottom: '8px', fontSize: '14px', fontWeight: 500 }}>纯增量模式验证:</div>
233+
<p style={{ margin: '8px 0', fontSize: '14px', color: '#666' }}>
234+
验证没有 ACTIVITY_SNAPSHOT,只有 ACTIVITY_DELTA 的情况下,event-mapper 是否能正确处理
235+
</p>
236+
<p style={{ margin: '8px 0', fontSize: '14px', color: '#666' }}>
237+
基于 text.txt 中的真实数据,包含节点生命周期和中断事件
238+
</p>
239+
</div>
240+
<div style={{ display: 'flex', flexDirection: 'column', height: '500px' }}>
241+
<ChatList ref={listRef}>
242+
{messages.map((message) => (
243+
<ChatMessage key={message.id} {...messageProps[message.role]} message={message}>
244+
{renderMsgContents(message)}
245+
</ChatMessage>
246+
))}
247+
</ChatList>
248+
<ChatSender
249+
ref={inputRef}
250+
value={inputValue}
251+
placeholder="输入任意内容开始测试纯增量模式"
252+
loading={senderLoading}
253+
onChange={(e: any) => setInputValue(e.detail)}
254+
onSend={sendHandler}
255+
onStop={() => chatEngine.abortChat()}
256+
/>
257+
</div>
258+
</Space>
259+
);
260+
};
261+
262+
export default ActivityDeltaTest;

0 commit comments

Comments
 (0)