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

百度文心一言 java 支持流式输出,Springboot+ sse的demo

参考:GitHub - mmciel/wenxin-api-java: 百度文心一言Java库,支持问答和对话,支持流式输出和同步输出。提供SpringBoot调用样例。提供拓展能力。

1、依赖

<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.16.18</version>
</dependency>

2、配置apikey和secretkey

3、主要使用的接口

4、返回的json格式 

3、WenxinEventSourceListener  事件监听器

和其他的接口不一样 需要 CompletionsResponse.data  封装下 ,不然前端页面需要兼容非json的格式

@Slf4j
public class WenxinEventSourceListener extends EventSourceListener {private long tokens;private SseEmitter sseEmitter;public WenxinEventSourceListener(SseEmitter sseEmitter) {this.sseEmitter = sseEmitter;}@Overridepublic void onOpen(EventSource eventSource, Response response) {log.info("建立sse连接...");}@SneakyThrows@Override@JsonIgnoreProperties(ignoreUnknown = true)public void onEvent(EventSource eventSource, String id, String type, String data) {ChatResponse bean = JSONUtil.parseObj(data).toBean(ChatResponse.class);log.info("返回数据:{}", data);if (bean.getIs_end()) {log.info("返回数据结束了");sseEmitter.send(SseEmitter.event().id("[TOKENS]").data("<br/><br/>tokens:" + tokens()).reconnectTime(3000));sseEmitter.send(SseEmitter.event().id("[DONE]").data("[DONE]").reconnectTime(3000));// 传输完成后自动关闭ssesseEmitter.complete();return;}log.info("OpenAI返回数据:{}", data);tokens += 1;if (data.equals("[DONE]")) {log.info("OpenAI返回数据结束了");sseEmitter.send(SseEmitter.event().id("[TOKENS]").data("<br/><br/>tokens:" + tokens()).reconnectTime(3000));sseEmitter.send(SseEmitter.event().id("[DONE]").data("[DONE]").reconnectTime(3000));// 传输完成后自动关闭ssesseEmitter.complete();return;}CompletionsResponse completionResponse = new CompletionsResponse();CompletionsResponse.Data dataResult = new CompletionsResponse.Data();dataResult.setText(bean.getResult());completionResponse.setData(dataResult);try {sseEmitter.send(SseEmitter.event().id(bean.getId()).data(completionResponse.getData()).reconnectTime(3000));} catch (Exception e) {log.error("sse信息推送失败!");eventSource.cancel();e.printStackTrace();}}@Overridepublic void onClosed(EventSource eventSource) {log.info("关闭sse连接...");}@SneakyThrows@Overridepublic void onFailure(EventSource eventSource, Throwable t, Response response) {if(Objects.isNull(response)){log.error("sse连接异常:{}", t);eventSource.cancel();return;}ResponseBody body = response.body();if (Objects.nonNull(body)) {// 错误处理 {"error_code":110,"error_msg":"Access token invalid or no longer valid"},异常:{}log.error("sse连接异常data:{},异常:{}", body.string(), t);} else {log.error("sse连接异常data:{},异常:{}", response, t);}eventSource.cancel();}/*** tokens* @return*/public long tokens() {return tokens;}
}

4、WenXinClient  流式主要看下 streamChat 方式,之前从千帆上找到流式例子 返回type是json的,所以之前自己手写的demo总报异常。

 public void streamChat(ChatBody chatBody, EventSourceListener eventSourceListener, ModelE modelE) {if (Objects.isNull(eventSourceListener)) {throw new WenXinException("参数异常:EventSourceListener不能为空");}chatBody.setStream(true);try {EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);Request request = new Request.Builder().url(assembleUrl(modelE)).post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()),new ObjectMapper().writeValueAsString(chatBody))).build();factory.newEventSource(request, eventSourceListener);} catch (Exception e) {log.error("请求参数解析异常:", e);e.printStackTrace();}}private String assembleUrl(ModelE modelE) {accessToken = WenXinConfig.refreshAccessToken();return modelE.getApiHost() + "?access_token=" + accessToken;}

5、定义Sse的接口是实现方法

public interface SseService {/*** 创建SSE* @param uid* @return*/SseEmitter createSse(String uid);/*** 关闭SSE* @param uid*/void closeSse(String uid);/*** 客户端发送消息到服务端* @param uid* @param chatRequest*/ChatResponse sseChat(String uid, ChatRequest chatRequest);
}
public class WenXinSseServiceImpl implements SseService {@Value("${chat.accessKeyId}")private String accessKeyId;@Value("${chat.accessKeySecret}")private String accessKeySecret;@Value("${chat.agentKey}")private String agentKey;@Value("${chat.appId}")private String appId;@AutowiredWenXinClient wenXinClient;@Overridepublic SseEmitter createSse(String uid) {//默认30秒超时,设置为0L则永不超时SseEmitter sseEmitter = new SseEmitter(0l);//完成后回调sseEmitter.onCompletion(() -> {log.info("[{}]结束连接...................", uid);LocalCache.CACHE.remove(uid);});//超时回调sseEmitter.onTimeout(() -> {log.info("[{}]连接超时...................", uid);});//异常回调sseEmitter.onError(throwable -> {try {log.info("[{}]连接异常,{}", uid, throwable.toString());sseEmitter.send(SseEmitter.event().id(uid).name("发生异常!").data(Message.builder().content("发生异常请重试!").build()).reconnectTime(3000));LocalCache.CACHE.put(uid, sseEmitter);} catch (IOException e) {e.printStackTrace();}});try {sseEmitter.send(SseEmitter.event().reconnectTime(5000));} catch (IOException e) {e.printStackTrace();}LocalCache.CACHE.put(uid, sseEmitter);log.info("[{}]创建sse连接成功!", uid);return sseEmitter;}@Overridepublic void closeSse(String uid) {SseEmitter sse = (SseEmitter) LocalCache.CACHE.get(uid);if (sse != null) {sse.complete();//移除LocalCache.CACHE.remove(uid);}}@Overridepublic ChatResponse sseChat(String uid, ChatRequest chatRequest) {if (StringUtils.isBlank(chatRequest.getMsg())) {log.error("参数异常,msg为null", uid);throw new BaseException("参数异常,msg不能为空~");}SseEmitter sseEmitter = (SseEmitter) LocalCache.CACHE.get(uid);if (sseEmitter == null) {log.info("聊天消息推送失败uid:[{}],没有创建连接,请重试。", uid);throw new BaseException("聊天消息推送失败uid:[{}],没有创建连接,请重试。~");}WenxinEventSourceListener openAIEventSourceListener = new WenxinEventSourceListener(sseEmitter);List<MessageItem> messages = new ArrayList<>();messages.add(MessageItem.builder().role(MessageItem.Role.USER).content(chatRequest.getMsg()).build());wenXinClient.streamChat(messages, openAIEventSourceListener, ModelE.ERNIE_Bot);LocalCache.CACHE.put("msg" + uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);ChatResponse response = new ChatResponse();response.setQuestionTokens(1);return response;}
}

6、主要的controller接口

/*** 创建sse连接** @param headers* @return*/@CrossOrigin@GetMapping("/createSse")public SseEmitter createConnect(@RequestHeader Map<String, String> headers) {String uid = getUid(headers);return sseService.createSse(uid);}/*** 聊天接口** @param chatRequest* @param headers*/@CrossOrigin@PostMapping("/chat")@ResponseBodypublic ChatResponse sseChat(@RequestBody ChatRequest chatRequest, @RequestHeader Map<String, String> headers, HttpServletResponse response) {String uid = getUid(headers);return sseService.sseChat(uid, chatRequest);}/*** 关闭连接** @param headers*/@CrossOrigin@GetMapping("/closeSse")public void closeConnect(@RequestHeader Map<String, String> headers) {String uid = getUid(headers);sseService.closeSse(uid);}

7、主要的页面代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>智能问答</title><link rel="stylesheet" href="styles.css"> <!-- 引入外部CSS --><script src="HZRecorder.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script><script src="js/markdown.min.js"></script><script src="js/eventsource.min.js"></script><script>function setText(text, uuid_str) {let content = document.getElementById(uuid_str);content.innerHTML = marked(text);}function uuid() {var s = [];var hexDigits = "0123456789abcdef";for (var i = 0; i < 36; i++) {s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);}s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01s[8] = s[13] = s[18] = s[23] = "-";var uuid = s.join("");console.log(uuid)return uuid;}window.onload = function () {/*let disconnectBtn = document.getElementById("disconnectSSE");*/let messageElement = document.getElementById("messageInput");let chat = document.getElementById("chat-messages");let sse;let uid = window.localStorage.getItem("uid");if (uid == null || uid == "" || uid == "null") {uid = uuid();}let text = "";let uuid_str;// 设置本地存储window.localStorage.setItem("uid", uid);// 发送消息按钮点击事件document.getElementById('sendTextButton').addEventListener('click', async function () {try {const userInput = document.getElementById('messageInput').value.trim();if (userInput) {await sseOneTurn(userInput)userInput.value = ''; // 清空输入框} else {alert('请输入文字消息!');}} catch (error) {alert('发送消息时发生错误: ' + error.message);}});// 回车事件messageElement.onkeydown = function () {if (window.event.keyCode === 13) {if (!messageElement.value) {return;}sseOneTurn(messageElement.value);}};function sseOneTurn(InputText) {uuid_str = uuid();//创建sseconst eventSource = new EventSourcePolyfill("/createSse", {headers: {uid: uid,},});eventSource.onopen = (event) => {console.log("开始输出后端返回值");sse = event.target;};eventSource.onmessage = (event) => {debugger;if (event.lastEventId == "[TOKENS]") {text = text + event.data;setText(text, uuid_str);text = "";return;}if (event.data == "[DONE]") {text = "";if (sse) {sse.close();}return;}let json_data = JSON.parse(event.data);console.log(json_data);if (json_data.text == null || json_data.text == "null") {return;}text = text + json_data.text;setText(text, uuid_str);};eventSource.onerror = (event) => {console.log("onerror", event);alert("服务异常请重试并联系开发者!");if (event.readyState === EventSource.CLOSED) {console.log("connection is closed");} else {console.log("Error occured", event);}event.target.close();};eventSource.addEventListener("customEventName", (event) => {console.log("Message id is " + event.lastEventId);});eventSource.addEventListener("customEventName", (event) => {console.log("Message id is " + event.lastEventId);});$.ajax({type: "post",url: "/chat",data: JSON.stringify({msg: InputText,}),contentType: "application/json;charset=UTF-8",dataType: "json",headers: {uid: uid,},beforeSend: function (request) {},success: function (result) {//新增问题框debugger;chat.innerHTML +='<tr><td style="height: 30px;">' +InputText +"<br/><br/> tokens:" +result.question_tokens +"</td></tr>";InputText = null;//新增答案框chat.innerHTML +='<tr><td><article id="' +uuid_str +'" class="markdown-body"></article></td></tr>';},complete: function () {},error: function () {console.info("发送问题失败!");},});}/*disconnectBtn.onclick = function () {if (sse) {sse.close();}};*/};</script></head>
<body><div class="chat-container"><div class="chat-header"><h1>智能问答</h1></div><div class="chat-messages" id="chat-messages"><!-- 聊天消息将会在这里显示 --></div><form class="message-form" onsubmit="return false;"><input type="text" id="messageInput" placeholder="输入消息..." autocomplete="off"><button type="button" id="sendTextButton">发送文字</button><button type="button" id="recordAndUploadButton">按住录音</button><progress id="uploadProgress" value="0" max="100" style="display:none;"></progress></form>
</div></body></html>

最后的呈现效果如下:

相关文章:

百度文心一言 java 支持流式输出,Springboot+ sse的demo

参考&#xff1a;GitHub - mmciel/wenxin-api-java: 百度文心一言Java库&#xff0c;支持问答和对话&#xff0c;支持流式输出和同步输出。提供SpringBoot调用样例。提供拓展能力。 1、依赖 <dependency> <groupId>com.baidu.aip</groupId> <artifactId…...

59.基于SSM实现的网上花店系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;网上花店是在MySQL中建立数据表保存信息&#xff0c;运用SSMVue框架和Java语言编写。并按照软件设计开发流程进行设计实现充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SSM的网…...

什么是字节码?

字节码&#xff08;Bytecode&#xff09;是Java虚拟机&#xff08;JVM&#xff09;能够理解和执行的中间代码。Java源代码首先编译成字节码文件&#xff08;扩展名为 .class&#xff09;&#xff0c;而不是直接编译成特定机器的机器码。字节码具有以下特点&#xff1a; 平台无…...

C++ JWT的使用

接入sdk需要使用JWT加密参数&#xff0c;做个记录以备后查 #include <iostream> #include <jwt-cpp/jwt.h> int main() { // 设置JWT的密钥&#xff08;对于HS256&#xff09; std::string secret_key "your-256-bit-secret"; // 创建一个新的JW…...

SpringBoot内置插件的使用(jackson和lombok)

文章目录 引言I lombok(自动为属性生成构造器)II jacksonsee also引言 idea正式版2021.2.2 已经捆绑安装jackson和lombok插件 I lombok(自动为属性生成构造器) Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。 htt…...

Franz Electron + React 源码启动运行填坑指南

环境要求 安装miniconda python 环境electron/rebuild用得着&#xff0c;miniconda 默认自带的 python 是 3.11 版本&#xff0c;比较新&#xff1b; 安装virsual studio 2019 要把C桌面相关的都安装了&#xff0c;大概需要20G&#xff0c;不要安装到 C 盘&#xff0c;都安装到…...

网络安全法中关于网络信息的保护和监管,有哪些规定?

网络安全法作为我们数字时代的重要法律保障&#xff0c;对于网络信息的保护和监管有着明确且详细的规定。这些规定不仅体现了国家对于网络安全的重视&#xff0c;也为我们每个人在数字世界中提供了坚实的法律屏障。 首先&#xff0c;我们来看一个关于网络运营者主体责任的案例。…...

前端XHR请求数据

axios封装了XHR(XMLHttpRequest) 效果 项目结构 Jakarta EE9&#xff0c;Web项目。 无额外的maven依赖 1、Web页面 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title&…...

利用香港多IP服务器优化网站访问速度的关键策略?

利用香港多IP服务器优化网站访问速度的关键策略? 随着数字化时代的不断发展&#xff0c;网站的全球访问速度成为企业吸引用户、提升竞争力的重要因素。特别对于跨国企业而言&#xff0c;如何确保全球用户都能享受到稳定快速的访问体验显得尤为重要。在这一背景下&#xff0c;…...

如何快速将视频做成二维码?扫描二维码播放视频的制作方法

视频二维码的用途越来越多&#xff0c;比如常见的有产品展示、企业宣传、教程说明、个人展示等都可以生成二维码&#xff0c;通过扫码在手机或者其他设备上预览内容&#xff0c;从而提升其他人获取视频的速度&#xff0c;实现内容的快速分享。 对于有制作视频二维码需求的小伙…...

使用python开发的闭运算调试器

使用python开发的开运算调试器 简介效果代码 简介 用来调试闭运算效果的小工具&#xff0c;滑动条可以控制滤波核的大小&#xff0c;用来查看不同滤波核下的闭运算效果。 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayou…...

一例Phorpiex僵尸网络变种的分析

概述 这是一例Phorpiex僵尸网络变种&#xff0c;通过NSIS打包&#xff0c;加载恶意dll(Flaminius.dll)&#xff0c;读取dat文件&#xff08;Preoral.dat&#xff09;&#xff0c;在内存解密并解压缩出一个Pe&#xff0c;创建同名傀儡进程并注入。通过可移动存储介质传播&#…...

PDF文件转换为CAD的方法

有时候我们收到一个PDF格式的设计图纸&#xff0c;但还需要进行编辑或修改时&#xff0c;就必须先将PDF文件转换回CAD格式。分享两个将PDF转换回CAD的方法&#xff0c;一个用到在线网站&#xff0c;一个用到PC软件&#xff0c;大家根据情况选择就可以了。 ☞在线CAD网站转换 …...

Java为什么会成为现在主流的编程语言

Java为什么会成为现在的主流语言 前言一、Java语言概述Java是什么为什么大多数人会选择从事Java为什么从事Java的工作者数量从年递减 二、Java语言的特点简单性面向对象分布式&#xff08;微服务&#xff09;健壮性安全性体系结构中立可移植性解释型高性能多线程动态性 三、Jav…...

动手学深度学习16 Pytorch神经网络基础

动手学深度学习16 Pytorch神经网络基础 1. 模型构造2. 参数管理1. state_dict()2. normal_() zeros_()3. xavier初始化共享参数的好处 3. 自定义层4. 读写文件net.eval() 评估模式 QA 1. 模型构造 定义隐藏层–模型结构定义前向函数–模型结构的调用 import torch from torch…...

前端无样式id或者class等来定位标签

目录&#xff1a; 1、使用背景2、代码处理 1、使用背景 客户使用我们产品组件&#xff0c;发现替换文件&#xff0c;每次替换都会新增如下的样式&#xff0c;造就样式错乱&#xff0c;是组件的文件&#xff0c;目前临时处理的话就是替换文件时删除新增的样式&#xff0c;但是发…...

机器人工具箱学习(三)

一、动力学方程 机器人的动力学公式描述如下&#xff1a; 式中&#xff0c; τ \boldsymbol{\tau} τ表示关节驱动力矩矢量&#xff1b; q , q ˙ , q \boldsymbol{q} ,\; \dot{\boldsymbol { q }} ,\; \ddot{\boldsymbol { q }} q,q˙​,q​分别为广义的关节位置、速度和加速…...

华为OD机试 - CPU算力分配(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…...

web前端框架设计第八课-表单控件绑定

web前端框架设计第八课-表单控件绑定 一.预习笔记 1.v-model实现表单数据双向绑定 2.搜索数据的实现 3.全选案例实现1—JQ方法 4.单选案例实现 5.数据级联&#xff08;二级级联&#xff09; 6.v-model中的修饰符 二.课堂笔记 三.课后回顾 –行动是治愈恐惧的良药&#xff0c…...

这三个网站我愿称之为制作答辩PPT的神

很多快要毕业的同学在做答辩PPT的时候总是感觉毫无思路&#xff0c;一窍不通。但这并不是你们的错&#xff0c;对于平时没接触过相关方面&#xff0c;第一次搞答辩PPT的人来说&#xff0c;这是很正常的一件事。一个好的答辩PPT可以根据以下分为以下几部分来写。 1.研究的背景和…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...