首页 > 编程语言 >java集成chatGpt完整案例代码(效果和官网一样逐字输出)

java集成chatGpt完整案例代码(效果和官网一样逐字输出)

时间:2023-06-09 19:56:44浏览次数:57  
标签:java msgId content var 消息 SSE chatGpt 官网 div

背景

要集成chatGpt参考我上一篇文章即可。但是,如果要实现官网一样的效果,逐字输出,难度就提升了不少了。经过在官网的研究发现它应该是采用了SSE技术,这是一种最新的HTTP交互技术。SSE(Server-Sent Events):通俗解释起来就是一种基于HTTP的,以流的形式由服务端持续向客户端发送数据的技术。相比较WebSocket更加轻量了。有了SSE,我们就可以实现,一次HTTP请求,可以逐步获取后端内容并及时输出展示,也就可以实现ChatGpt官网的效果了。下面给出简单的实现代码

 

后端核心代码

@Controller
@RequestMapping("/chat")
public class ChatController {

    /**
     * 暂存消息(由于前端EventSource对象仅支持Get请求,故消息通过POST发送到后端后进行中转)
     */
    Map<String, String> msgMap = new ConcurrentHashMap<>();

    @Autowired
    ChatService chatService;

    /**
     * 发送消息
     *
     * @param msg 消息
     * @return 消息ID
     */
    @ResponseBody
    @PostMapping("/sendMsg")
    public String sendMsg(String msg) {
        String msgId = IdUtil.simpleUUID();
        msgMap.put(msgId, msg);
        return msgId;
    }

    /**
     * 对话
     *
     * @param msgId 消息ID
     * @return SseEmitter
     */
    @GetMapping(value = "/conversation/{msgId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter conversation(@PathVariable("msgId") String msgId) {
        SseEmitter sseEmitter = new SseEmitter();
        String msg = msgMap.remove(msgId);

        //调用流式会话服务
        chatService.streamChatCompletion(msg, sseEmitter);

        //及时返回SseEmitter对象
        return sseEmitter;
    }
}

 

前端核心代码

var eventSource = new EventSource('http://localhost:8080/chat/conversation/'+data)
             eventSource.addEventListener('open', function(e) {
                console.log("EventSource连接成功");
             });
             eventSource.addEventListener("message", function(evt){
                var data = evt.data;
                var json = JSON.parse(data);
                var content = json.content ? json.content : '';
                content = content.replaceAll('\n','<br/>');
                console.log(json)
                var outputBody = $('#outputTxt');
                outputBody.html(outputBody.html()+content);
                var outputCard = $('#outputCard');
                var scrollHeight = outputCard[0].scrollHeight;
                outputCard.scrollTop(scrollHeight);
            });
            eventSource.addEventListener('error', function (e) {
                console.log("EventSource异常:" + e)
            });

 

效果图

 

 

完整项目代码

https://github.com/hdwang123/GptTest

 

感想

在调研这个技术的时候,遇到很多挑战,虽然艰难好在最终都克服了。主要有以下几个方面的问题:

1. SSE技术仅支持GET请求,这个查了相关文章有第三方库可以实现POST,由于我这里只是做个简单的例子,所以不想再去弄什么第三方库。于是终于想到了办法,就是在后端进行消息暂存:先用POST请求将数据提交到后端,返回一个消息ID,然后再用使用SSE技术提交消息ID建立连接。

2. SSE向前端send的消息总是一把输出,并没有实现逐字输出效果。折腾了半天终于从SSE源码中看到了解决方案。那就是GetMapping控制器方法必须及时返回,要做到这一点,send消息方法的调用必须在新的线程中进行调用,我用@Async解决了

3.SSE向前端输出英文的时候丢失空格,所有英文单词挤在一起了,哎!这个我一开始是直接发送消息内容的,发现英文单词前面的空格丢失了。没办法,改成现在发送json对象字符串的方式,保证了空格不丢失。

4. 前端样式调整问题,主要遇到了滚动条自动滚动到div底部失效问题,scrollTop设置值始终为0。看了好多文章,也找不到解决方案。后来看到别人写的是两个div,灵感来了。我也用两个div,父div设置滚动,子div设置内容,这样就搞定了!

 

标签:java,msgId,content,var,消息,SSE,chatGpt,官网,div
From: https://www.cnblogs.com/hdwang/p/17470132.html

相关文章

  • Java内存分析
    一、Java内存分区java内存分区****方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。****堆java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。****虚拟机栈......
  • Java对象中转换空值的字段
    在后端返回数据给前端时,公司的框架会把值为null的数据去掉,方便前端取值。如:Person对象为:{name:"浩二",age:24,weight:null,height:114},那返回给前端的就为{name:"浩二",age:24,height:114}。如果这个时候有个需求: Integer类型的字段为null给-1 Long类型的字段为null......
  • 大家都说Java有三种创建线程的方式!并发编程中的惊天骗局!
    在Java中,创建线程是一项非常重要的任务。线程是一种轻量级的子进程,可以并行执行,使得程序的执行效率得到提高。Java提供了多种方式来创建线程,但许多人都认为Java有三种创建线程的方式,它们分别是继承Thread类、实现Runnable接口和使用线程池。但是,你们知道吗?其实在创建线程的过程中......
  • JavaScript 构造器模式:创建可重用的对象
    前言JavaScript是一种基于对象的语言,对象是JavaScript中最重要的概念之一。在JavaScript中,我们可以使用构造器模式来创建可重用的对象。本文将介绍JavaScript构造器模式的概念、用法和实例,并给出博客标题《JavaScript构造器模式:创建可重用的对象》。构造器模式构造器模......
  • Caused by: java.lang.ClassNotFoundException: com.alibaba.fastjson2.util.Wrap
    1.情景展示使用fastjson2,运行时报错:Causedby:java.lang.ClassNotFoundException:com.alibaba.fastjson2.util.Wrap2.具体分析出现这个问题,是因为pom.xml当中引用的有关fastjson的jar包冲突造成的。只要我们把冲突的jar包排除掉就可以了。3.解决方案在idea当中,使用插件......
  • Java 集合框架体系简介
    为什么要使用集合存储多个数据可以使用数组,但由于数组在内存中是连续存储的,所以会有一些限制。比如数组在创建时就要指定长度,即可以容纳的元素个数,且指定后无法更改;数组在创建时需要指定元素的类型,并且所有元素都必须是该类型或其子类;添加或删除数组中的元素需要创建一个新数组再......
  • java的接口和抽象类简单理解
    接口是定义了系统各模块应该遵守的标准。实现者对外提供哪些服务,调用者可以调用哪些服务以及如何调用服务。抽象类作为系统中各个子类的共同父类,所表现的是一种模板设计,只实现了最基础的共通功能,相当于一个中间产品,各个子类具体实现抽象方法。抽象类可以没有抽象方法,......
  • java8如何校验ssh-keygen生成的公私钥
    如果你的公私钥文件不是PEM格式的,而是其他格式,如OpenSSH格式(通常以`id_rsa`和`id_rsa.pub`命名),你可以使用Java的`JSch`库来验证它们的有效性。下面是一个示例代码,演示如何使用`JSch`库验证OpenSSH格式的公私钥对:首先,你需要在项目中引入JSch库的依赖。你可以使用Maven或手动下载并......
  • java不打印异常堆栈
    背景:生产环境抛异常,但却没有将堆栈信息输出到日志,只有简单的java.lang.NullPointerException错误信息。原因分析JVM在默认启动的时候会加上OmitStackTraceInFastThrow参数,含义是当大量抛出同样的异常的后,后面的异常输出将不打印堆栈。原因是打印堆栈的时候底层会调用到Throw......
  • 使用JAVA开发微信公众平台(一)——环境搭建与开发接入
    一、初始微信公众平台微信公众平台,即我们平时所说的“公众号”,曾用名“官方平台”、“媒体平台”,但最终命名为“公众平台”。从微信的命名我可以发现,公众平台不只是官方、媒体使用的平台,而是对所有公众都开放的统一平台。喜欢本文,请点击下方喜欢按钮呗!❤️微信公众平台地址:http......