手写chatGPT——fetch解析text/event-stream会话流并逐字回显到页面——js技能提升

news2024/11/17 5:21:12

直接上效果图:
在这里插入图片描述
页面分上下两部分,上面是会话界面,底部是提交框。

直接上代码:

解决步骤1:引入vue+elementUi

  <head>
    <meta charset="UTF-8" />
    <title>Fetch Stream Example</title>
  </head>
  <script src="./vue.js"></script>
  <!-- 引入样式 -->
  <link
    rel="stylesheet"
    href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
  />
  <!-- 引入组件库 -->
  <script src="https://unpkg.com/element-ui/lib/index.js"></script>

解决步骤2:搭建静态页面——使用vue

<div id="bodyWrap">
      <div class="wrapCon">
        <div class="wrapCls" v-for="(item,index) in historyList" :key="index">
          <div class="questionWrap">
            <img
              src="https://img2.baidu.com/it/u=2125727497,3458875842&fm=253&fmt=auto&app=138&f=JPEG"
              alt=""
            />
            <div class="question">{{item.message}}</div>
          </div>
          <div class="output">{{item.content}}</div>
        </div>
      </div>

      <div id="footerId">
        <el-input
          type="textarea"
          :rows="3"
          ref="textearaRef"
          placeholder="请输入内容"
          v-model="textarea"
        >
        </el-input>
        <el-button size="small" type="primary" @click="handleSubmit"
          >提交</el-button
        >
      </div>
    </div>

解决步骤3:css部分

 <style>
    * {
      margin: 0;
      padding: 0;
    }
    body {
      font-size: 14px;
      padding: 10px;
    }
    #bodyWrap {
      height: 100vh;
      border: 1px solid #efefef;
      border-radius: 10px;
      padding: 10px;
    }
    .wrapCon {
      height: calc(100vh - 60px);
      overflow: auto;
    }
    #footerId {
      position: fixed;
      bottom: 10px;
      display: flex;
      left: 30px;
      right: 30px;
    }
    #footerId .el-textarea {
      margin-right: 10px;
      flex: 1;
    }
    .wrapCls {
      margin: 10px;
      padding: 10px;
      border: 1px solid #efefef;
      border-radius: 5px;
    }
    .questionWrap {
      display: flex;
      width: 60%;
    }
    .questionWrap img {
      border-radius: 50%;
      margin-right: 10px;
      width: 40px;
      height: 40px;
    }
    .output {
      margin-top: 10px;
      background-color: #d9ecff;
      border: 1px solid #409eff;
      padding: 10px;
      border-radius: 4px;
      margin-left: 50px;
      min-height: 20px;
    }
    .question {
      background-color: #f0f9eb;
      border: 1px solid #67c23a;
      padding: 10px;
      border-radius: 4px;
    }
  </style>

解决步骤4:js部分

<script>
 new Vue({
   el: '#bodyWrap',
   data() {
     return {
       textarea:
         '我的元器件参数是: 100Ω±1%-1/10W 帮我选择一个国巨型号,精简一下话术20字之内,只要给我一个型号就可以',
       historyList: [],
     };
   },
   mounted() {
     this.$refs.textearaRef.focus();
   },
   methods: {
     handleSubmit() {
       if (!this.textarea) {
         return;
       }
       this.render();
     },
     render() {
       let that = this;
       let params = {
         message: that.textarea,
         content: '',
       };
       that.historyList.push(params);
       // 假设你要获取的数据是一个文本流
       //下面的接口地址是我局域网后端同事提供的,需要换成你们自己的才可以
       fetch('http://192.168.35.3:11434/api/chat', {
         method: 'POST',
         headers: {
           'Content-Type': 'application/json',
         },
         body: JSON.stringify({
           model: 'llama3.1',
           messages: [
             {
               role: 'user',
               content: this.textarea,
             },
           ],
           stream: true,
         }),
         dataType: 'text/event-stream',
       })
         .then((response) => response.body) // 获取响应体
         .then((body) => {
           // 创建一个读取流
           const reader = body.getReader();
           // 使用循环来读取数据流
           const p = document.createElement('p');
           const output = document.createElement('div');
           (function read() {
             reader.read().then(({ done, value }) => {
               let con = '';
               if (done) {
                 return;
               }
               let json = new TextDecoder('utf-8').decode(value);
               if (json) {
                 let obj = JSON.parse(json);
                 p.appendChild(
                   document.createTextNode(obj.message.content)
                 );
                 output.appendChild(p);
                 that.historyList[that.historyList.length - 1].content +=
                   obj.message.content;
               }
               // 递归调用read函数以继续读取数据流
               read();
             });
           })();
         });
     },
   },
 });
</script>

最开始其实是想用eventSource的方式来处理的。但是onMessage一直收不到后端的数据,所以后面就放弃了,转而用了fetch。下面也记录一下eventSource的用法:

下面使用eventSource来处理,但是效果没有实现

解决步骤1:在h5中使用fetchEventSource,需要用到webpack

1.初始化包管理文件
npm install
2.安装webpackwebpack-cli编译器
npm install webpack --save-dev
npm install webpack-cli --save-dev
3.新建webpack.config.js文件,文件内容如下:
const path = require('path');
module.exports = {
  entry: './test.js', //打包入口
  output: {
    //打包文件输出路径
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.js',
  },
};
4.新建test.js文件,文件内容如下:
import { fetchEventSource } from '@microsoft/fetch-event-source';
fetchEventSource('http://192.168.35.3:11434/api/chat', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'llama3.1',
    messages: [{ role: 'user', content: '你好啊' }],
    stream: true,
  }),
  onmessage: (ev) => {
    console.log('ev', ev, JSON.parse(ev.data));
    // document.getElementById('output').textContent += ev.data;
    console.log(111);
  },
  onopen() {
    console.log('Connection opened');
  },
  onclose() {
    console.log('Connection closed');
  },
  onerror(err) {
    console.error('Error:', err);
  },
});

感觉上面的代码没问题,但是onmessage方法中就是收不到ev的内容,所以没有实现最终的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1981680.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java计算机毕设】社团管理系统MySQL springboot vue maven项目设计源码代码+文档 前后端可分离也可不分离

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】社团管理系统MySQL springboot vue maven项目设计源码代码文档 前后端可分离也可不分离 2项目介绍 系统功能&#xff1a; 社团管理系统包括管理员、团长、学生三种角色。 管理员功能包括个人中心模块用于修…

ElasticSearch集成webFlux响应式开发

目录 前言 1.Weflux特点&#xff1a; 2.WebFlux简单集成ElasticSearch 2.1 引入基本依赖模块 3.application.yml文件的配置 4.定义Product实体类 5.定义ElasticSearch的数据访问层接口 6.定义Controller 7.启动SpringBoot程序&#xff0c;用postman进行接口测试 前言 We…

mysql的 undo log、redo log、bin log、buffer pool

文章目录 Buffer Pool为什么需要Buffer PoolBuffer Pool 缓存了什么 Redo log为什么需要 redo log&#xff1f;redo log 什么时候刷盘&#xff1f;redo log 文件写满了怎么办&#xff1f; undo log 本文章内容都来自小林coding博主&#xff0c;基于他的文章内容&#xff0c;加一…

保研408真题练习:2010年全国硕士研究生入学统一考试(单选篇1)

&#x1f9ca;&#x1f9ca;&#x1f9ca;单项选择题&#xff08;共40道&#xff09; &#x1f9ca;数据结构&#xff08;11道&#xff09; &#x1f965;1.2.考察的都是栈和队列的入栈&#xff08;队&#xff09;出栈&#xff08;队&#xff09;问题 这道题目重点是掌握各种…

linux网络编程(2)

什么是多线程服务器&#xff1f; 先认识什么是单线程服务器 就是服务器只处理一个客户端信息。 多线程服务器类似的&#xff0c;就是处理很多个客户端的信息。 多进程服务器的核心理念 使用while循环&#xff0c;让服务器一直处于接收状态&#xff0c;每接收到一个客户端&am…

翰德恩赋能新能源龙头企业硬件敏捷研发

该企业始创于1984年&#xff0c;是全球知名的智慧能源系统解决方案提供商。创立40年来&#xff0c;形成了集“发电、储电、输电、变电、配电、售电、用电”为一体的全产业链优势&#xff0c;业务遍及140多个国家和地区&#xff0c;拥有4大全球研发中心&#xff0c;建立6大国际营…

Ldap未授权访问漏洞

LDAP中文全称为&#xff1a;轻型目录访问协议&#xff08;Lightweight Directory Access Protocol&#xff09;&#xff0c;默认使用389&#xff0c; LDAP 底层一般使用 TCP 或 UDP 作为传输协议。目录服务是一个特殊的数据库&#xff0c;是一种以树状结构的目录数据库为基础。…

药厂子母钟系统,强抗干扰能力,满足复杂生产环境

在制药行业中&#xff0c;精确的时间同步对于确保药品生产的质量和合规性至关重要。药厂子母钟系统作为一种高度可靠的时间同步解决方案&#xff0c;不仅能够提供准确的时间信息&#xff0c;还具有强大的抗干扰能力&#xff0c;非常适合在复杂的生产环境中使用。本文将详细介绍…

登录注册功能开发

本篇文章记录怎么实现一个简单的登陆注册功能。 讲解里的代码是不完全的&#xff0c;具体的代码我会放在文章最后 文章目录 准备为什么要有登录&#xff1a;简述注册功能&#xff1a;简述登录功能完全代码&#xff1a;数据实体部分&#xff1a;Users类&#xff1a;UsersLoginD…

ME31L-创建计划协议

ME31L创建计划协议 一般常用的就是LA和LPA LP不需要审批&#xff0c;LPA需要审批。 计划协议和采购申请实现的功能相同。 计划协议创建界面和创建合同的界面相同。 ME38可以分配交货计划行。 输入计划协议号。回车。 双击行项目后&#xff0c;维护行项目字段。 然后可以在货…

一个线程在sleep的时候set一个信号会起作用吗

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

代码随想录第29天|贪心

134.加油站 代码随想录 代码随想录 索引01234gas12345cost34512 计算每个加油站的剩余油量&#xff0c;累计sum&#xff0c;一旦<0就从下一个重新计数。 我还没理解为什么我们不需要计算环路的sum&#xff0c;而是只需要遍历一次。 因为使用了两个变量&#xff1a;curSu…

Java ExecutorService:你真的了解它吗?

文章目录 一、什么是ExecutorService&#xff1f;二、ExecutorService的核心功能三、如何创建和使用ExecutorService&#xff1f; 时光匆匆&#xff0c;又来到另一个里程碑&#xff0c;感谢粉丝们的陪伴&#xff0c;有你们真好~ 不水文啦&#xff0c;一起加油叭~ 一、什么是Exe…

【Java】Jsoup 解析HTML报告

一、需求背景 有好几种报告文件&#xff0c;目前是人肉找报告信息填到Excel上生成统计信息 跟用户交流了下需求和提供的几个文件&#xff0c;发现都是html文件 其实所谓的报告的文件&#xff0c;就是一些本地可打开的静态资源&#xff0c;里面也有js、img等等 二、方案选型 前…

adb环境变量配置(附详细图解)

adb环境变量配置&#xff08;附详细图解&#xff09; 1、找到ADB工具的位置。通常&#xff0c;如果你Android Studio已经安装了Android SDK&#xff0c;ADB工具位于SDK的platform-tools目录下。我这里的目录是C:\Users\user\AppData\Local\Android\Sdk\platform-tools\adb.exe…

微信防封指南请收好

一、新号与老号的添加限制 建议新注册的微信号主动添加好友的数量不宜过多&#xff0c;推荐每日添加不超过5个好友&#xff1b;对于老号&#xff0c;建议每日添加不超过20个好友。保持适度的添加速度&#xff0c;避免被系统判定为异常操作。 二、避免使用营销性词汇 在发送消…

【计算机视觉】图像处理基本知识

一 基本的图像操作和处理 1.1 PIL&#xff1a;Python图像处理类库 PIL&#xff08;Python Imaging Library&#xff0c;图像处理库&#xff09;提供了通用的图像处理功能&#xff0c;以及大量有用的基本图像操作。PIL库已经集成在Anaconda库中&#xff0c;推荐使用Anaconda&a…

openmetadata自定义连接器开发教程

openmetadata自定义连接器开发教程 一、开发通用自定义连接器教程 官网教程链接&#xff1a; 1.https://docs.open-metadata.org/v1.3.x/connectors/custom-connectors 2.https://github.com/open-metadata/openmetadata-demo/tree/main/custom-connector &#xff08;一&…

【ARM】SMMU系统虚拟化(3)_ VMSAv8-64 address translation stages

讲解颗粒度granule size如何影响地址转换的过程&#xff1a; 对于每个颗粒度来说&#xff1a; 输入的地址范围如何影响起始的lookup levels。对于stage2 转换来说&#xff0c;给链接的转换页表造成的可能的影响。TTBR 地址和indexing对于起始的lookup 1.以4KB的translation g…

初始JVM ! ! ! 相信我,中国人不骗中国人,这一篇也就入门了!!!

目录 1.JVM到底是什么&#xff1f; 2.JVM的三大核心是什么&#xff1f; 1. 解释和运行 2. 内存管理 3. 即时编译&#xff08;JIT&#xff09; 3.常见的Jvm虚拟机有哪些&#xff1f; 1.3.1 Java虚拟机规范 1.3.2 Java虚拟机规范 4. JVM的组成 4.1、类的生命周期 4.1…