当前位置: 首页 > 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模型评估实战:从原理到实践,用Evaliphy简化评测全流程

1. 项目概述&#xff1a;当AI测试遇上“简化”难题最近和几个做AI应用开发的朋友聊天&#xff0c;大家不约而同地提到了同一个痛点&#xff1a;模型效果评估太折腾了。这让我想起自己去年折腾一个文本分类项目时的经历——为了评估模型在几个不同测试集上的表现&#xff0c;我写…...

Hermes Agent 可视化监控与文档生成工具 hermes-dashboard 详解

1. 项目概述与核心价值如果你正在使用 Hermes Agent 进行 AI 智能体开发&#xff0c;或者对 Agent 的内部运行状态感到好奇&#xff0c;那么你很可能需要一个“上帝视角”。hermes-dashboard正是这样一个工具&#xff0c;它为你提供了一个实时的监控仪表盘和一个自动生成的、可…...

使用Taotoken后如何清晰观测API用量与成本变化

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 使用Taotoken后如何清晰观测API用量与成本变化 对于团队管理者或开发者而言&#xff0c;将大模型能力集成到产品中后&#xff0c;资…...

在Hermes Agent项目中集成Taotoken实现自定义模型供应商的切换

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在Hermes Agent项目中集成Taotoken实现自定义模型供应商的切换 1. 场景与目标 Hermes Agent 是一个功能强大的智能体开发框架&…...

如何快速破解Cursor Pro限制:一键激活AI编程助手的完整指南

如何快速破解Cursor Pro限制&#xff1a;一键激活AI编程助手的完整指南 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached you…...

如何给非技术背景的老板汇报技术问题?一个框架搞定

一、为什么你的技术汇报&#xff0c;老板总是听不进去&#xff1f;作为软件测试工程师&#xff0c;你可能经历过这样的场景&#xff1a;你花了一整个周末整理出一份详尽的测试报告&#xff0c;里面涵盖了用例执行率、缺陷分布、严重等级、性能拐点等专业数据。但当你信心满满地…...

在多模型AI客服场景下利用Taotoken实现成本与效果的平衡

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在多模型AI客服场景下利用Taotoken实现成本与效果的平衡 应用场景类&#xff0c;设想一个在线客服系统需要集成对话AI的场景&#…...

从视频到文字:当B站知识需要被存档时,我们如何优雅地捕获声音

从视频到文字&#xff1a;当B站知识需要被存档时&#xff0c;我们如何优雅地捕获声音 【免费下载链接】bili2text Bilibili视频转文字&#xff0c;一步到位&#xff0c;输入链接即可使用 项目地址: https://gitcode.com/gh_mirrors/bi/bili2text 你是否曾有过这样的经历…...

数字时代的计划性抹杀:从强制升级到生态锁定的技术围剿

1. 数字时代的“计划性报废”&#xff1a;从凯迪拉克到小电驴的隐喻 前几天&#xff0c;我在网上申请一张信用卡&#xff0c;过程堪称一场荒诞剧。银行明明通过邮件联系我&#xff0c;也知道我的账号密码&#xff0c;甚至在我通过了“我不是机器人”的图片验证后&#xff0c;却…...

Taotoken API密钥的精细权限管理与操作审计日志在安全运维中的作用

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken API密钥的精细权限管理与操作审计日志在安全运维中的作用 对于负责技术基础设施安全与合规的团队而言&#xff0c;引入新…...