JAVA后端调用OpenAI接口 实现打字机效果(SSE)

news/2024/9/3 0:30:30 标签: java, 开发语言, chatgpt, SSE

SSE_0">SSE

SSE(Server-Sent Events,服务器发送事件)是一种基于HTTP协议的通信技术,它允许服务器持续地将数据推送给客户端,而无需客户端发起请求。这种通信方式通常用于实时性要求较高的场景,如实时更新、通知、或者数据流式传输。
SSE与传统的Ajax轮询或长轮询相比,具有更低的延迟、更高的效率,并且更易于实现。它建立在HTTP协议之上,利用HTTP/1.1的持久连接,允许服务器在连接建立后持续地向客户端发送数据,客户端通过监听一个HTTP连接来接收这些数据。
在Web开发中,服务器通常会使用特殊的HTTP响应头(如"Content-Type: text/event-stream")来指示客户端这是一个SSE流,并且按照一定的格式发送事件数据给客户端。客户端则可以使用JavaScript中的EventSource对象来接收并处理这些事件,从而实现实时的数据更新。

SseEmitter

SseEmitter是Spring框架中的一个类,专门用于Java。SSE代表服务器发送事件,是一种使服务器能够通过HTTP向Web客户端推送数据更新的技术。SseEmitter是在Spring应用程序中实现SSE服务器支持的便捷方式。
使用SseEmitter,您可以在Spring应用程序中创建一个端点,客户端可以连接到该端点,服务器可以通过此连接向客户端推送事件。这对于实时更新非常有用,例如显示实时通知、进度更新或流式传输数据。

实现:

  • OpenAI支持Stream流格式接收

在这里插入图片描述

接口连续的数据读取

官网示例

在这里插入图片描述

{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo-0125", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}

{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo-0125", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
...
{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo-0125", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
demo
java">  private static final String API_KEY = "********************";
  private static final Pattern contentPattern = Pattern.compile("\"content\":\"(.*?)\"}");
  private static final String MODEL_ENGINE = "gpt-3.5-turbo";
 public static void test() throws InterruptedException, IOException {
   		 //params 的入参封装 这里省略 参考上面图片 或去官网 需要stream形式请求
    	 HttpRequest httpRequest = HttpRequest.post("https://api.openai.com/v1/chat/completions")
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + API_KEY)
                .body(JSONUtil.toJsonStr(params));

        //TODO 代理 到shadowsocks 
        httpRequest.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890)));

        HttpResponse execute = httpRequest.execute();
        InputStream inputStream = execute.bodyStream();

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            if (StringUtils.hasLength(line)) {
                System.out.println(line);
                Matcher matcher = contentPattern.matcher(line);
                if (matcher.find()) {
                    String content = matcher.group(1);
                    System.out.println(content);
                }
            }
        }
}

SSE_56">SSE发送

demo

ChatController

java"> @Autowired
 private ChatService chatService;
 @GetMapping("/test")
 public SseEmitter test(String question) {
       SseEmitter sseEmitter = new SseEmitter();
       chatService.question(question, sseEmitter);
       return sseEmitter;
   }

ChatService

java">  private static final String API_KEY = "********************";
  private static final Pattern contentPattern = Pattern.compile("\"content\":\"(.*?)\"}");
 	@Async
    public void question(String question, SseEmitter sseEmitter) {
        try {
            // 构建请求参数
            String params = "{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"user\",\"content\":\"" + question + "\"}],\"stream\":true}";

            // 发起 HTTP 请求
            HttpRequest httpRequest = HttpRequest.post("https://api.openai.com/v1/chat/completions")
                    .header("Content-Type", "application/json")
                    .header("Authorization", "Bearer " + API_KEY)
                    .body(JSONUtil.toJsonStr(params))
                    .setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890)));

            // 执行 HTTP 请求
            HttpResponse execute = httpRequest.execute();

            // 处理响应流
            try (InputStream inputStream = execute.bodyStream();
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {

                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    if (StringUtils.hasLength(line)) {
                        // 输出响应内容
                        System.out.println(line);

                        // 提取内容
                        Matcher matcher = contentPattern.matcher(line);
                        if (matcher.find()) {
                            String content = matcher.group(1);
                            System.out.println(content);

                            // 发送 SSE 事件 (模拟延迟)
                            Thread.sleep(1000);
                            sseEmitter.send(SseEmitter.event().name("answer").data("{" + content + "}"));
                        }
                    }
                }
            }
        } catch (IOException | InterruptedException e) {
            // 异常处理
            throw new RuntimeException(e);
        } finally {
            // 完成 SSE 连接
            sseEmitter.complete();
        }
    }

测试

在这里插入图片描述


http://www.niftyadmin.cn/n/5437665.html

相关文章

【R1芯片:Apple Vision Pro中最神秘的角落】

没错,看到标题各位一定已经知道了,本篇文章我们接着来聊一聊Apple Vision Pro。Apple Vision Pro在北美正式发售至今已经过了一个月多了,笔者也是写了数篇文章来讲解了这款Apple全新的空间计算设备,那为什么今天还来探讨Apple Vis…

美食制作手记

美食制作手记 文章目录 美食制作手记1. 电饭煲焖饭系列2. 电饭煲糕点 1. 电饭煲焖饭系列 一周五款不重样的电饭煲焖饭!妈妈再也不用担心我没好好吃饭了~ 本系列先预炒,然后倒入生米中一锅出。 老干妈香菇腊肠焖饭: 加工:4根腊肠切…

20240318uniapp怎么引用组件

在script中增加 import index from "/pages/index/index.vue" 把index直接整个作为一个组件引入 然后注册组件 在export default中增加 components: {index:index }, 注册了index组件&#xff0c;内容为import的index 然后就可以在template里使用 <index&…

mybatis拦截器打印sql日志

前言 利用mybatis拦截器打印输出sql 操作 编写拦截器 package com.it2.excel01.interceptor;import java.text.DateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Properties;import lombok.extern.slf4j.Slf4j; impo…

2024年旅游经济与文化传播国际会议(ICTECC 2024)

2024年旅游经济与文化传播国际会议&#xff08;ICTECC 2024&#xff09; 2024 International Conference on Tourism Economy and Cultural Communication 会议简介&#xff1a; 旅游经济与文化传播之间存在密切的关系。旅游经济是以旅游活动为依托&#xff0c;通过旅游资源…

如何在点击每一个元素的时候 在当前元素下面追加一个表格进行展示

要在点击每一个元素时&#xff0c;在当前元素下方追加一个表格进行展示&#xff0c;你可以在每个列表项内部添加一个表格元素&#xff0c;并使用条件渲染来控制是否展示该表格。 以下是一个示例代码&#xff0c;展示了如何在点击每个元素时&#xff0c;在当前元素下方追加一个…

MFC中CString的用法及使用示例

CString 是 Microsoft Foundation Classes (MFC) 库中的一个类&#xff0c;用于处理 C 风格的字符串。它提供了很多有用的方法和函数&#xff0c;使得字符串的操作变得更加简单和安全。下面是一些 CString 的基本用法和使用示例&#xff1a; 1. 包含头文件 首先&#xff0c;你…

计算机网络 谢希仁(001-2)

计算机网络-方老师 总时长 24:45:00 共50个视频&#xff0c;6个模块 此文章包含1.5到1.7的内容 1.5计算机网络类别 连通 共享 分类方法 广域网是边缘部分和核心部分的核心部分 以前是拨号连接 现在是光纤 总线型 星型 环形网 1.6计算机网络的性能 带上单位之后就不是…