当前位置: 首页 > 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.研究的背景和…...

flutter开发实战-实现多渠道打包及友盟统计(亲测有效)

flutter开发实战-实现多渠道打包及友盟统计&#xff08;亲测有效&#xff09; 最近开发过程中&#xff0c;需要引入友盟进行统计服务。友盟统计还需要区分不同渠道的打开应用的情况&#xff0c;所以需要处理多渠道打包的问题。 一、引入友盟统计 在工程的pubspec.yaml中引入…...

JavaScript-JSON对象

JSON格式 JSON&#xff08;JavaScript Object Notation, JS对象简谱&#xff09;是一种轻量级的数据交换格式。它基于ECMAScript&#xff08;European Computer Manufacturers Association, 欧洲计算机协会的一个子集&#xff0c;采用完全独立于编程语言的文本格式来存储和表示…...

【C语言】自定义类型之---结构体超详解(结构体的定义使用、指针结构体,内存对齐,......代码详解)

目录 前言&#xff1a; 一&#xff1a;结构体 1.1&#xff1a;什么是结构体&#xff1f; 1.2&#xff1a;结构体类型的声明 1.3&#xff1a;结构体变量的定义 1.4&#xff1a;结构体的内存对齐 1.5&#xff1a;结构体传参 二&#xff1a;位段 2.1&#xff1a;位段是什…...

【完美恢复】修复计算机中丢失emp.dll的多个详细方法

最近&#xff0c;在尝试运行某款游戏时&#xff0c;我遭遇了一个令人头痛的问题——“emp.dll文件丢失”。这个错误通常意味着游戏的某个关键文件没有被正确加载或已损坏。以下是我解决问题的步骤和一些心得体会&#xff0c;希望对遇到类似问题的玩家们有所帮助。 emp.dll是一…...

暗黑4可以搬砖吗?暗黑4怎么搬砖 搬砖攻略

暗黑4可以搬砖吗&#xff1f;暗黑4怎么搬砖 搬砖攻略 暗黑破坏神4属于是暴雪旗下一款经典游戏IP&#xff0c;在全世界有着广泛的玩家群体&#xff0c;更是在今年暴雪国服宣布回归之后&#xff0c;吸引了一大批新玩家加入。今天小编就为大家带来暗黑4的详细搬砖教程。 现在我们…...

WLAN技术

冲突域&#xff1a;连接在同一传输线缆上的所有工作站的集合&#xff0c;或者说是同一物理网段上所有节点的集合共同竞争网络资源形成的域叫冲突域。 在OSI模型中&#xff0c;冲突域被看作是第一层的概念&#xff0c;连接同一冲突域的设备有中继器、集线器&#xff08;hub&…...

维修AB罗克韦尔工控机 PanelView 900 2711-T9C8 SER C 触摸屏人机界面

可视化和 HMI 解决方案可帮助您满足生产力、创新和全球化需求。为电子操作员界面终端、分布式客户端/服务器 HMI 和信息软件提供了一致的外观和感觉。编程工具和高级软件应用程序包括远程访问和数据分析&#xff0c;可加速开发并提高效率。 图形终端 图形终端提供各种尺寸、操…...

334_C++_std::bind中使用shared_from_this()

std::bind(&HttpClient::getPwd, shared_from_this(), std::placeholders::_1, std::placeholders::_2);[ HttpClient继承自NetObj,NetObj是父类,NetObj受到std::shared_pt...

【Python】防御性编程入门

1. 前言 防御性编程指的是为了防止代码泄露后被竞品公司窃取技术&#xff0c;使用一种较高级的明文加密编程方式。也可以当做一种带解密性质的时间胶囊&#xff0c;锻炼程序员自己的记忆能力、读代码能力等。 2. 案例分析 2.1 import Import里面可以多取一些喜欢的名字&#…...

无线麦克风哪个品牌音质最好?热门无线麦克风品牌推荐

这段时间短视频行业兴起&#xff0c;很多人都开始尝试步入自媒体&#xff0c;不过想要自己的视频内容更出色、更吸引人&#xff0c;好的音频设备肯定是必不可少的&#xff0c;而麦克风就是其中的一种。麦克风的好坏也将决定了一个视频的质量与完整性等等&#xff0c;如果我们作…...