AIGC: 关于ChatGPT中基于API实现一个StreamClient流式客户端

2023-12-05

Java版GPT的StreamClient

  • 可作为其他编程语言的参考
  • 注意: 下面包名中的 xxx 可以换成自己的
  • 代码基于java,来源于网络,可修改成其他编程语言实现
  • 参考前文: https://blog.csdn.net/Tyro_java/article/details/134748994

1 )核心代码结构设计

  • src
    • main
      • java
        • com.xxx.gpt.client
          • listener
            • AbstractStreamListener.java
            • ConsoleStreamListener.java
          • ChatGPTStreamClient.java
    • test
      • java
        • com.xxx.gpt.client.test
          • StreamClientTest.java

2 )相关程序如下

  • 前文,通过我们开发的Client能够正常的和 Open AI 进行交互,能够去调用GPT的API
  • 通过API将我们的 message 请求发送给GPT并且正常的接收到了GPT对我们的返回
  • 在前面我们去浏览 GPT 它的API的时候,我们发现它是支持流式访问的
  • 我们可以开发一个Stream的Client,能够支持流式的接收GPT的响应
  • 流式的Client在很多场景下也是非常有必要的
  • 首先需要去先创建一个listener,去流式的接收GPT的返回, 我们实现一个 AbstractStreamListener 类 和 ConsoleStreamListener 类
  • 需要继承于 EventSourceListener, 内部添加几个方法
    • onOpen
    • onEvent, 可以获取到流失的输入,这个是重点
    • onClosed
    • onFailure

AbstractStreamListener.java

package com.xxx.gpt.client.listener;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.xxx.gpt.client.entity.ChatChoice;
import com.xxx.gpt.client.entity.ChatCompletionResponse;
import com.xxx.gpt.client.entity.Message;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;

import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

@Slf4j
public abstract class AbstractStreamListener extends EventSourceListener {
    protected String lastMessage = "";

    /**
     * Called when all new message are received.
     *
     * @param message the new message
     */
    @Setter
    @Getter
    protected Consumer<String> onComplate = s -> {};

    /**
     * Called when a new message is received.
     * 收到消息 单个字
     *
     * @param message the new message
     */
    public abstract void onMsg(String message);
    /**
     * Called when an error occurs.
     * 出错时调用
     *
     * @param throwable the throwable that caused the error
     * @param response  the response associated with the error, if any
     */
    public abstract void onError(Throwable throwable, String response);

    @Override
    public void onOpen(EventSource eventSource, Response response) {
        // do nothing
    }

    @Override
    public void onClosed(EventSource eventSource) {
        // do nothing
    }

    @Override
    public void onEvent(EventSource eventSource, String id, String type, String data) {
        if (data.equals("[DONE]")) {
            onComplate.accept(lastMessage);
            return;
        }
        // 将数据反序列化为 GPT的 response
        ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
        // 获取GPT的返回,读取Json
        List<ChatChoice> choices = response.getChoices();
        // 为空则 return
        if (choices == null || choices.isEmpty()) {
            return;
        }
        // 获取流式信息
        Message delta = choices.get(0).getDelta();
        String text = delta.getContent();
        if (text != null) {
            lastMessage += text;
            onMsg(text);
        }
    }

    @SneakyThrows
    @Override
    public void onFailure(EventSource eventSource, Throwable throwable, Response response) {
        try {
            log.error("Stream connection error: {}", throwable);
            String responseText = "";
            if (Objects.nonNull(response)) {
                responseText = response.body().string();
            }
            log.error("response:{}", responseText);
            String forbiddenText = "Your access was terminated due to violation of our policies";
            if (StrUtil.contains(responseText, forbiddenText)) {
                log.error("Chat session has been terminated due to policy violation");
                log.error("检测到号被封了");
            }
            String overloadedText = "That model is currently overloaded with other requests.";
            if (StrUtil.contains(responseText, overloadedText)) {
                log.error("检测到官方超载了,赶紧优化你的代码,做重试吧");
            }
            this.onError(throwable, responseText);
        } catch (Exception e) {
            log.warn("onFailure error:{}", e);
            // do nothing
        } finally {
            eventSource.cancel();
        }
    }
}

ConsoleStreamListener.java

package com.xxx.gpt.client.listener;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ConsoleStreamListener extends AbstractStreamListener {
    @Override
    public void onMsg(String message) {
        System.out.print(message);
    }
    @Override
    public void onError(Throwable throwable, String response) {}
}
  • 再创建一个 ChatGPTStreamClient 类
    • 添加如下相关属性
    • 添加 init 方法
    • 完成 streamChatCompletion 方法

ChatGPTStreamClient.java

package com.xxx.gpt.client;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.ContentType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.gpt.client.entity.ChatCompletion;
import com.xxx.gpt.client.entity.Message;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import okhttp3.sse.EventSources;

import java.net.Proxy;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatGPTStreamClient {
    private String apiKey;
    private List<String> apiKeyList;
    private OkHttpClient okHttpClient;
    /**
     * 连接超时
     */
    @Builder.Default
    private long timeout = 90;

    /**
     * 网络代理
     */
    @Builder.Default
    private Proxy proxy = Proxy.NO_PROXY;
    /**
     * 反向代理
     */
    @Builder.Default
    private String apiHost = ChatApi.CHAT_GPT_API_HOST;

    /**
     * 初始化
     */
    public ChatGPTStreamClient init() {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.connectTimeout(timeout, TimeUnit.SECONDS);
        client.writeTimeout(timeout, TimeUnit.SECONDS);
        client.readTimeout(timeout, TimeUnit.SECONDS);
        if (Objects.nonNull(proxy)) {
            client.proxy(proxy);
        }
        okHttpClient = client.build();
        return this;
    }

    /**
     * 流式输出
     */
    public void streamChatCompletion(ChatCompletion chatCompletion,
                                     EventSourceListener eventSourceListener) {
        chatCompletion.setStream(true);
        try {
            EventSource.Factory factory = EventSources.createFactory(okHttpClient);
            ObjectMapper mapper = new ObjectMapper();
            String requestBody = mapper.writeValueAsString(chatCompletion);
            String key = apiKey;
            if (apiKeyList != null && !apiKeyList.isEmpty()) {
                key = RandomUtil.randomEle(apiKeyList);
            }
            Request request = new Request.Builder()
                    .url(apiHost + "v1/chat/completions")
                    .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()),
                            requestBody))
                    .header("Authorization", "Bearer " + key)
                    .build();
            factory.newEventSource(request, eventSourceListener);
        } catch (Exception e) {
            log.error("请求出错:{}", e);
        }
    }

    /**
     * 流式输出
     */
    public void streamChatCompletion(List<Message> messages,
                                     EventSourceListener eventSourceListener) {
        ChatCompletion chatCompletion = ChatCompletion.builder()
                .messages(messages)
                .stream(true)
                .build();
        streamChatCompletion(chatCompletion, eventSourceListener);
    }
}

再添加一个测试方法 StreamClientTest.java

package com.xxx.gpt.client.test;

import com.xxx.gpt.client.ChatGPTStreamClient;
import com.xxx.gpt.client.entity.ChatCompletion;
import com.xxx.gpt.client.entity.Message;
import com.xxx.gpt.client.listener.ConsoleStreamListener;
import com.xxx.gpt.client.util.Proxys;
import org.junit.Before;
import org.junit.Test;

import java.net.Proxy;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;

public class StreamClientTest {
    private ChatGPTStreamClient chatGPTStreamClient;

    @Before
    public void before() {
        Proxy proxy = Proxys.http("127.0.0.1", 7890);
        chatGPTStreamClient = ChatGPTStreamClient.builder()
                .apiKey("sk-6kchadsfsfkc3aIs66ct") // 填入自己的 key
                .proxy(proxy)
                .timeout(600)
                .apiHost("https://api.openai.com/")
                .build()
                .init();
    }
    @Test
    public void chatCompletions() {
        ConsoleStreamListener listener = new ConsoleStreamListener();
        Message message = Message.of("写一段七言绝句诗");
        ChatCompletion chatCompletion = ChatCompletion.builder()
                .messages(Arrays.asList(message))
                .build();
        chatGPTStreamClient.streamChatCompletion(chatCompletion, listener);
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
  • 这样,程序基本已经完成了
  • 这里构建了一个流式访问的参数,然后去调用GPT的API实现了流式的输出
  • 可参考以上 Java 版实现, 去实现其他语言版本的 StreamClient
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AIGC: 关于ChatGPT中基于API实现一个StreamClient流式客户端 的相关文章

随机推荐

  • 数字化转型的引擎:企业与消费者同步变革

    数字化转型的引擎 企业与消费者同步变革 随着科技的飞速发展 数字化转型已成为企业生存和发展的必然选择 在这个过程中 企业和消费者都面临着巨大的变革和挑战 本文将探讨数字化转型的推动力 以及企业和消费者如何应对这些变革 一 数字化转型的推动力
  • VLAN实验

    题目要求 1 PC1和PC3所在接口为Access接口 2 PC2 4 5 6处于同一网段 其中PC2可以访问PC4 5 6 PC4可以访问PC5 但不能访问PC6 PC5不能访问PC6 3 PC1 3与PC2 4 5 6不在同一网段 4
  • 编译tzdata提示内存越界 malloc(): memory corruption /bin/sh: line 1: 32317 Aborted

    编译tzdata时 遇到如下所示的错误提示 awk v outfile main zi f ziguard awk africa antarctica asia australasia europe northamerica southam
  • 图片转表格软件有哪些?进来马上给你揭晓

    在整理资料的时候 你或多或少都会遇到一些图片格式的表格 需要你手动输入文字信息进行整理 然而 如果数据多的话 很容易出错 并且很浪费时间和精力 为了解决这个问题 你可以借助一些工具 来帮助你更高效地处理这种情况 这些工具通常可以将图片中的表
  • Ubuntu20.04使用SVN(Rabbitvcs)

    原文 https blog csdn net u014552102 article details 129914787 1 安装Rabbitvcs sudo apt get install rabbitvcs nautilus sudo r
  • WebGL笔记:js中矩阵库的使用

    矩阵库 手写矩阵 其实很麻烦 可以将其模块化 市面上已经有许多开源的矩阵库 比如 WebGL 编程指南 里的 cuon matrix js three js 的 Matrix3 和 Matrix4 对象 three js的 Matrix4
  • WebGL笔记:矩阵缩放的数学原理和实现

    矩阵缩放的数学原理 和平移一样 以同样的原理 也可以理解缩放矩阵 让向量OA基于原点进行缩放 x方向上缩放 sx y方向上缩放 sy z方向上缩放 sz 最终得到向量OB 矩阵缩放的应用 比如我要让顶点在x轴向缩放2 y轴向缩放3 轴向缩放
  • VBA字典与数组第八讲:数组及数组公式结果的制约性和集合性

    VBA数组与字典方案 教程 10144533 是我推出的第三套教程 目前已经是第二版修订了 这套教程定位于中级 字典是VBA的精华 我要求学员必学 7 1 3 9教程和手册掌握后 可以解决大多数工作中遇到的实际问题 这套字典教程共两册 一共
  • linux下gdb的使用以及dump转存文件的生成使用

    1 gdb Linux 调试器 gdb的使用 Linux之gdb的使用 gdb调试工具 如何在多线程 多进程以及正在运行的程序下调试 2 dump文件 c linux dump定位错误 Linux下更改 coredump文件生成路径 lin
  • Node.js爬虫实战:百度图片爬取

    说在前面 网络爬虫是一种自动化工具 能够模拟人类在互联网上浏览和提取信息的行为 它的应用范围广泛 包括数据采集 信息监控 搜索引擎优化等方面 而在数据抓取和处理中 获取图片资源往往是一个常见的需求 本文将介绍如何使用Node js和相关库构
  • 抖音商品详情接口在电商行业中的重要性及实时数据获取实现

    一 引言 抖音作为当下最热门的短视频平台之一 拥有庞大的用户群体和活跃度 为电商行业带来了巨大的商业机会 抖音商品详情接口作为连接抖音平台和电商系统的关键纽带 具有重要的作用 本文将深入探讨抖音商品详情接口在电商行业中的重要性 并介绍如何通
  • 提取音频哪个软件好?揭秘市面上的热门选择

    就像许多人都喜欢在视频中加入动听的音乐来增强情感氛围一样 你有没有想过如果能够将那些打动你的音乐片段单独提取出来 作为自己的手机铃声 那该有多好呢 是的 某个视频中听到了一段难以忘怀的音乐 你可能会迫不及待地想将其设置为自己的专属手机铃声
  • 阿里云服务器有WordPress还可以再安装宝塔面板吗?

    不推荐 宝塔面板要求必须是纯净的操作系统环境安装宝塔 否则可能会有问题的 所以最好是先安装宝塔面板 再去安装wordpress 原文地址 阿里云服务器 WordPress 还可以再安装宝塔面板吗 轻量云Cloud WordPress一款广泛
  • C++简易计数器

    Created by Carlgood Note This program is written in version DEV C 5 11 include
  • 腾讯云用centos还是ubuntu系统好?

    腾讯云服务器提供了多种操作系统选择 包括 CentOS Ubuntu Windows Server 等 用户可以根据自己的需求和习惯选择适合的操作系统 通常比较推荐安装centos 7 x版本的系统 但在 CentOS 和 Ubuntu 之
  • HAL库STM32常用外设教程(二)—— GPIO输入\输出

    HAL库STM32常用外设教程 二 GPIO输入 输出 文章目录 HAL库STM32常用外设教程 二 GPIO输入 输出 前言 一 GPIO功能概述 二 GPIO的HAl库驱动 三 GPIO使用示例 1 示例功能 四 代码讲解 五 总结
  • VBA技术资料MF91:计算机自动执行VBA脚本代码

    我给VBA的定义 VBA是个人小型自动化处理的有效工具 利用好了 可以大大提高自己的工作效率 而且可以提高数据的准确度 我的教程一共九套 分为初级 中级 高级三大部分 是对VBA的系统讲解 从简单的入门 到数据库 到字典 到高级的网抓及类的
  • 计算机毕设项目 - HTML的健身房信息管理系统

    项目背景 随着科学技术的飞速发展 各行各业都在努力与现代先进技术接轨 通过科技手段提高自身的优势 对于健身房信息管理系统当然也不能排除在外 随着网络技术的不断成熟 带动了健身房信息管理系统 它彻底改变了过去传统的管理方式 不仅使服务管理难度
  • python爬虫概述及简单实践

    文章目录 一 先了解用户获取网络数据的方式 二 简单了解网页源代码的组成 1 web基本的编程语言 2 使用浏览器查看网页源代码 三 爬虫概述 1 认识爬虫 2 python爬虫 3 爬虫分类 4 爬虫应用 5 爬虫是一把双刃剑 6 pyt
  • AIGC: 关于ChatGPT中基于API实现一个StreamClient流式客户端

    Java版GPT的StreamClient 可作为其他编程语言的参考 注意 下面包名中的 xxx 可以换成自己的 代码基于java 来源于网络 可修改成其他编程语言实现 参考前文 https blog csdn net Tyro java