当前位置: 首页 > news >正文

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

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@Getterprotected 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);@Overridepublic void onOpen(EventSource eventSource, Response response) {// do nothing}@Overridepublic void onClosed(EventSource eventSource) {// do nothing}@Overridepublic void onEvent(EventSource eventSource, String id, String type, String data) {if (data.equals("[DONE]")) {onComplate.accept(lastMessage);return;}// 将数据反序列化为 GPT的 responseChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);// 获取GPT的返回,读取JsonList<ChatChoice> choices = response.getChoices();// 为空则 returnif (choices == null || choices.isEmpty()) {return;}// 获取流式信息Message delta = choices.get(0).getDelta();String text = delta.getContent();if (text != null) {lastMessage += text;onMsg(text);}}@SneakyThrows@Overridepublic 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 {@Overridepublic void onMsg(String message) {System.out.print(message);}@Overridepublic 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.Defaultprivate long timeout = 90;/*** 网络代理*/@Builder.Defaultprivate Proxy proxy = Proxy.NO_PROXY;/*** 反向代理*/@Builder.Defaultprivate 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;@Beforepublic 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();}@Testpublic 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

相关文章:

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

Java版GPT的StreamClient 可作为其他编程语言的参考注意: 下面包名中的 xxx 可以换成自己的代码基于java&#xff0c;来源于网络&#xff0c;可修改成其他编程语言实现参考前文: https://blog.csdn.net/Tyro_java/article/details/134748994 1 &#xff09;核心代码结构设计 …...

FutureTask

1. 作用 异步操作获取执行结果取消任务执行&#xff0c;判断是否取消执行判断任务执行是否完毕 2. demo public static void main(String[] args) throws Exception {Callable<String> callable () -> search();FutureTask<String> futureTasknew FutureTask&…...

【力扣热题100】207. 课程表 python 拓扑排序

【力扣热题100】207. 课程表 python 拓扑排序 写在最前面207. 课程表解决方案&#xff1a;判断是否可以完成所有课程的学习方法&#xff1a;拓扑排序实现步骤Python 实现性能分析结论 写在最前面 刷一道力扣热题100吧 难度中等 https://leetcode.cn/problems/course-schedule…...

【滑动窗口】LeetCode2953:统计完全子字符串

作者推荐 [二分查找]LeetCode2040:两个有序数组的第 K 小乘积 本题其它解法 【离散差分】LeetCode2953:统计完全子字符串 题目 给你一个字符串 word 和一个整数 k 。 如果 word 的一个子字符串 s 满足以下条件&#xff0c;我们称它是 完全字符串&#xff1a; s 中每个字符…...

base64转PDF

今天做皖事通的对接&#xff0c;下载电子证照后发现回传的是base64&#xff0c;调试确认是个麻烦事&#xff0c;网上搜了一下没有base64转PDF的在线预览功能&#xff0c;只能自己写个调试工具了&#xff0c;以下是通过纯JS方式写的代码&#xff0c;可直接拿去使用&#xff1a; …...

clip-path,css裁剪函数

https://www.cnblogs.com/dzyany/p/13985939.html clip-path - CSS&#xff1a;层叠样式表 | MDN 我们看下这个例子 polygon里有四个值分别代表这四个点相对于原图左上方的偏移量。 裁剪个五角星...

第二证券:食品饮料板块拉升,乳业股亮眼,西部牧业“20cm”涨停

证券时报网讯&#xff0c;食物饮料板块5日盘中拉升走高&#xff0c;乳业股体现活跃&#xff0c;到发稿&#xff0c;骑士乳业涨超27%&#xff0c;西部牧业“20cm”涨停&#xff0c;阳光乳业亦涨停。 其它个股方面&#xff0c;盖世食物涨超20%&#xff0c;润普食物涨超18%&#…...

React 好用的工具库

1、html-react-parser HTML 到 React 解析器&#xff0c;适用于服务器 &#xff08;Node.js&#xff09; 和客户端&#xff08;浏览器&#xff09;&#xff0c;适用于React节点修改过滤等需求 解析器将 HTML 字符串转换为一个或多个 React 元素。可以将一个元素替换为另一个元素…...

C++面试宝典第2题:逆序输出整数

题目 写一个方法&#xff0c;将一个整数逆序打印输出到控制台。注意&#xff1a;当输入的数字含有结尾的0时&#xff0c;输出不应带有前导的0。比如&#xff1a;123的逆序输出为321&#xff0c;8600的逆序输出为68&#xff0c;-609的逆序输出为-906。 解析 这道题本身并没有什么…...

Twincat功能块使用经验总结

控制全局变量&#xff1a; //轴控制指令 bi_Power: BOOL; //使能 bi_Reset: BOOL; //复位 bi_Stop: BOOL; //停止 bi_JogForward: BOOL; //正向点动 bi_JogBackwards: BOOL; //反向点动 bi_MoveAdditive: BOOL; //增量位…...

香港服务器时间不准,差8小时

解决方案1 1、timedatectl查看系统时间 2、查看系统时区 ls /usr/share/zoneinfo 3、删除当前系统所处时区 rm /etc/localtime 4、创建软链接&#xff0c;以替换当前的时区信息 ln -s /usr/share/zoneinfo/Universal /etc/localtime 解决方案2 手动设置硬件时钟 1、设置系…...

C++ 抽象类和接口 详解

目录 0 引言1 抽象类2 接口2.1 Java与C接口的区别 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;C专栏&#x1f4a5; 标题&#xff1a;C 抽象类和接口 详解❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01;&#x1f…...

【Linux】awk 使用

awk 输出 // 打印所有列 $ awk {print $0} file // 打印第一列 $ awk {print $1} file // 打印第一和第三列 $ awk {print $1, $3} file // 打印第三列和第一列&#xff0c;注意先后顺序 $ cat file | awk {print $3, $1} …...

LeetCode力扣每日一题(Java):9、回文数

一、题目 二、解题思路 1、我的思路 当x<0时&#xff0c;x一定不是回文数&#xff0c;直接返回false 当x>0且x<10时&#xff0c;x一定是回文数&#xff0c;直接返回true x>10时&#xff0c;先将x转为字符串。将数字转成字符串方法挺多的&#xff0c;以下是&…...

WPF前端实现人脸扫描动画效果

前言 本章实现的效果主要通过OpacityMask与LinearGradientBrush(径向渐变) 的组合应用来实现。最终实现效果如下: LinearGradientBrush线性渐变画刷 LinearGradientBrush其实很简单,我们只需要关注5个属性,使用这5个属性你就可以完成这个画刷几乎所有的变化。 属性介…...

更改AndroidStudio模拟器位置

C盘何等的珍贵&#xff0c;可是好多工具&#xff0c;软件非得默认安装在C盘。。导致C盘越来越紧张。。 在日常使用过程中&#xff0c;安装任何软件都会将其安装到非系统盘下&#xff0c;Android模拟器也不能例外。保护好C盘也是日常一个良好的习惯。 Android AVD默认路径&…...

Dash 协议介绍

<?xml version"1.0" encoding"utf-8"?> <MPD xmlns"urn:mpeg:dash:schema:mpd:2011" minBufferTime"PT1.5S" type"static" mediaPresentationDuration"PT0H1M0.3S" maxSegmentDuration"PT0H0M2.0…...

RabbitMQ的消息发送和接收机制

所有 MQ 产品从模型抽象上来说都是一样的过程&#xff1a; 消费者&#xff08;consumer&#xff09;订阅某个队列。生产者&#xff08;producer&#xff09;创建消息&#xff0c;然后发布到队列&#xff08;queue&#xff09;中&#xff0c;最后将消息发送到监听的消费者。 上…...

记录111

在两台 RHEL 8 服务器上搭建 PostgreSQL 和 pgpool-II 环境涉及到安装 PostgreSQL、配置流复制&#xff08;Streaming Replication&#xff09;以及安装和配置 pgpool-II。以下是详细的步骤&#xff1a; ### 准备工作 1. **获取服务器**&#xff1a;确保你有两台运行 RHEL 8 的…...

振动和震动的区别?

问题描述&#xff1a;振动和震动的区别&#xff1f; 问题解决&#xff1a; 震动&#xff08;Oscillation&#xff09;&#xff1a; 特点&#xff1a; 随机的、突发的、不经常的、无规律的运动。例子&#xff1a; 地壳震动、消息震动全国&#xff0c;强调的是运动的力度或幅度&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...