react实现模拟chatGPT问答页

ops/2024/11/27 12:05:11/

目录

  • 需求
  • 前端方法
    • 用原始的方案
    • 用SSE实现方案
      • GET请求
      • POST请求

需求

需要使用react实现模拟chatGPT的页面,后端接口使用流式传输 stream: true,并且用 POST 方法进行传参

前端方法

用原始的方案

大概思路:使用 fetch 接受数据,然后读取数据流,解析数据,获取到需要的数据结构,然后再封装展示的方法,html 用原生js获取id实现页面的展示,展示的过程中自定义定时器,实现打字机的效果
以下代码:

if (input.trim()) {const streamUrl = '/opentrek-chat/completions';fetch(streamUrl, {method: 'POST',headers: {'Content-Type': '',//根据需求写'Authorization': '', //根据需求写},body: JSON.stringify({"messages": [{"role": "user","content": "全新CT6照明功能介绍"},],"stream": true,}),}).then(response => {const decoder = new TextDecoder('utf-8');const reader = response.body.getReader();let buffer = ''; // 用于累计流数据let allMessagesArr = []; // 用于存储所有的消息内容// 使用 async/await 来简化递归流读取async function readStream() {try {const { done, value } = await reader.read();if (done) {console.log('流读取完成');// 解析并处理所有流数据handleMessages();return;}// 解码流数据并更新 bufferconst chunk = decoder.decode(value, { stream: true });buffer += chunk;readStream(); // 继续读取流数据} catch (error) {console.error("读取流数据出错:", error);}}// 处理流数据并提取消息function handleMessages() {// 以行为单位拆分消息const messages = buffer.split('\n').filter(item => item.trim() !== '' && item.startsWith('data: ')).map(item => item.slice(5).replace(/\n$/, '').trim()) // 去掉 'data: ' 和末尾的换行.filter(item => item !== '[DONE]') // 忽略 '[DONE]' 标记.map(item => {try {return JSON.parse(item)?.message?.content; // 解析 JSON 数据并提取 content} catch (e) {console.error("消息解析失败:", e);return null;}}).filter(Boolean); // 去除无效数据// 将所有有效消息添加到 allMessagesArr 中allMessagesArr = allMessagesArr.concat(messages);displayMessages(allMessagesArr); // 调用函数展示消息}// 展示逐条输出消息async function displayMessages(messages) {let index = 0;const typingElement = document.getElementById('typing-element'); // 假设这是你要展示消息的 DOM 元素async function type() {if (index < messages.length) {typingElement.textContent += messages[index];  // 展示当前消息index++;await new Promise(resolve => setTimeout(resolve, 50)); // 延时 50mstype();  // 递归调用实现逐条输出}}// 调用逐条输出消息的函数await type();// 更新 React 状态,展示所有有效消息setMessages(prevMessages => [...prevMessages, ...messages]);}// 开始读取流数据readStream();}).catch(error => {console.error('请求出错:', error);});
}

用SSE实现方案

如果后端是get请求的话,可以直接用SSE的方案,一下是一个使用vue实现的demo
react实现的方法大差不差

GET请求

<template ><div><h1>Streamed Data</h1><div v-for="(message, index) in messages" :key="index">{{ message }}</div></div >
</template ><script>
export default {data() {return {messages: [], // 保存从服务端接收到的流式数据};},methods: {connectToStream() {// 创建 EventSource 对象,连接后端流式接口const eventSource = new EventSource(`http://192.168.254.200:8081/ask/stream`);// 监听 'message' 事件(默认事件)eventSource.onmessage = (event) => {try {const data = event.data; // 解析 JSON 数据this.messages.push(data); // 将数据添加到消息列表} catch (error) {console.error("Error parsing JSON:", error);}};// 错误处理eventSource.onerror = (error) => {console.error("EventSource failed:", error);eventSource.close(); // 关闭连接};},},mounted() {this.connectToStream(); // 在组件挂载时启动 SSE 连接},
};
</script><style>
/* 可选样式 */
h1 {color: #333;
}
div {font-family: Arial, sans-serif;margin: 10px 0;
}
</style>

POST请求

EventSource(SSE):是一种持久的连接,用于从服务器向客户端推送实时更新,通常是 GET 请求,它会持续保持连接,不会像普通的请求那样一次性响应数据。
所以我们需要通过转化,来实现POST获取SSE的数据流
需要下载插件:@microsoft/fetch-event-source

npm install @microsoft/fetch-event-source
或
yarn add @microsoft/fetch-event-source
//在代码当中进行引入
import { fetchEventSource } from '@microsoft/fetch-event-source';//这个方法写到触发的事件当中
async function startSseWithPost() {try {// 启动服务器事件源请求await fetchEventSource('/opentrek-chat/v2/completions', {method: 'POST',headers: {'Content-Type': '',//看项目需求'Authorization': '', //看项目需求},body: JSON.stringify({"messages": [{"role": "user","content": "全新CT6照明功能介绍"},],"stream": true,}),onopen(response) {// 连接建立成功时的回调console.log('连接已建立:', response);},onmessage(event) {// 处理收到的消息if (event.data) {try {const messageData = JSON.parse(event.data);if (messageData?.message?.content) {// 显示接收到的消息内容,封装的方法displayMessage(messageData.message.content);} else {console.error('接收到的数据没有有效的消息内容');}} catch (error) {// 解析消息数据时出现错误console.error('解析消息数据时出错:', error);}}},onerror(err) {// 发生错误时的回调console.error('SSE 发生错误:', err);},});} catch (error) {// 启动 SSE 连接时的错误处理console.error('启动 SSE 失败:', error);}
}function displayMessage(messageContent) {// 获取消息显示的容器const messageContainer = document.getElementById('text');if (!messageContainer || !messageContent) {console.error('无效的消息内容或容器未找到');return;}// 创建新的元素来显示消息const newMessageElement = document.createElement('p');messageContainer.appendChild(newMessageElement);// 使用 requestAnimationFrame 进行逐字显示效果let index = 0;const typingSpeed = 100;  // 每个字符的显示间隔时间(毫秒)// 清空之前的消息内容newMessageElement.textContent = '';// 定义逐字显示的函数function typeNextCharacter() {if (index < messageContent.length) {newMessageElement.textContent += messageContent[index];  // 添加下一个字符index++;// 使用 requestAnimationFrame 来平滑显示字符setTimeout(() => requestAnimationFrame(typeNextCharacter), typingSpeed);}}// 开始逐字显示消息requestAnimationFrame(typeNextCharacter);
}// 启动 SSE 流
startSseWithPost();

http://www.ppmy.cn/ops/137076.html

相关文章

Http 请求协议

HTTP的请求协议 请求数据格式&#xff1a; 请求行 请求数据的第一行&#xff0c;包含请求方式、资源路径、协议及版本。 请求头 从请求数据的第二行&#xff0c;以key: value的格式 常见的请求头 Host&#xff1a;请求的主机名&#xff0c;如&#xff1a;localhost:8080&#x…

Qt界面篇:QMessageBox高级用法

1、演示效果 2、用法注意 2.1 设置图标 用于显示实际图标的pixmap取决于当前的GUI样式。也可以通过设置icon pixmap属性为图标设置自定义pixmap。 QMessageBox::Icon icon(

常见线程安全问题之复合操作

创作内容丰富的干货文章很费心力&#xff0c;感谢点过此文章的读者&#xff0c;点一个关注鼓励一下作者&#xff0c;激励他分享更多的精彩好文&#xff0c;谢谢大家&#xff01; 复合操作的问题本质上和 TOCTOU 是一样的&#xff0c;如果有多个操作&#xff08;如同一变量的读写…

解决 java -jar 报错:xxx.jar 中没有主清单属性

问题复现 在使用 java -jar xxx.jar 命令运行 Java 应用程序时&#xff0c;遇到了以下错误&#xff1a; xxx.jar 中没有主清单属性这个错误表示 JAR 文件缺少必要的启动信息&#xff0c;Java 虚拟机无法找到应用程序的入口点。本文将介绍该错误的原因以及如何通过修改 pom.xm…

工作坊报名|使用 TEN 与 Azure,探索你的多模态交互新场景

GPT-4o Realtime API 发布&#xff0c;语音 AI 技术正在进入一场新的爆发。语音AI技术的实时语音和视觉互动能力将为我们带来更多全新创意和应用场景。 实时音频交互&#xff1a; 允许应用程序实时接收并响应语音和文本输入。自然语音生成&#xff1a; 减少 AI 技术生成的语音…

多任务基础知识学习

一、单任务与多任务的区别&#xff1a; 学习链接&#xff1a;https://zhuanlan.zhihu.com/p/27421983 多任务学习:单模型解决多个问题_什么是单任务模型-CSDN博客 SingleTask: Train one model for each task, respectively 多任务学习(Multi-Task Leamning,MTL)是机器学习只…

微信小程序中会议列表页面的前后端实现

题外话&#xff1a;想通过集成腾讯IM来解决即时聊天的问题&#xff0c;如果含语音视频&#xff0c;腾讯组件一年5万起步&#xff0c;贵了&#xff01;后面我们改为自己实现这个功能&#xff0c;这里只是个总结而已。 图文会诊需求 首先是个图文列表界面 同个界面可以查看具体…

java框架Netty网络编程——问鼎篇

Netty进阶 01 初识 Netty&#xff1a;为什么 Netty 这么流行&#xff1f; 粘包现象 案例 服务端代码 public static void main(String[] args) {NioEventLoopGroup bossGroupnew NioEventLoopGroup(1);NioEventLoopGroup workerGroupnew NioEventLoopGroup(2);try {ServerBoo…