我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Flutter+SpringBoot实现ChatGPT流实输出

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Flutter+SpringBoot实现ChatGPT流实输出

Flutter+SpringBoot实现ChatGPT流式输出、上下文了连续对话

最终实现Flutter的流式输出+上下文连续对话。
在这里插入图片描述

这里就是提供一个简单版的工具类和使用案例,此处页面仅参考。

服务端

这里直接封装提供工具类,修改自己的apiKey即可使用,支持连续对话

工具类及使用

http依赖这里使用okHttp

    <dependency>      <groupId>com.squareup.okhttp3groupId>      <artifactId>okhttpartifactId>      <version>4.9.3version>    dependency>
import com.alibaba.fastjson2.JSON;import com.squareup.okhttp.Call;import com.squareup.okhttp.MediaType;import com.squareup.okhttp.OkHttpClient;import com.squareup.okhttp.Request;import com.squareup.okhttp.RequestBody;import com.squareup.okhttp.Response;import com.squareup.okhttp.ResponseBody;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import vip.ailtw.common.utils.StringUtil;import javax.annotation.PostConstruct;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.Serializable;import java.util.List;import java.util.concurrent.TimeUnit;import java.util.function.Consumer;import java.util.regex.Matcher;import java.util.regex.Pattern;@Slf4j@Componentpublic class ChatGptStreamUtil {        private final String apiKey = "xxxxxxxxxxxxxx";    public final String gptCompletionsUrl = "https://api.openai.com/v1/chat/completions";    private static final OkHttpClient client = new OkHttpClient();    private static MediaType mediaType;    private static Request.Builder requestBuilder;    public final static Pattern contentPattern = Pattern.compile("\"content\":\"(.*?)\"}");        public final static String EVENT_DATA = "d";        public final static String EVENT_ERROR = "e";        public final static String END = "<>";    @PostConstruct    public void init() {        client.setConnectTimeout(60, TimeUnit.SECONDS);        client.setReadTimeout(60, TimeUnit.SECONDS);        mediaType = MediaType.parse("application/json; charset=utf-8");        requestBuilder = new Request.Builder()                .url(gptCompletionsUrl)                .header("Content-Type", "application/json")                .header("Authorization", "Bearer " + apiKey);    }        public GptChatResultDTO chatStream(List<ChatGptDTO> talkList, Consumer<String> callable) throws Exception {        long start = System.currentTimeMillis();        StringBuilder resp = new StringBuilder();        Response response = chatStream(talkList);        //解析对话内容        try (ResponseBody responseBody = response.body();             InputStream inputStream = responseBody.byteStream();             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {            String line;            while ((line = bufferedReader.readLine()) != null) {                if (!StringUtils.hasLength(line)) {                    continue;                }                Matcher matcher = contentPattern.matcher(line);                if (matcher.find()) {                    String content = matcher.group(1);                    resp.append(content);                    callable.accept(content);                }            }        }        int wordSize = 0;        for (ChatGptDTO dto : talkList) {            String content = dto.getContent();            wordSize += content.toCharArray().length;        }        wordSize += resp.toString().toCharArray().length;        long end = System.currentTimeMillis();        return GptChatResultDTO.builder().resContent(resp.toString()).time(end - start).wordSize(wordSize).build();    }        private Response chatStream(List<ChatGptDTO> talkList) throws Exception {        ChatStreamDTO chatStreamDTO = new ChatStreamDTO(talkList);        RequestBody bodyOk = RequestBody.create(mediaType, chatStreamDTO.toString());        Request requestOk = requestBuilder.post(bodyOk).build();        Call call = client.newCall(requestOk);        Response response;        try {            response = call.execute();        } catch (IOException e) {            throw new IOException("请求时IO异常: " + e.getMessage());        }        if (response.isSuccessful()) {            return response;        }        try (ResponseBody body = response.body()) {            if (429 == response.code()) {                String msg = "Open Api key 已过期,msg: " + body.string();                log.error(msg);            }            throw new RuntimeException("chat api 请求异常, code: " + response.code() + "body: " + body.string());        }    }    private boolean sendToClient(String event, String data, SseEmitter emitter) {        try {            emitter.send(SseEmitter.event().name(event).data("{" + data + "}"));            return true;        } catch (IOException e) {            log.error("向客户端发送消息时出现异常", e);        }        return false;    }        public boolean sendData(String data, SseEmitter emitter) {        if (StringUtil.isBlank(data)) {            return true;        }        return sendToClient(EVENT_DATA, data, emitter);    }        public void sendEnd(SseEmitter emitter) {        try {            sendToClient(EVENT_DATA, END, emitter);        } finally {            emitter.complete();        }    }        public void sendError(SseEmitter emitter) {        try {            sendToClient(EVENT_ERROR, "我累垮了", emitter);        } finally {            emitter.complete();        }    }        @Data    @NoArgsConstructor    @AllArgsConstructor    @Builder    public static class GptChatResultDTO implements Serializable {                private String resContent;                private int wordSize;                private long time;    }        @Data    @Builder    @NoArgsConstructor    @AllArgsConstructor    public static class ChatGptDTO implements Serializable {                private String content;                private String role;    }        @Getter    public static enum GptRoleEnum {        USER_ROLE("user", "用户"),        GPT_ROLE("assistant", "ChatGPT本身"),                SYSTEM_ROLE("system", "对话设定"),        ;        private final String value;        private final String desc;        GptRoleEnum(String value, String desc) {            this.value = value;            this.desc = desc;        }    }        @Data    public static class ChatStreamDTO {        private static final String model = "gpt-3.5-turbo";        private static final boolean stream = true;        private List<ChatGptDTO> messages;        public ChatStreamDTO(List<ChatGptDTO> messages) {            this.messages = messages;        }        @Override        public String toString() {            return "{\"model\":\"" + model + "\"," +                    "\"messages\":" + JSON.toJSONString(messages) + "," +                    "\"stream\":" + stream + "}";        }    }}

使用案例:

    public static void main(String[] args) throws Exception {        ChatGptStreamUtil chatGptStreamUtil = new ChatGptStreamUtil();        chatGptStreamUtil.init();        //构建一个上下文对话情景        List<ChatGptDTO> talkList = new ArrayList<>();        //设定gpt        talkList.add(ChatGptDTO.builder().content("你是chatgpt助手,能过帮助我查阅资料,编写教学报告。").role(GptRoleEnum.GPT_ROLE.getValue()).build());        //开始提问        talkList.add(ChatGptDTO.builder().content("请帮我写一篇小学数学加法运算教案").role(GptRoleEnum.USER_ROLE.getValue()).build());        chatGptStreamUtil.chatStream(talkList, (respContent) -> {            //这里是gpt每次流式返回的内容            System.out.println("gpt返回:" + respContent);        });    }

SpringBoot接口

基于SpringBoot工程,提供接口,供Flutter端使用。

通过上面的工具类的使用,可以知道gpt返回给我们的内容是一段一段的,因此如果我们服务端也要提供类似的效果,提供两个思路和实现:

  • WebSocket,服务端接收gpt返回的内容时推送内容给flutter
  • 使用Http长链接,也就是 SseEmitter,这里也是采用这种方式。

代码:

@RestController@RequestMapping("/chat")@Slf4jpublic class ChatController {    @Autowired    private ChatGptStreamUtil chatGptStreamUtil;      @PostMapping(value = "/chatStream")    @ApiOperation("流式对话")    public SseEmitter chatStream() {        SseEmitter emitter = new SseEmitter(80000L);              //构建一个上下文对话情景        List<ChatGptDTO> talkList = new ArrayList<>();        //设定gpt        talkList.add(ChatGptDTO.builder().content("你是chatgpt助手,能过帮助我查阅资料,编写教学报告。").role(GptRoleEnum.GPT_ROLE.getValue()).build());        //开始提问        talkList.add(ChatGptDTO.builder().content("请帮我写一篇小学数学加法运算教案").role(GptRoleEnum.USER_ROLE.getValue()).build());        GptChatResultDTO gptChatResultDTO = chatGptStreamUtil.chatStream(talkList, (content) -> {          //这里服务端接收到消息就发送给Flutter               chatGptStreamUtil.sendData(content, emitter);            });        return emitter;    }}

Flutter端

这里使用dio作为网络请求的工具

依赖

dio: ^5.2.1+1

工具类

import 'dart:async';import 'dart:convert';import 'package:dio/dio.dart';import 'package:flutter/cupertino.dart';import 'package:flutter/foundation.dart';import 'package:get/get.dart' hide Response;///http工具类class HttpUtil {  Dio? client;  static HttpUtil of() {    return HttpUtil.init();  }  //初始化http工具  HttpUtil.init() {    if (client == null) {      var options = BaseOptions(          baseUrl: Config.baseUrl,          connectTimeout: const Duration(seconds: 100),          receiveTimeout: const Duration(seconds: 100));      client = Dio(options);      // 请求与响应拦截器/异常拦截器      client?.interceptors.add(OnReqResInterceptors());    }  }  Future<Stream<String>?> postStream(String path,      [Map<String, dynamic>? params]) async {    Response<ResponseBody> rs =    await Dio().post<ResponseBody>(Config.baseUrl + path,        options: Options(headers: {          "Accept": "text/event-stream",          "Cache-Control": "no-cache"        }, responseType: ResponseType.stream),        data: params     );    StreamTransformer<Uint8List, List<int>> unit8Transformer =    StreamTransformer.fromHandlers(      handleData: (data, sink) {        sink.add(List<int>.from(data));      },    );    var resp = rs.data?.stream        .transform(unit8Transformer)        .transform(const Utf8Decoder())        .transform(const LineSplitter());    return resp;  }/// Dio 请求与响应拦截器class OnReqResInterceptors extends InterceptorsWrapper {    Future<void> onRequest(      RequestOptions options, RequestInterceptorHandler handler) async {    //统一添加token    var headers = options.headers;    headers['Authorization'] = '请求头token';    return super.onRequest(options, handler);  }    void onError(DioError err, ErrorInterceptorHandler handler) {    if (err.type == DioErrorType.unknown) {      // 网络不可用,请稍后再试    }    return super.onError(err, handler);  }    void onResponse(      Response<dynamic> response, ResponseInterceptorHandler handler) {    Response res = response;    return super.onResponse(res, handler);  }}

使用

  //构建文章、流式对话  chatStream() async {    final stream = await HttpUtil.of().postStream("/api/chat/chatStream");    String respContent = "";    stream?.listen((content) {      debugPrint(content);      if (content != '' && content.contains("data:")) {        //解析数据        var start = content.indexOf("{") + 1;        var end = content.indexOf("}");        var substring = content.substring(start, end);        content = substring;        respContent += content;        print("返回的内容:$content");      }    });  }

来源地址:https://blog.csdn.net/wq2323/article/details/133523990

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Flutter+SpringBoot实现ChatGPT流实输出

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java调用ChatGPT(基于SpringBoot和Vue)实现可连续对话和流式输出的ChatGPTAPI

这篇文章主要介绍了Java调用ChatGPT(基于SpringBoot和Vue),实现可连续对话和流式输出的ChatGPTAPI(可自定义实现AI助手),文中代码示例介绍的非常详细,感兴趣的朋友可以参考下
2023-05-18

Java调用ChatGPT(基于SpringBoot和Vue)实现连续对话、流式输出和自定义baseUrl

目录 版本更新说明1. 配置阶段1.1 依赖引入1.2 配置application.yml文件1.3 注解添加 2. 使用2.1 生成回答2.1.1 测试 2.2 生成图片2.2.1 测试 2.3 下载图片2.3.1
2023-08-16

Java怎么实现带缓冲的输入输出流

本篇内容主要讲解“Java怎么实现带缓冲的输入输出流”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java怎么实现带缓冲的输入输出流”吧!缓冲是 I/O 的一种性能优化。缓冲流为 I/O 流增加
2023-06-29

微信小程序怎么实现类似ChatGPT的流式传输

这篇文章主要介绍“微信小程序怎么实现类似ChatGPT的流式传输”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“微信小程序怎么实现类似ChatGPT的流式传输”文章能帮助大家解决问题。小程序上实现流失
2023-07-05

.NET实现ChatGPT的Stream传输的过程

本文介绍了在.NET中实现ChatGPT流式传输的详细过程。通过安装OpenAI客户端库并配置流式传输设置,开发人员可以实时与ChatGPT交互,获得即时响应。文中详细描述了建立连接、发送提示、读取响应等步骤,并提供了代码示例。此外,文中还给出了提示,建议开发者验证API密钥、调整设置、使用异步模式和适当的数据处理技术。
.NET实现ChatGPT的Stream传输的过程
2024-04-02

实时获取Python的print输出流

我的应用场景是:使用shell执行python文件,并且通过调用的返回值获取python的标准输出流。 shell程序如下:cmd='python '$1' '$2' '$3' '$5' '$4RESULT=eval $cmdecho $R
2023-01-31

python实现输出日历

终于还有一个星期就放暑假了,可以抽出时间来学点新的东西,想想半年多没登CSDN差点把密码忘了我也是醉了。废话不多说,正文开始以下是使用几个简单的函数实现给定指定年月实现当月日历输出的程序,大部分内容引用自网易云课堂哈工大开设的Python程
2023-01-31

Java SpringBoot怎么集成ChatGPT实现AI聊天

这篇文章主要介绍“Java SpringBoot怎么集成ChatGPT实现AI聊天”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java SpringBoot怎么集成ChatGPT实现AI聊天”文章
2023-07-05

flutter底部弹出BottomSheet怎么实现

本篇内容介绍了“flutter底部弹出BottomSheet怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!效果图:ModalBott
2023-06-29

php如何实现输出

这篇文章给大家分享的是有关php如何实现输出的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。php输出方法:1、使用echo语句输出,例“echo hi!”;2、使用print语句输出;3、使用printf()函数
2023-06-14

Flutter StaggeredGridView如何实现瀑布流效果

这篇文章将为大家详细讲解有关Flutter StaggeredGridView如何实现瀑布流效果,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体内容如下在根目录pubspec.yaml文件中添加依赖de
2023-06-29

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录