图文并茂手把手教你基于React多种方案调用OpenAI接口实现ChatGPT打字机效果

先来看一下效果

代码仓库

前期准备

  • 前端项目
  • 后端接口(OpenAI接口即可)

启动一个新的 React 项目

  • 如果小伙伴们有现有项目,可跳过此步骤直接进入下一步~
  • Next.js 是一个全栈式的 React 框架。它用途广泛,可以让你创建任意规模的 React 应用------可以是静态博客,也可以是复杂的动态应用。要创建一个新的 Next.js 项目,请在你的终端运行:
lua 复制代码
npx create-next-app@latest

下载依赖

bash 复制代码
cd xiaojin-react-chatgpt

npm i

运行项目

arduino 复制代码
npm run dev

引入 antd

安装并引入 antd

css 复制代码
npm install antd --save

基础页面准备

  • 我们先使用简单的代码来实现效果
  • 修改src\app\page.js代码如下
javascript 复制代码
"use client";
import { useState } from "react";
import { Input, Button } from "antd";

const { TextArea } = Input;
export default function Home() {
  let [outputValue, setOutputValue] = useState("");
  return (
    <main className="flex min-h-screen text-black flex-col items-center justify-between p-24">
      <h2>Chat GPT 打字机效果</h2>
      <TextArea rows={17} value={outputValue} />
      <Button>发送请求</Button>
    </main>
  );
}

页面效果如下

接口准备

  • 注册一个OpenAI账号(或者使用其他接口也可以)

接口文档示例

Chat聊天-聊天完成对象-参数说明

参数 类型 描述
id string 聊天完成的唯一标识符
choices array 聊天完成选项列表。如果n大于1,可以有多个选项
created integer 创建聊天完成的Unix时间戳(秒)
model string 用于聊天完成的模型
system_fingerprint string 该指纹表示模型运行的后端配置
object string 对象类型,总是 chat.completion
usage object 完成请求的使用统计信息
completion_tokens integer 生成的完成中的标记数
prompt_tokens integer 提示中的标记数
total_tokens integer 请求中使用的标记总数(提示 + 完成)

准备接口参数

yaml 复制代码
 const data = {
      model: "XXX",
      messages: [
        {
          role: "user",
          content: "写一篇1000字关于春天的作文",
        },
      ],
      prompt: "写一篇1000字关于春天的作文",
      temperature: 0.75,
      stream: true,
    };

方案1:使用fetch来处理stream流实现打字机效果

使用流的方式处理 Fetch

  • mdn文档

  • Fetch API 允许你跨网络获取资源,它提供了现代化的 API 去替代 XHR。它有一系列的优点,真正好的是,浏览器最近增加了将 fetch 响应作为可读流使用的能力。

  • Request.body 和 Response.body 属性也是这样,它们将主体内容暴露作为一个可读流的 getter。

调用代码示例

javascript 复制代码
    const response = await fetch(url, {
      method: "POST",
      body: JSON.stringify(data),
      headers: {
        "Content-Type": "application/json",
      },
    });

    const reader = response.body.getReader();
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        console.log("***********************done");
        console.log(value);
        break;
      }
      console.log("--------------------value");
      console.log(value);
    }
  • 在函数中,我们使用 response.body.getReader() 将 reader 锁定到该流,然后遵循我们之前看到的相同的模式------使用 reader 读取每个分块,在再次运行 read() 方法之前,检查 done 是否为 true,如果是 true,处理结束,如果是 false,读取下一个分块并且处理它。
  • 通过循环获取传输的数据

编写页面逻辑代码

  • 我们暂时使用固定参数来进行模拟
  • 写一个简单的demo来演示
javascript 复制代码
"use client";
import { useState } from "react";
import { Input, Button } from "antd";
const { TextArea } = Input;
export default function Home() {
  let [outputValue, setOutputValue] = useState("");
  const send = async () => {
    const url = "http://10.169.112.194:7100/v1/chat/completions";
    const data = {
      model: "chatglm2-6b",
      messages: [
        {
          role: "user",
          content: "写一篇1000字关于春天的作文",
        },
      ],
      prompt: "写一篇1000字关于春天的作文",
      temperature: 0.75,
      stream: true,
    };
    const response = await fetch(url, {
      method: "POST",
      body: JSON.stringify(data),
      headers: {
        "Content-Type": "application/json",
      },
    });

    const reader = response.body.getReader();
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        console.log("***********************done");
        console.log(value);
        break;
      }
      console.log("--------------------value");
      console.log(value);
    }
  };
  return (
    <main className="flex min-h-screen text-black flex-col items-center justify-between p-24">
      <h2>Chat GPT 打字机效果</h2>
      <TextArea rows={17} value={outputValue} />
      <Button onClick={send}>发送请求</Button>
    </main>
  );
}

点击按钮查看打印结果

  • 我们可以看到打印出来的都是buffer字符串,我们需要对其进行解析才可以得知最终结果

解析buffer

arduino 复制代码
const encode = new TextDecoder("utf-8");
const reader = response.body.getReader();
 while (true) {
      const { done, value } = await reader.read();
      const text = encode.decode(value);
      if (done) {
        console.log("***********************done");
        console.log(text);
        break;
      }
      console.log("--------------------value");
      console.log(text);
    }

查看解析

我们可以看到解析结果格式如下

css 复制代码
data: {"id": "chatcmpl-3zmRJUd4TTpm9xP9NbQVHw", "model": "chatglm2-6b", "choices": [{"index": 0, "delta": {"content": "希望"}, "finish_reason": null}]}

使用正则解析数据

我们编写一个函数~~然后打印数据

ini 复制代码
 const getReaderText = (str) => {
    // 定义正则表达式匹配模式
    let result = str.match(/{"content": "(\S*)"}, "finish_reason":/);
    return result && result[1] ? result[1] : "";
  };

赋值数据到文本框

解决换行问题

解决textarea显示\n但是并没有换行的问题,经过排查发现,是接口返回的数据是\n,所以渲染有异常,因此我们可以用下面的方式去解决.

javascript 复制代码
replaceAll(/\\n/g,'\r\n')

初步实现简易打字机效果

代码块支持(待补充)

自动滚动(等我忙完补充)

方案2:axios请求方式(等我忙完补充)等待补充

今天就写到这里啦~

  • 小伙伴们,( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ我们明天再见啦~~
  • 大家要天天开心哦

欢迎大家指出文章需要改正之处~

学无止境,合作共赢

欢迎路过的小哥哥小姐姐们提出更好的意见哇~~

相关推荐
大圣编程5 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang6 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆6 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜7 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞8 小时前
异步HttpModule的实现方式
java·服务器·前端
丹宇码农10 小时前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器
2501_9437823511 小时前
【共创季稿事节】猜数字游戏:二分法思维与交互式反馈
前端·游戏·microsoft·harmonyos·鸿蒙·鸿蒙系统
GV191rLvq11 小时前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
吠品11 小时前
LangChain 里 tool_call_id 为空?一次 MCP 工具集成的排查记录
前端