原生js: AI聊天功能, 仿照chatGPT问答功能

问:

现在我们需要一个ai聊天功能, 接口已经给出:

只要是message就是我们的数据, 是message_end就是结束信息, 其他的我们不需要管.

回答:

我们不使用传统的fetch请求这个接口, 而是使用sse, eventSource去请求,

当我们输入框回车 或者 点击元素, 获取到输入框中用户输入的值, 然后创建用户问题标签,

同时, 检查当前有无eventSource , 没有的话创建eventSource, eventSource要设置,withCredentials:true携带cookie.

当请求接口的时候没有返回值之前, 我们需要创建一个 "正在加载..."的标签, 给用户提示.

在eventSource.onmessage外面创建一个newStr空字段, 

注意: 要在eventSource.onmessage外面创建, 在里面创建会创建很多的newStr,

eventSource.onmessage处理返回的数据,

这里加了一个小功能: 接口返回文字我们的网页跟随, 返回文字增加一行我们就向下滑动,时刻是在最下面, 不需要用户手动下滑.

判断: 当时要是message_end, 就调用eventSource.close关闭连接.


 

问:

eventSource = new EventSource(fullUrl, { withCredentials: true });的{ withCredentials: true }什么呢意思?

回答:

在JavaScript中,`EventSource` 是用于接收服务器推送的一种技术,通常用于实现服务器发送事件(Server-Sent Events,SSE)。在你的代码片段中,`EventSource` 的初始化使用了一个配置对象 `{ withCredentials: true }`。

`withCredentials: true` 的意思是在跨域请求中携带凭据(credentials),比如说在请求中包括 cookies 和 HTTP认证信息(即使是跨域请求)。这对于需要在跨域请求中进行认证或会话管理的情况非常有用。具体来说:

- 如果 `withCredentials` 设置为 `true`,则 `EventSource` 在发送请求时会包括当前页面的 cookies 和 HTTP认证信息(如果存在的话)。
- 如果 `withCredentials` 设置为 `false` 或未指定,默认情况下,跨域请求中不会携带 cookies 和 HTTP认证信息。

使用 `withCredentials: true` 的场景包括需要进行用户身份验证或授权的跨域服务器推送。这确保了在跨域情况下能够正确地传递身份验证信息,使得服务器能够基于用户的会话状态提供个性化的数据或服务。

需要注意的是,为了确保安全性,跨域请求中的 `withCredentials` 功能通常需要服务器端的支持,并且需要服务器配置允许接收包含凭据的请求

问:

我们调用ai聊天接口, 触发一次事件, 会不停的调用这个接口, 请问为什么?

回答:

function handleKeyPress(event) {if (event.keyCode === 13) {event.preventDefault();var input = document.getElementById('chat_input');var question = input.value.trim();if (question !== '') {var dialogueContainer = document.querySelector('.dialogue dl');let str = "";// 创建用户问题的对话框项var dt = document.createElement('dt');var dtParagraph = document.createElement('p');dtParagraph.textContent = '' + question + '';dt.appendChild(dtParagraph);dialogueContainer.appendChild(dt);const appinfo = getCookie('appinfo');var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';var params = {query: question,unicodeToChinese: 1,};if (appinfo) {params.appinfo = appinfo;}var queryString = new URLSearchParams(params).toString();var fullUrl = apiUrl + '?' + queryString;eventSource = new EventSource(fullUrl, { withCredentials: true });// 创建对话框和段落元素var dd = document.createElement('dd');var ddParagraph = document.createElement('p');dd.appendChild(ddParagraph);dialogueContainer.appendChild(dd);// 初始化段落元素的文本内容var newStr = '';// 处理消息事件eventSource.onmessage = function (event) {var data = JSON.parse(event.data);

这里我们触发handleKeyPress事件后, eventSource = new EventSoutce会一直调用, 所以就一直创建新的连接,

需要怎么改?

function handleKeyPress(event) {if (event.keyCode === 13) {event.preventDefault();var input = document.getElementById('chat_input');var question = input.value.trim();if (question !== '') {var dialogueContainer = document.querySelector('.dialogue dl');let str = "";// 创建用户问题的对话框项var dt = document.createElement('dt');var dtParagraph = document.createElement('p');dtParagraph.textContent = '' + question + '';dt.appendChild(dtParagraph);dialogueContainer.appendChild(dt);const appinfo = getCookie('appinfo');var apiUrl = 'https://xxxx.xxxx.com/xxxx-x/xxxx/xxxx';var params = {query: question,unicodeToChinese: 1,};if (appinfo) {params.appinfo = appinfo;}var queryString = new URLSearchParams(params).toString();var fullUrl = apiUrl + '?' + queryString;// 检查是否已有 EventSource 实例if (!eventSource || eventSource.readyState === EventSource.CLOSED) {eventSource = new EventSource(fullUrl, { withCredentials: true });

我们在创建eventSource = new EventSource之前先进入判断: 当前没有eventSource的情况采取创建连接,否则不创建连接.这样修改后触发一次handleKeyPress事件就创建一个eventSource连接.

其中遇到的问题:

接口返回得文字是一个字或者两个字或者三个字, 我们在页面展示的时候, 总是会按字数的增加递增或者一个字一行:

你好

你好啊

你好啊是

你好啊是啥

x

x

x

xxx

的一个

xx

xx

xx

x

x

xx

xx

x

xx

xx

深度

xx

xxx

xx

上面两种情况不是我们需要的, 

这个情况的原因是:

我们每次循环遍历文字我们都创建了一个p标签, 导致接口返回一个字我们就会重新开启一行, 这是p标签导致的.

最终代码:

<!-- 聊天显示 -->
<div class="dialogue"><dl><!--<dt><p>“财新数据通”是什么?</p></dt><dd><p>“财新数据通是集金融数据、权威资讯、品质服务于一体的金融数据资讯产品,帮助读者完成资讯获取、背景调查、数据分析和决策制定</p></dd>--></dl></div>
<!-- 聊天输入框 --><div class="customer_service"><p>联系客服</p><!-- <input class="chat_input" type="text" placeholder="请输入您想咨询的问题…"> --><input id="chat_input" class="chat_input" type="text" placeholder="请输入您想咨询的问题…"onkeydown="handleKeyPress(event)"></div>// 聊天函数
var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问, 控制回车, 只调用一次接口function handleKeyPress(event) {console.log(event, '聊天函数event');if (event.keyCode === 13 || event.type === 'click') {if (event.keyCode === 13) {event.preventDefault();event.stopPropagation(); // 阻止事件继续传播}var input = document.getElementById('chat_input');var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本if (question !== '') {var dialogueContainer = document.querySelector('.dialogue dl');let str = "";// 创建用户问题的对话框项var dt = document.createElement('dt');var dtParagraph = document.createElement('p');dtParagraph.textContent = '' + question + '';dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;dt.appendChild(dtParagraph);dialogueContainer.appendChild(dt);const appinfo = getCookie('appinfo');var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';var params = {query: question,unicodeToChinese: 1,};if (appinfo) {params.appinfo = appinfo;}var queryString = new URLSearchParams(params).toString();var fullUrl = apiUrl + '?' + queryString;// 检查是否已有 EventSource 实例if (!eventSource || eventSource.readyState === EventSource.CLOSED) {eventSource = new EventSource(fullUrl, { withCredentials: true });// 创建对话框和段落元素//var dd = document.createElement('dd');//var ddParagraph = document.createElement('p');//dd.appendChild(ddParagraph);//dialogueContainer.appendChild(dd);var loadingAnswer = '正在加载...'; // 定义 loading 回答// 创建对话框和段落元素 创建 loading 回答的对话框项var loadingDt = document.createElement('dd');var loadingDtParagraph = document.createElement('p');loadingDtParagraph.textContent = loadingAnswer;loadingDt.appendChild(loadingDtParagraph);dialogueContainer.appendChild(loadingDt);// 初始化段落元素的文本内容var newStr = '';// 处理消息事件eventSource.onmessage = function (event) {var data = JSON.parse(event.data);if (data.event === "message") {// 使用每个新字符更新段落元素的文本内容 更新 loading 回答为接口返回的数据newStr += data.answer;loadingDtParagraph.textContent = newStr;// 滚动页面到底部window.scrollTo(0, document.body.scrollHeight);// 如果是第一个字符,显示对话框if (newStr.length === 1) {showDialogue();}// 如果接收到完整的回复,关闭 EventSource 对象if (data.complete) {eventSource.close();}} else if (data.event === "message_end") {eventSource.close();}};function showDialogue() {// 显示对话框和段落元素样式loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框}}// 清空输入框input.value = '';}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringSecurity+Mysql数据库实现用户安全登录认证

Spring Security 是一个提供身份认证、授权和防范常见攻击的安全权限框架。无论是对命令式&#xff0c;还是响应式web应用程序都完美支持&#xff0c;现在主要用作保护基于 Spring 框架的应用程序的事实标准。相对于shiro来说&#xff0c;SpringSecurity功能更加复杂而且更加强…

吴恩达:如何系统学习机器学习?

最近在知乎圆桌里看到吴恩达的回答&#xff0c;【如何系统学习机器学习&#xff1f;】颇为惊喜&#xff0c;仿佛看到了知乎刚成立时的样子&#xff0c;请各个行业大佬来分享专业知识。 该回答目前已经有三千多赞&#xff0c;评论区也相当火爆&#xff0c;一片膜拜之声。 吴恩…

Java线程池的这几个大坑,你踩过几个?

首先看一个简单的例子&#xff1a;代码可能会抛出空指针异常,但这个异常就会被吞掉。 要优雅解决问题&#xff0c;可以为线程池设置一个全局的异常处理器,使用自定义的线程工厂来设置! java public class CustomThreadFactory implements ThreadFactory { private final Threa…

Vue3从零开始——掌握setup、ref和reactive函数的奥秘

文章目录 一、Vue 3 组合式 API 概述二、setup​ 函数的基本使用2.1 setup​ 函数的特点2.2 setup​ 函数的基本结构2.3 实现一个简单的小demo 三、ref​ 函数的功能和应用3.1 ref​函数介绍3.2 基本使用3.2.1 定义ref​数据3.2.2 修改响应式变量 3.3 使用ref​函数实现计数器 …

LabVIEW工件表面瑕疵识别系统

开发了一种利用LabVIEW和IMAQ Vision视觉工具进行工件表面瑕疵识别的系统。该系统通过图像处理技术识别并分类工件表面的裂纹、划痕等缺陷&#xff0c;从而提升生产线的分拣效率和产品质量。 项目背景 工业生产中&#xff0c;工件表面的缺陷直接影响产品质量和生产效率。传统人…

Go语言加Vue3零基础入门全栈班11 Go语言+gorm用户管理系统实战 2024年08月03日 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728Go语言操作MySQL开发用户管理系统API教程_20240729Redis零基础快速入门_20231227GoRedis开发用户管理系统API实战_20240730Mo…

Linux-入门-02

上节我们讲了如何安装虚拟机,本节课讲一些linux的常用命令,首先我们需要做一些配置,我们的centos的镜像是最小版安装,里面什么也没有,所以我们的linux是不能进行联网的,接下来我们就来一步一步联网 1、配置网络 首先我们需要先使用命令查看ip地址,linux中一切皆文件,只能使用命…

数据结构——排序(1):插入排序

目录 一、排序的概念 二、排列的运用 三、常见的排序算法 四、插入排序 1.直接插入排序 &#xff08;1&#xff09;思路 &#xff08;2&#xff09;过程图示 &#xff08;3&#xff09;代码实现 (4)代码解释 &#xff08;5&#xff09;特性 2.希尔排序 &#xff08;1…

CSS技巧专栏:一日一例 20-纯CSS实现点击会凹陷的按钮

本例图片 案例分析 其实这个按钮非常的简单啊,主要就是利用了box-shadow的inset。 布局代码 <button class="base">凹下的按钮</button> 基础样式 :root{--main-bg-color: #dcdcdc; /* 将页面背景色调整为浅灰色 */--color:#000;--hover-color:#99…

LLM - GQA 之 Group Query Attention 论文与源码精读

目录 Abstract 1.Introduction 2.Method 2.1 UpTraining 2.2 Grouped-query attention 3.Experiments 3.1 Experimental setup 3.2 Main Results 3.3 Ablations 4.Related Work 5.Conclustion 6.Code 6.1 repeat_kv 6.2 Attention Arg Init 6.3 Attention Mat I…

IT服务质量管理攻略(至简)

质量管理、风险管理和信息安全管理是IT服务监督管理的重要内容&#xff0c;三者之间相对独立。IT服务质量管理是通过制订质量方针、质量目标和质量计划&#xff0c;实施质量控制、质量保证和质量改进活动&#xff0c;确保IT服务满足服务级别协议的要求&#xff0c;最终获得用户…

openvidu私有化部署

openvidu私有化部署 简介 OpenVidu 是一个允许您实施实时应用程序的平台。您可以从头开始构建全新的 OpenVidu 应用程序&#xff0c;但将 OpenVidu 集成到您现有的应用程序中也非常容易。 OpenVidu 基于 WebRTC 技术&#xff0c;允许开发您可以想象的任何类型的用例&#xf…

[算法]第一集 递归(未完待续)

递归啊递归&#xff0c;说简单简单&#xff0c;说难难。 首先我们要知道 一、什么是递归&#xff1f; 我们再C语言和数据结构里都用了不少递归&#xff0c;这里就不多详细介绍。 递归简单来说就是函数自己调用自己的情况 二、为什么要用递归呢&#xff1f; 本质来说其实就…

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;32选择#2&#xff1a;33选择#3&#xff1a;34 1.5 检查WRF是否…

Linux 中的信号处理

Linux 中的信号处理是操作系统中非常重要的一个概念&#xff0c;通过信号处理&#xff0c;进程之间可以进行通信、协调以及实现一些重要的功能。本文将从信号的概念、类型、生成、传递、处理、以及常见的信号处理函数等方面展开讨论&#xff0c;以帮助读者更深入地了解 Linux 中…

【机器学习】BP神经网络中的链式法则

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 BP神经网络中的链式法则1. 引言2. 链式法则基础2.1 什么是链式法则&#xff1f;…

29.Labview界面设计(下篇) --- 自定义控件库、界面布局与外观设计

摘要&#xff1a; 题主在上一篇文章中向大家讲解了前面板逻辑框架及结构的搭建和控件的类型介绍&#xff0c;那么本章主要围绕前面板的控件布局以及控件的自定义类型和背景等外观优化项中来讲解。 本篇文章讲解界面设计的下篇内容&#xff0c;上篇内容链接大家可以直接点击链接…

国家统计局中国主要城市面板数据(1990-2023年)

数据说明&#xff1a;数据来源于国家统计局&#xff0c;指标包含&#xff1a;城市、年份、第三产业增加值、第一产业增加值 地区生产总值、第二产业增加值、年末户籍人口、城镇非私营单位在岗职工平均工资 房地产开发投资额、房地产开发住宅投资额、房地产开发办公楼投资额、房…

Linux C 程序 【03】线程栈空间

1.开发背景 上一个篇章创建了线程&#xff0c;参考 FreeRTOS&#xff0c;每个线程都是有自己的内存空间&#xff0c;Linux上面也是一样的&#xff0c;这个篇章主要描述线程栈空间的设置。 2.开发需求 设计实验&#xff1a; 1&#xff09;创建线程&#xff0c;并配置线程内存大…

培训第二十二天(mysql数据库主从搭建)

上午 1、为mysql添加开机启动chkconfig [rootmysql1 ~]# chkconfig --list //列出系统服务在不同运行级别下的启动状态注&#xff1a;该输出结果只显示 SysV 服务&#xff0c;并不包含原生 systemd 服务。SysV 配置数据可能被原生 systemd 配置覆盖。 要列出 systemd 服务…