suggestions.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <script setup lang="tsx">
  2. import { SmileOutlined, UserOutlined } from '@ant-design/icons-vue';
  3. import { Flex } from 'ant-design-vue';
  4. import { Bubble, Prompts, Sender, useXAgent, useXChat, type BubbleListProps } from 'ant-design-x-vue';
  5. import { ref } from 'vue';
  6. defineOptions({ name: 'AXUseXChatSuggestions' });
  7. const sleep = () => new Promise((resolve) => setTimeout(resolve, 1000));
  8. const roles: BubbleListProps['roles'] = {
  9. user: {
  10. placement: 'end',
  11. avatar: { icon: <UserOutlined />, style: { background: '#87d068' } },
  12. },
  13. text: {
  14. placement: 'start',
  15. avatar: { icon: <UserOutlined />, style: { background: '#fde3cf' } },
  16. typing: true,
  17. },
  18. suggestion: {
  19. placement: 'start',
  20. avatar: { icon: <UserOutlined />, style: { visibility: 'hidden' } },
  21. variant: 'borderless',
  22. messageRender: (content) => (
  23. <Prompts
  24. vertical
  25. items={(content as any as string[]).map((text) => ({
  26. key: text,
  27. icon: <SmileOutlined style={{ color: '#FAAD14' }} />,
  28. description: text,
  29. }))}
  30. />
  31. ),
  32. },
  33. };
  34. type AgentUserMessage = {
  35. type: 'user';
  36. content: string;
  37. };
  38. type AgentAIMessage = {
  39. type: 'ai';
  40. content?: string;
  41. list?: (
  42. | {
  43. type: 'text';
  44. content: string;
  45. }
  46. | {
  47. type: 'suggestion';
  48. content: string[];
  49. }
  50. )[];
  51. };
  52. type AgentMessage = AgentUserMessage | AgentAIMessage;
  53. type BubbleMessage = {
  54. role: string;
  55. };
  56. const content = ref('');
  57. const senderLoading = ref(false);
  58. // Agent for request
  59. const [ agent ] = useXAgent<AgentMessage>({
  60. request: async ({ message }, { onSuccess }) => {
  61. senderLoading.value = true;
  62. await sleep();
  63. const { content } = message || {};
  64. senderLoading.value = false;
  65. onSuccess({
  66. type: 'ai',
  67. list: [
  68. {
  69. type: 'text',
  70. content: `Do you want?`,
  71. },
  72. {
  73. type: 'suggestion',
  74. content: [`Look at: ${content}`, `Search: ${content}`, `Try: ${content}`],
  75. },
  76. ],
  77. });
  78. },
  79. });
  80. // Chat messages
  81. const { onRequest, parsedMessages } = useXChat<AgentMessage, BubbleMessage>({
  82. agent: agent.value,
  83. defaultMessages: [
  84. {
  85. id: 'init',
  86. message: {
  87. type: 'ai',
  88. content: 'Hello, what can I do for you?',
  89. },
  90. status: 'success',
  91. },
  92. ],
  93. requestPlaceholder: {
  94. type: 'ai',
  95. content: 'Waiting...',
  96. },
  97. // Convert AgentMessage to BubbleMessage
  98. parser: (agentMessages) => {
  99. const list = agentMessages.content ? [agentMessages] : (agentMessages as AgentAIMessage).list;
  100. return (list || []).map((msg) => ({
  101. role: msg.type,
  102. content: msg.content,
  103. }));
  104. },
  105. });
  106. defineRender(() => {
  107. return (
  108. <Flex vertical gap="middle">
  109. <Bubble.List
  110. roles={roles}
  111. style={{ maxHeight: 300 }}
  112. items={parsedMessages.value.map(({ id, message, status }) => ({
  113. key: id,
  114. loading: status === 'loading',
  115. ...message,
  116. }))}
  117. />
  118. <Sender
  119. loading={senderLoading.value}
  120. value={content.value}
  121. onChange={(v) => content.value = v}
  122. onSubmit={(nextContent) => {
  123. onRequest({
  124. type: 'user',
  125. content: nextContent,
  126. });
  127. content.value = '';
  128. }}
  129. />
  130. </Flex>
  131. )
  132. });
  133. </script>