【ant design X】

1、sender组件

2、flex弹性布局

3、useEffect钩子

State Hook 

状态帮助组件 “记住”用户输入的信息。例如,一个表单组件可以使用状态存储输入值,而一个图像库组件可以使用状态存储所选的图像索引。

使用以下 Hook 以向组件添加状态:

useEffect 是一个 React Hook,它允许你 将组件与外部系统同步

useEffect(setup, dependencies?) 

在组件的顶层调用 useEffect 来声明一个 Effect:

 
useEffect(setup, dependencies?) 
在组件的顶层调用 useEffect 来声明一个 Effect:

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);
  // ...
}

4、从 Less 变量到 Design Token

5、bubble 构件

6、Conversion是管理对话记录

7、useXChat主要用于管理会话

通过 Agent 进行会话数据管理,并产出供页面渲染使用的数据。

管理对话

import { UserOutlined } from '@ant-design/icons';
import { Bubble, Sender, useXAgent, useXChat } from '@ant-design/x';
import { Flex, type GetProp } from 'antd';
import React from 'react';

const sleep = () => new Promise((resolve) => setTimeout(resolve, 1000));

const roles: GetProp<typeof Bubble.List, 'roles'> = {
  ai: {
    placement: 'start',
    avatar: { icon: <UserOutlined />, style: { background: '#fde3cf' } },
    typing: { step: 5, interval: 20 },
    style: {
      maxWidth: 600,
    },
  },
  local: {
    placement: 'end',
    avatar: { icon: <UserOutlined />, style: { background: '#87d068' } },
  },
};

let mockSuccess = false;

const App = () => {
  const [content, setContent] = React.useState('');

  // Agent for request
  const [agent] = useXAgent<string, { message: string }, string>({
    request: async ({ message }, { onSuccess, onError }) => {
      await sleep();
      mockSuccess = !mockSuccess;
      if (mockSuccess) {
        onSuccess([`Mock success return. You said: ${message}`]);
      }

      onError(new Error('Mock request failed'));
    },
  });

  // Chat messages
  const { onRequest, messages } = useXChat({
    agent,
    requestPlaceholder: 'Waiting...',
    requestFallback: 'Mock failed return. Please try again later.',
  });

  return (
    <Flex vertical gap="middle">
      <Bubble.List
        roles={roles}
        style={{ maxHeight: 300 }}
        items={messages.map(({ id, message, status }) => ({
          key: id,
          loading: status === 'loading',
          role: status === 'local' ? 'local' : 'ai',
          content: message,
        }))}
      />
      <Sender
        loading={agent.isRequesting()}
        value={content}
        onChange={setContent}
        onSubmit={(nextContent) => {
          onRequest(nextContent);
          setContent('');
        }}
      />
    </Flex>
  );
};

export default App;

7、useXAgent用于模型调度的 Agent 钩子。

import { LoadingOutlined, TagsOutlined } from '@ant-design/icons';
import { ThoughtChain, useXAgent } from '@ant-design/x';
import { Button, Descriptions, Splitter } from 'antd';
import React from 'react';

import type { ThoughtChainItem } from '@ant-design/x';

/**
 * 🔔 Please replace the BASE_URL, PATH, MODEL, API_KEY with your own values.
 */
const BASE_URL = 'https://api.example.com';
const PATH = '/chat';
const MODEL = 'gpt-3.5-turbo';
/** 🔥🔥 Its dangerously! */
// const API_KEY = '';

interface YourMessageType {
  role: string;
  content: string;
}

const App = () => {
  const [status, setStatus] = React.useState<ThoughtChainItem['status']>();
  const [lines, setLines] = React.useState<any[]>([]);

  const [agent] = useXAgent<YourMessageType>({
    baseURL: BASE_URL + PATH,
    model: MODEL,
    // dangerouslyApiKey: API_KEY
  });

  async function request() {
    setStatus('pending');

    agent.request(
      {
        messages: [{ role: 'user', content: 'hello, who are u?' }],
        stream: true,
      },
      {
        onSuccess: (chunks) => {
          setStatus('success');
          console.log('onSuccess', chunks);
        },
        onError: (error) => {
          setStatus('error');
          console.error('onError', error);
        },
        onUpdate: (chunk) => {
          setLines((pre) => [...pre, chunk]);
          console.log('onUpdate', chunk);
        },
      },
    );
  }

  return (
    <Splitter>
      <Splitter.Panel>
        <Button type="primary" disabled={status === 'pending'} onClick={request}>
          Agent Request
        </Button>
      </Splitter.Panel>
      <Splitter.Panel>
        <ThoughtChain
          style={{ marginLeft: 16 }}
          items={[
            {
              title: 'Agent Request Log',
              status: status,
              icon: status === 'pending' ? <LoadingOutlined /> : <TagsOutlined />,
              description:
                status === 'error' &&
                agent.config.baseURL === BASE_URL + PATH &&
                'Please replace the BASE_URL, PATH, MODEL, API_KEY with your own values.',
              content: (
                <Descriptions column={1}>
                  <Descriptions.Item label="Status">{status || '-'}</Descriptions.Item>
                  <Descriptions.Item label="Update Times">{lines.length}</Descriptions.Item>
                </Descriptions>
              ),
            },
          ]}
        />
      </Splitter.Panel>
    </Splitter>
  );
};

export default App;

8、XStream 流 转换可读数据流

XStream 默认的 transformStream 是用于 SSE 协议的流转换器。readableStream 接收一个 new ReadableStream(...) 实例,常见的如 await fetch(...).body

import { TagsOutlined } from '@ant-design/icons';
import { Bubble, ThoughtChain, XStream } from '@ant-design/x';
import { Button, Splitter } from 'antd';
import React from 'react';

const contentChunks = ['He', 'llo', ', w', 'or', 'ld!'];

function mockReadableStream() {
  const sseChunks: string[] = [];

  for (let i = 0; i < contentChunks.length; i++) {
    const sseEventPart = `event: message\ndata: {"id":"${i}","content":"${contentChunks[i]}"}\n\n`;
    sseChunks.push(sseEventPart);
  }

  return new ReadableStream({
    async start(controller) {
      for (const chunk of sseChunks) {
        await new Promise((resolve) => setTimeout(resolve, 100));
        controller.enqueue(new TextEncoder().encode(chunk));
      }
      controller.close();
    },
  });
}

const App = () => {
  const [lines, setLines] = React.useState<Record<string, string>[]>([]);
  const content = lines.map((line) => JSON.parse(line.data).content).join('');

  async function readStream() {
    // 🌟 Read the stream
    for await (const chunk of XStream({
      readableStream: mockReadableStream(),
    })) {
      console.log(chunk);
      setLines((pre) => [...pre, chunk]);
    }
  }

  return (
    <Splitter>
      <Splitter.Panel>
        {/* -------------- Emit -------------- */}
        <Button type="primary" onClick={readStream} style={{ marginBottom: 16 }}>
          Mock Default Protocol - SSE
        </Button>
        {/* -------------- Content Concat -------------- */}
        {content && <Bubble content={content} />}
      </Splitter.Panel>
      {/* -------------- Log -------------- */}
      <Splitter.Panel style={{ marginLeft: 16 }}>
        <ThoughtChain
          items={
            lines.length
              ? [
                  {
                    title: 'Mock Default Protocol - Log',
                    status: 'success',
                    icon: <TagsOutlined />,
                    content: (
                      <pre style={{ overflow: 'scroll' }}>
                        {lines.map((i) => (
                          <code key={i.data}>{i.data}</code>
                        ))}
                      </pre>
                    ),
                  },
                ]
              : []
          }
        />
      </Splitter.Panel>
    </Splitter>
  );
};

export default App;

8、箭头函数学习了

lines.map(...)
  • map 方法:遍历数组 lines,对每个元素执行回调函数,并返回新数组。
  • 回调函数(line) => JSON.parse(line.data).content
    • line.data:假设 lines 中的每个元素是对象,且包含 data 属性(通常为 JSON 字符串)。
    • JSON.parse(line.data):将 JSON 字符串解析为 JavaScript 对象。
    • .content:从解析后的对象中提取 content 属性的值

9、学习了chat控件,用来管理对话管理的内容

10、question

以下是用户提问后,大模型回答并最终显示文字的完整流程梳理:


1. 用户输入问题

  • 用户在界面上通过 Sender 组件输入内容。
  • 输入的内容被存储在状态变量 inputValue 中。
 

tsx

<Sender value={inputValue} ... onChange={setInputValue} />


2. 触发提交事件

  • 当用户点击发送按钮或按下回车键时,调用 onSubmit 函数。
  • 此函数检查是否有正在进行的请求(避免重复提交),然后调用 onRequest 发起请求。
 

tsx

onSubmit(() => { onSubmit(inputValue); // 调用处理函数 setInputValue(''); // 清空输入框 })


3. 发起请求

  • onRequest 是由 useXChat 提供的 API,用于向大模型发起请求。
  • 请求参数包含:
    • stream: true:表示使用流式响应。
    • message: 包含用户输入内容和角色(通常是 "user")。
 

tsx

onRequest({ stream: true, message: { role: 'user', content: val }, });


4. 大模型返回数据

  • 大模型开始返回数据,通常以 chunk 形式进行流式传输。
  • transformMessage 回调函数处理每个 chunk 数据,提取推理中 (reasoning_content) 和实际输出 (content) 的内容,并拼接成完整的回复。
 

tsx

transformMessage: (info) => { const currentThink = message?.choices?.[0]?.delta?.reasoning_content; const currentContent = message?.choices?.[0]?.delta?.content; ... return { content, role: 'assistant' }; }


5. 更新消息列表

  • 返回的数据经过处理后,会更新到 messages 状态中。
  • messages 是一个数组,包含了历史对话记录,结构为:
 

ts

{ status: 'loading' | 'done', message: { role: 'user' | 'assistant', content: string } }


6. 渲染聊天记录

  • Bubble.List 组件负责将 messages 数组中的每条消息渲染出来。
  • 每条消息根据角色(role)决定显示在左侧(AI)还是右侧(用户)。
 

tsx

<Bubble.List items={messages.map(i => ({ ...i.message, typing: i.status === 'loading' ? { step: 5, interval: 20 } : false, }))} />


7. 加载状态与动态更新

  • 如果当前仍在等待大模型返回数据,则 status === 'loading',此时显示打字动画。
  • 当数据完全接收完毕,状态变为 'done',停止动画并展示完整回复。

✅ 总结流程图

阶段描述
1️⃣ 用户输入在 Sender 中输入问题,值存入 inputValue
2️⃣ 提交点击发送按钮触发 onSubmit,调用 onRequest
3️⃣ 请求向大模型 API 发送请求,启用 stream 模式
4️⃣ 流式返回大模型逐步返回数据,通过 transformMessage 解析
5️⃣ 更新状态将解析后的回复更新至 messages 状态
6️⃣ 渲染使用 Bubble.List 动态渲染聊天记录
7️⃣ 加载控制根据 status 显示加载动画或完整内容

12 

完整逻辑
  • 当 info 存在时:提取 info.originMessage 和 info.chunk
  • 当 info 不存在时:使用空对象 {} 作为默认值,此时 originMessage 和 chunk 都为 undefined

13、useXAgent使用XRrequest请求

XAgent的钩子函数

XRequest的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值