aiChatDialogue.stories.jsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. import React, { useState, useCallback, useRef, useEffect } from 'react';
  2. import { AIChatDialogue, RadioGroup, Radio, Button, Input, Toast, chatInputToChatCompletion, chatInputToMessage, AIChatInput } from '../../index';
  3. import CustomRenderContentItem from './CustomRenderContentItem';
  4. import RenderConfigContentItem from './RenderConfig';
  5. import AIChatInputWithDialogueDemo from './AIChatInputWithDialogue';
  6. import ResponseToMessageDemo from './DataAdapter/responseToMessage';
  7. import StreamingChatCompletionToMessageDemo from './DataAdapter/streamingChatCompletionToMessage';
  8. import StreamingResponseToMessageDemo from './DataAdapter/streamingResponseToMessage';
  9. import ChatCompletionToMessageDemo from './DataAdapter/chatCompletionToMessage';
  10. import MultiAssistant from './multiAssistant';
  11. import { defaultMessages, loadingMessages, continueSendMessages, multiModalityMessage, reasoningMessage, toolCallMessage, annotationMessage, referenceMessage, failedMessage } from './message';
  12. import MultiAgentDemo from './MultiAgent';
  13. export default {
  14. title: 'AIChatDialogue',
  15. }
  16. const roleConfig = {
  17. user: {
  18. name: 'User',
  19. avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
  20. },
  21. assistant: {
  22. name: 'Assistant',
  23. avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
  24. },
  25. system: {
  26. name: 'System',
  27. avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
  28. }
  29. }
  30. export const AlignAndMode = () => {
  31. const [messages, setMessage] = useState(defaultMessages);
  32. const [mode, setMode] = useState('bubble');
  33. const [align, setAlign] = useState('leftRight');
  34. const onAlignChange = useCallback((e) => {
  35. setAlign(e.target.value);
  36. }, []);
  37. const onModeChange = useCallback((e) => {
  38. setMode(e.target.value);
  39. }, []);
  40. const onChatsChange = useCallback((chats) => {
  41. setMessage(chats);
  42. }, []);
  43. const onReferenceClick = useCallback((item, message) => {
  44. console.log('onReferenceClick', item, message);
  45. }, []);
  46. return (
  47. <>
  48. <span style={{ display: 'flex', flexDirection: 'column', rowGap: '8px'}}>
  49. <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
  50. 模式
  51. <RadioGroup onChange={onModeChange} value={mode} type={"button"}>
  52. <Radio value={'bubble'}>气泡</Radio>
  53. <Radio value={'noBubble'}>非气泡</Radio>
  54. <Radio value={'userBubble'}>用户会话气泡</Radio>
  55. </RadioGroup>
  56. </span>
  57. <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
  58. 会话布局方式
  59. <RadioGroup onChange={onAlignChange} value={align} type={"button"}>
  60. <Radio value={'leftRight'}>左右分布</Radio>
  61. <Radio value={'leftAlign'}>左对齐</Radio>
  62. </RadioGroup>
  63. </span>
  64. </span>
  65. <AIChatDialogue
  66. key={align + mode}
  67. align={align}
  68. mode={mode}
  69. chats={messages}
  70. roleConfig={roleConfig}
  71. onChatsChange={onChatsChange}
  72. onReferenceClick={onReferenceClick}
  73. // onMessageReset={onMessageReset}
  74. />
  75. </>
  76. );
  77. }
  78. export const Loading = () => {
  79. return (
  80. <AIChatDialogue
  81. align="leftRight"
  82. mode="bubble"
  83. chats={loadingMessages}
  84. roleConfig={roleConfig}
  85. />
  86. )
  87. }
  88. export const Scroll = () => {
  89. return (
  90. <div style={{ height: '300px', border: '1px solid lightgray', padding: '10px', borderRadius: '10px' }}>
  91. <AIChatDialogue
  92. align="leftRight"
  93. mode="bubble"
  94. chats={defaultMessages}
  95. roleConfig={roleConfig}
  96. />
  97. </div>
  98. )
  99. }
  100. export const continueSend = () => {
  101. const [selecting, setSelecting] = useState(false);
  102. const onSelect = useCallback((selectionId) => {
  103. console.log('onSelect', selectionId);
  104. }, []);
  105. return (
  106. <AIChatDialogue
  107. align="leftRight"
  108. mode="bubble"
  109. chats={continueSendMessages}
  110. roleConfig={roleConfig}
  111. selecting={selecting}
  112. onSelect={onSelect}
  113. onMessageShare={() => {
  114. setSelecting(!selecting);
  115. }}
  116. />
  117. )
  118. }
  119. export const Action = () => {
  120. const [messages, setMessage] = useState(defaultMessages);
  121. const [selected, setSelected] = useState(false);
  122. const [editMessageId, setEditMessageId] = useState(null);
  123. const onChatsChange = useCallback((chats) => {
  124. setMessage(chats);
  125. }, []);
  126. const onSelect = useCallback((selectionId) => {
  127. console.log('onSelect', selectionId);
  128. }, []);
  129. const onCancel = useCallback(() => {
  130. const index = messages.findIndex(item => item.id === editMessageId);
  131. const newChat = {
  132. ...messages[index],
  133. isEditing: false,
  134. };
  135. const newChats = [...messages];
  136. newChats.splice(index, 1, newChat);
  137. setMessage(newChats);
  138. }, [editMessageId, messages]);
  139. // todo: 确认消息编辑交互形态
  140. const messageEditRender = (message) => {
  141. let value = message.content;
  142. const onChange = (curValue) => {
  143. value = curValue;
  144. }
  145. const onSubmit = () => {
  146. const index = messages.findIndex(item => item.id === editMessageId);
  147. const newChat = {
  148. ...messages[index],
  149. isEditing: false,
  150. content: value,
  151. };
  152. const newChats = [...messages];
  153. newChats.splice(index, 1, newChat);
  154. setMessage(newChats);
  155. };
  156. return (
  157. <div style={{ width: '100%', marginTop: '5px' }}>
  158. <Input defaultValue={value} onChange={onChange} />
  159. <div>
  160. <Button style={{ marginTop: '10px' }} type='primary' onClick={onSubmit}>保存</Button>
  161. <Button style={{ marginTop: '10px', marginLeft: '10px' }} type='primary' onClick={onCancel}>取消</Button>
  162. </div>
  163. </div>
  164. );
  165. }
  166. const onMessageEdit = useCallback((message) => {
  167. setEditMessageId(message.id);
  168. console.log('onMessageEdit', message);
  169. }, []);
  170. const onMessageReset = useCallback((e) => {
  171. setTimeout(() => {
  172. setMessage((message) => {
  173. const lastMessage = message[message.length - 1];
  174. const newLastMessage = {
  175. ...lastMessage,
  176. status: 'completed',
  177. content: 'This is a mock reset message.',
  178. }
  179. return [...message.slice(0, -1), newLastMessage]
  180. })
  181. }, 2000);
  182. })
  183. return (
  184. <AIChatDialogue
  185. align="leftRight"
  186. mode="bubble"
  187. chats={messages}
  188. roleConfig={roleConfig}
  189. selecting={selected}
  190. onMessageCopy={(message) => {
  191. console.log('onMessageCopy', message);
  192. }}
  193. onMessageDelete={() => {
  194. console.log('onMessageDelete');
  195. }}
  196. onMessageReset={onMessageReset}
  197. onMessageGoodFeedback={(message) => {
  198. console.log('onMessageGoodFeedback', message.id);
  199. }}
  200. onMessageBadFeedback={() => {
  201. console.log('onMessageBadFeedback');
  202. }}
  203. onChatsChange={onChatsChange}
  204. onMessageShare={() => {
  205. console.log('onMessageShare', selected);
  206. setSelected(!selected);
  207. }}
  208. onMessageEdit={onMessageEdit}
  209. onSelect={onSelect}
  210. messageEditRender={messageEditRender}
  211. />
  212. )
  213. }
  214. export const Selecting = () => {
  215. const hintsExample = [
  216. "Semi 组件库有哪些常用组件?",
  217. "能否展示一个使用 Semi 组件库构建的页面示例?",
  218. "Semi 组件库有官方文档吗?",
  219. ]
  220. const ref = useRef(null);
  221. const [align, setAlign] = useState('leftRight');
  222. const [select, setSelect] = useState(true);
  223. const [selection, setSelection] = useState('allSelect');
  224. const [hints, setHints] = useState(hintsExample);
  225. useEffect(() => {
  226. ref.current.selectAll();
  227. }, []);
  228. const onAlignChange = useCallback((e) => {
  229. setAlign(e.target.value);
  230. }, []);
  231. const onSelectChange = useCallback((e) => {
  232. setSelect(e.target.value);
  233. }, []);
  234. const onSelectionChange = useCallback((e) => {
  235. if(e.target.value === 'allSelect') {
  236. ref.current.selectAll();
  237. } else {
  238. ref.current.deselectAll();
  239. }
  240. setSelection(e.target.value);
  241. }, []);
  242. const onSelect = useCallback((selectionId) => {
  243. console.log('onSelect', selectionId);
  244. }, []);
  245. const onHintClick = useCallback((hint) => {
  246. setHints([]);
  247. }, []);
  248. return (
  249. <div>
  250. <span style={{ display: 'flex', flexDirection: 'column', rowGap: '8px'}}>
  251. <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
  252. 会话布局方式
  253. <RadioGroup onChange={onAlignChange} value={align} type={"button"}>
  254. <Radio value={'leftRight'}>左右分布</Radio>
  255. <Radio value={'leftAlign'}>左对齐</Radio>
  256. </RadioGroup>
  257. </span>
  258. <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
  259. 是否开启选择
  260. <RadioGroup onChange={onSelectChange} value={select} type={"button"}>
  261. <Radio value={true}>开启</Radio>
  262. <Radio value={false}>关闭</Radio>
  263. </RadioGroup>
  264. </span>
  265. <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
  266. 选择方式
  267. <RadioGroup onChange={onSelectionChange} value={selection} type={"button"}>
  268. <Radio value={'allSelect'}>全选</Radio>
  269. <Radio value={'cancelSelect'}>取消全选</Radio>
  270. </RadioGroup>
  271. </span>
  272. </span>
  273. <AIChatDialogue
  274. ref={ref}
  275. align={align}
  276. mode="bubble"
  277. chats={defaultMessages}
  278. selecting={select}
  279. onSelect={onSelect}
  280. roleConfig={roleConfig}
  281. hints={hints}
  282. onHintClick={onHintClick}
  283. />
  284. </div>
  285. )
  286. }
  287. export const Editing = () => {
  288. const [messages, setMessage] = useState(defaultMessages);
  289. const [editMessageId, setEditMessageId] = useState(null);
  290. const onChatsChange = useCallback((chats) => {
  291. setMessage(chats);
  292. }, []);
  293. const onMessageEdit = useCallback((message) => {
  294. setEditMessageId(message.id);
  295. console.log('onMessageEdit', message);
  296. }, []);
  297. const onCancel = useCallback(() => {
  298. const index = messages.findIndex(item => item.id === editMessageId);
  299. const newChat = {
  300. ...messages[index],
  301. isEditing: false,
  302. };
  303. const newChats = [...messages];
  304. newChats.splice(index, 1, newChat);
  305. setMessage(newChats);
  306. }, [editMessageId, messages]);
  307. // todo: 确认消息编辑交互形态
  308. const messageEditRender = (message) => {
  309. let value = message.content;
  310. const onChange = (curValue) => {
  311. value = curValue;
  312. }
  313. const onSubmit = () => {
  314. const index = messages.findIndex(item => item.id === editMessageId);
  315. const newChat = {
  316. ...messages[index],
  317. isEditing: false,
  318. content: value,
  319. };
  320. const newChats = [...messages];
  321. newChats.splice(index, 1, newChat);
  322. setMessage(newChats);
  323. };
  324. return (
  325. <div style={{ width: '100%', marginTop: '5px' }}>
  326. <Input defaultValue={value} onChange={onChange} />
  327. <div style={{ display: 'flex', flexDirection: 'row-reverse', columnGap: '10px' }}>
  328. <Button style={{ marginTop: '10px' }} type='primary' onClick={onSubmit}>发送</Button>
  329. <Button style={{ marginTop: '10px', marginLeft: '10px' }} type='primary' onClick={onCancel}>取消</Button>
  330. </div>
  331. </div>
  332. // <Input defaultValue={value} onChange={onChange} />
  333. );
  334. }
  335. return (
  336. <AIChatDialogue
  337. align="leftRight"
  338. mode="bubble"
  339. chats={messages}
  340. roleConfig={roleConfig}
  341. onChatsChange={onChatsChange}
  342. onMessageEdit={onMessageEdit}
  343. messageEditRender={messageEditRender}
  344. />
  345. )
  346. }
  347. export const MultiModality = () => {
  348. const [messages, setMessage] = useState(multiModalityMessage);
  349. const onChatsChange = useCallback((chats) => {
  350. setMessage(chats);
  351. }, []);
  352. const onFileClick = useCallback((file) => {
  353. console.log('onFileClick', file);
  354. }, []);
  355. return (
  356. <AIChatDialogue
  357. align="leftRight"
  358. mode="bubble"
  359. chats={messages}
  360. roleConfig={roleConfig}
  361. onChatsChange={onChatsChange}
  362. onFileClick={onFileClick}
  363. disabledFileClick={true}
  364. />
  365. )
  366. }
  367. export const Reasoning = () => {
  368. const [messages, setMessage] = useState(reasoningMessage);
  369. const onChatsChange = useCallback((chats) => {
  370. setMessage(chats);
  371. }, []);
  372. return (
  373. <AIChatDialogue
  374. align="leftRight"
  375. mode="userBubble"
  376. chats={messages}
  377. roleConfig={roleConfig}
  378. onChatsChange={onChatsChange}
  379. />
  380. )
  381. }
  382. export const ToolCall = () => {
  383. const [messages, setMessage] = useState(toolCallMessage);
  384. const onChatsChange = useCallback((chats) => {
  385. setMessage(chats);
  386. }, []);
  387. return (
  388. <AIChatDialogue
  389. align="leftRight"
  390. mode="bubble"
  391. chats={messages}
  392. roleConfig={roleConfig}
  393. onChatsChange={onChatsChange}
  394. />
  395. )
  396. }
  397. export const Annotation = () => {
  398. const [messages, setMessage] = useState(annotationMessage);
  399. const onChatsChange = useCallback((chats) => {
  400. setMessage(chats);
  401. }, []);
  402. const onAnnotationClick = useCallback((annotation) => {
  403. console.log('onAnnotationClick', annotation);
  404. Toast.success('Ready to open the sidebar!');
  405. }, []);
  406. return (
  407. <AIChatDialogue
  408. align="leftRight"
  409. mode="bubble"
  410. chats={messages}
  411. roleConfig={roleConfig}
  412. onChatsChange={onChatsChange}
  413. onAnnotationClick={onAnnotationClick}
  414. />
  415. )
  416. }
  417. export const ShowReference = () => {
  418. const onReferenceClick = useCallback((item) => {
  419. console.log('onReferenceClick', item);
  420. }, []);
  421. return (
  422. <AIChatDialogue
  423. align="leftRight"
  424. mode="bubble"
  425. // mode="noBubble"
  426. chats={multiModalityMessage}
  427. roleConfig={roleConfig}
  428. showReference={true}
  429. onReferenceClick={onReferenceClick}
  430. />
  431. )
  432. }
  433. export const Reference = () => {
  434. const [messages, setMessage] = useState(referenceMessage);
  435. const onChatsChange = useCallback((chats) => {
  436. setMessage(chats);
  437. }, []);
  438. return (
  439. <AIChatDialogue
  440. align="leftRight"
  441. mode="bubble"
  442. chats={messages}
  443. roleConfig={roleConfig}
  444. onChatsChange={onChatsChange}
  445. />
  446. )
  447. }
  448. export const CustomRender = CustomRenderContentItem;
  449. export const RenderConfig = RenderConfigContentItem;
  450. export const Failed = () => {
  451. const [messages, setMessage] = useState(failedMessage);
  452. const onChatsChange = useCallback((chats) => {
  453. setMessage(chats);
  454. }, []);
  455. return (
  456. <AIChatDialogue
  457. align="leftRight"
  458. mode="bubble"
  459. chats={messages}
  460. roleConfig={roleConfig}
  461. onChatsChange={onChatsChange}
  462. />
  463. )
  464. }
  465. export const Hints = () => {
  466. const hintsExample = [
  467. "Semi 组件库有哪些常用组件?",
  468. "能否展示一个使用 Semi 组件库构建的页面示例?",
  469. "Semi 组件库有官方文档吗?",
  470. ]
  471. const [messages, setMessage] = useState(defaultMessages);
  472. const [hints, setHints] = useState(hintsExample);
  473. const onChatsChange = useCallback((chats) => {
  474. console.log('onChatsChange', chats);
  475. setMessage(chats);
  476. }, []);
  477. const onHintClick = useCallback((hint) => {
  478. setHints([]);
  479. }, []);
  480. return (
  481. <AIChatDialogue
  482. align="leftRight"
  483. mode="bubble"
  484. chats={messages}
  485. roleConfig={roleConfig}
  486. onChatsChange={onChatsChange}
  487. hints={hints}
  488. onHintClick={onHintClick}
  489. />
  490. )
  491. }
  492. export const MultiAssistantStory = () => <MultiAssistant />;
  493. MultiAssistantStory.storyName = 'Multi Assistant';
  494. export const MultiAgentStory = () => <MultiAgentDemo />;
  495. MultiAgentStory.storyName = 'Multi Agent';
  496. export const AIChatInputWithDialogue = () => <AIChatInputWithDialogueDemo />;
  497. AIChatInputWithDialogue.storyName = 'AIChatInput With Dialogue';
  498. export const ResponseToMessageStory = () => <ResponseToMessageDemo />;
  499. ResponseToMessageStory.storyName = 'adapter: response';
  500. export const ChatCompletionToMessageStory = () => <ChatCompletionToMessageDemo />;
  501. ChatCompletionToMessageStory.storyName = 'adapter: chatCompletion';
  502. export const StreamingResponseToMessageStory = () => <StreamingResponseToMessageDemo />;
  503. StreamingResponseToMessageStory.storyName = 'adapter: streaming response';
  504. export const StreamingChatCompletionToMessageStory = () => <StreamingChatCompletionToMessageDemo />;
  505. StreamingChatCompletionToMessageStory.storyName = 'adapter: streaming chatCompletion';
  506. export const ChatInputToChatCompletionStory = () => {
  507. const [generating, setGenerating] = useState(false);
  508. const [messages, setMessages] = useState([]);
  509. const onContentChange = useCallback((content) => {
  510. // console.log('onContentChange', content);
  511. }, []);
  512. const onMessageSend = useCallback((props) => {
  513. // console.log('onMessageSend', props);
  514. const userMessage = chatInputToMessage(props);
  515. const chatCompletion = chatInputToChatCompletion(props);
  516. setGenerating(true);
  517. setMessages((messages) => [...messages, {
  518. id: `message-${Date.now()}`,
  519. ...userMessage
  520. }]);
  521. console.log('chatCompletion', chatCompletion);
  522. }, []);
  523. return (
  524. <AIChatInput
  525. placeholder={'发送后查看控制台输出的结果'}
  526. defaultContent={'帮我写一个关于<input-slot placeholder="[主题]">独角兽</input-slot>的故事'}
  527. skills={[]}
  528. generating={generating}
  529. uploadProps={{ action: "https://api.semi.design/upload" }}
  530. onContentChange={onContentChange}
  531. onMessageSend={onMessageSend}
  532. onStopGenerate={() => setGenerating(false)}
  533. />
  534. )
  535. };