├── .gitignore ├── README.md ├── pom.xml └── src └── main └── java └── top └── hualuo ├── XhStreamClient.java └── dto ├── MsgDTO.java ├── RequestDTO.java └── ResponseDTO.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 讯飞星火大模型流式输出,对接星火官方API,对接简单,只需增加配置即可完成对接, 3 | 支持Tokens计算。 4 | 5 | # 使用方式 6 | ## 1、引入该依赖 7 | 8 | ```java 9 | 10 | io.github.a812086325 11 | xfxh-java 12 | 1.0 13 | 14 | ``` 15 | 16 | ## 2、添加yml配置 17 | ```yml 18 | xfxh: 19 | apiHost: spark-api.xf-yun.com 20 | apiPath: /v1.1/chat 21 | appId: xxx 22 | apiKey: xxx 23 | apiSecret: xxx 24 | ``` 25 | 26 | ## 3、编写配置类 27 | ```java 28 | @Configuration 29 | @ConfigurationProperties(prefix = "xfxh") 30 | @Data 31 | public class XfXhConfig { 32 | private String apiHost; 33 | private String apiPath; 34 | private String appId; 35 | private String apiKey; 36 | private String apiSecret; 37 | } 38 | ``` 39 | 40 | ## 4、在启动类或配置类注册一个bean 41 | ```java 42 | @SpringBootApplication 43 | public class DevScaffoldApplication { 44 | @Autowired 45 | private XfXhConfig xfXhConfig; 46 | 47 | public static void main(String[] args) { 48 | SpringApplication.run(DevScaffoldApplication.class, args); 49 | } 50 | 51 | @Bean 52 | public XhStreamClient xhStreamClient (){ 53 | return XhStreamClient.builder() 54 | .apiHost(xfXhConfig.getApiHost()) 55 | .apiPath(xfXhConfig.getApiPath()) 56 | .appId(xfXhConfig.getAppId()) 57 | .apiKey(xfXhConfig.getApiKey()) 58 | .apiSecret(xfXhConfig.getApiSecret()) 59 | 60 | .build(); 61 | } 62 | } 63 | ``` 64 | 65 | ## 5、自定义一个Listener类继承WebSocketListener 66 | ```java 67 | public class XfXhListener extends WebSocketListener { 68 | 69 | @Override 70 | public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { 71 | super.onOpen(webSocket, response); 72 | 73 | } 74 | 75 | @Override 76 | public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { 77 | super.onMessage(webSocket, text); 78 | System.out.println("text:\n" + text); 79 | ResponseDTO responseData = JSONObject.parseObject(text,ResponseDTO.class); 80 | if(0 == responseData.getHeader().getCode()){ 81 | ResponseDTO.PayloadDTO pl = responseData.getPayload(); 82 | List tests = pl.getChoices().getText(); 83 | MsgDTO textDTO = tests.stream().findFirst().orElse(new MsgDTO()); 84 | 85 | System.out.println(textDTO.toString()); 86 | 87 | if(2 == responseData.getHeader().getStatus()){ 88 | ResponseDTO.PayloadDTO.UsageDTO.TextDTO testDto = pl.getUsage().getText(); 89 | Integer totalTokens = testDto.getTotalTokens(); 90 | System.out.println("本次花费:"+totalTokens + " tokens"); 91 | 92 | 93 | webSocket.close(3,"客户端主动断开链接"); 94 | } 95 | 96 | 97 | }else{ 98 | System.out.println("返回结果错误:\n" + responseData.getHeader().getCode()+ responseData.getHeader().getMessage() ); 99 | } 100 | } 101 | 102 | @Override 103 | public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { 104 | super.onFailure(webSocket, t, response); 105 | } 106 | 107 | @Override 108 | public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { 109 | super.onClosed(webSocket, code, reason); 110 | } 111 | } 112 | 113 | ``` 114 | 115 | ## 6、在需要发送的地方,注入xhStreamClient就可以发送消息啦 116 | 1. 第一个参数为uid,用户标识 117 | 2. 第二个参数为消息对象,是一个集合,需要上下文回答,需要把历史消息也传入 118 | 3. 第三个参数为刚才自定义的Listener类 119 | ```java 120 | @Autowired 121 | private XhStreamClient xhStreamClient; 122 | 123 | MsgDTO dto = MsgDTO.builder().role(MsgDTO.Role.USER.getName()).content("请介绍一下你自己").build(); 124 | 125 | xhStreamClient.sendMsg("123", Arrays.asList(dto),new XfXhListener()); 126 | ``` 127 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.a812086325 8 | xfxh-java 9 | 1.1 10 | xfxh-java 11 | 讯飞星火大模型 JAVA SDK 12 | https://www.hualuo.top 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.7.0 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | The Apache Software License, Version 2.0 26 | http://www.apache.org/licenses/LICENSE-2.0.txt 27 | repo 28 | 29 | 30 | 31 | 32 | scm:git@github.com:a812086325/xfxh-java.git 33 | scm:git@github.com:a812086325/xfxh-java.git 34 | 35 | https://github.com/a812086325/xfxh-java 36 | 37 | 38 | 39 | 40 | hualuo 41 | 812086325@qq.com 42 | https://github.com/a812086325 43 | +8 44 | 45 | 46 | 47 | 48 | 49 | oss 50 | https://s01.oss.sonatype.org/content/repositories/snapshots 51 | 52 | 53 | oss 54 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 55 | 56 | 57 | 58 | 59 | 8 60 | 8 61 | UTF-8 62 | true 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.projectlombok 70 | lombok 71 | true 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-test 76 | test 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-websocket 82 | 83 | 84 | 85 | cn.hutool 86 | hutool-http 87 | 5.8.12 88 | 89 | 90 | 91 | com.squareup.okhttp3 92 | okhttp-sse 93 | 3.14.9 94 | 95 | 96 | 97 | com.squareup.okhttp3 98 | logging-interceptor 99 | 3.14.9 100 | 101 | 102 | 103 | junit 104 | junit 105 | 4.13.2 106 | test 107 | 108 | 109 | 110 | com.alibaba 111 | fastjson 112 | 1.2.60 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | org.springframework.boot 121 | spring-boot-maven-plugin 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-compiler-plugin 126 | 127 | utf-8 128 | 8 129 | 8 130 | 131 | 132 | 133 | org.sonatype.plugins 134 | nexus-staging-maven-plugin 135 | 1.6.8 136 | true 137 | 138 | oss 139 | https://s01.oss.sonatype.org/ 140 | true 141 | 142 | 143 | 144 | com.thoughtworks.xstream 145 | xstream 146 | 1.4.15 147 | 148 | 149 | 150 | 151 | 152 | org.apache.maven.plugins 153 | maven-source-plugin 154 | 2.2.1 155 | 156 | 157 | attach-sources 158 | 159 | jar-no-fork 160 | 161 | 162 | 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-gpg-plugin 167 | 1.5 168 | 169 | 170 | sign-artifacts 171 | verify 172 | 173 | sign 174 | 175 | 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-javadoc-plugin 181 | 2.9.1 182 | 183 | private 184 | true 185 | UTF-8 186 | UTF-8 187 | UTF-8 188 | 189 | -Xdoclint:none 190 | 191 | 192 | 193 | package 194 | 195 | jar 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | deploy 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-source-plugin 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-javadoc-plugin 217 | 218 | 219 | 220 | org.apache.maven.plugins 221 | maven-gpg-plugin 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /src/main/java/top/hualuo/XhStreamClient.java: -------------------------------------------------------------------------------- 1 | package top.hualuo; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import cn.hutool.http.HttpUtil; 5 | import com.alibaba.fastjson.JSONObject; 6 | import okhttp3.OkHttpClient; 7 | import okhttp3.Request; 8 | import okhttp3.WebSocket; 9 | import okhttp3.WebSocketListener; 10 | import top.hualuo.dto.MsgDTO; 11 | import top.hualuo.dto.RequestDTO; 12 | 13 | import javax.crypto.Mac; 14 | import javax.crypto.spec.SecretKeySpec; 15 | import java.nio.charset.Charset; 16 | import java.text.SimpleDateFormat; 17 | import java.util.*; 18 | 19 | /** 20 | * 星火流式请求客户端 21 | */ 22 | public class XhStreamClient { 23 | private String apiHost; 24 | private String apiPath; 25 | private String appId; 26 | private String apiKey; 27 | private String apiSecret; 28 | 29 | public static Builder builder() { 30 | return new Builder(); 31 | } 32 | 33 | private XhStreamClient(Builder builder) { 34 | if (StrUtil.isBlank(builder.apiHost)) { 35 | builder.apiHost = "spark-api.xf-yun.com"; 36 | } 37 | this.apiHost = builder.apiHost; 38 | 39 | this.apiPath = builder.apiPath; 40 | 41 | this.appId = builder.appId; 42 | 43 | this.apiKey = builder.apiKey; 44 | 45 | this.apiSecret = builder.apiSecret; 46 | } 47 | 48 | public static final class Builder { 49 | private String apiHost; 50 | private String apiPath; 51 | private String appId; 52 | private String apiKey; 53 | private String apiSecret; 54 | 55 | public Builder() { 56 | } 57 | 58 | public Builder apiHost(String val) { 59 | this.apiHost = val; 60 | return this; 61 | } 62 | 63 | public Builder apiPath(String val) { 64 | this.apiPath = val; 65 | return this; 66 | } 67 | 68 | public Builder appId(String val) { 69 | this.appId = val; 70 | return this; 71 | } 72 | 73 | public Builder apiKey(String val) { 74 | this.apiKey = val; 75 | return this; 76 | } 77 | 78 | public Builder apiSecret(String val) { 79 | this.apiSecret = val; 80 | return this; 81 | } 82 | 83 | public XhStreamClient build() { 84 | return new XhStreamClient(this); 85 | } 86 | } 87 | 88 | 89 | /** 90 | * 获取验证请求url 91 | * @return 92 | */ 93 | public String getAuthorizationUrl(){ 94 | try { 95 | // 获取鉴权时间 date 96 | SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 97 | format.setTimeZone(TimeZone.getTimeZone("GMT")); 98 | String date = format.format(new Date()); 99 | 100 | // 获取signature_origin字段 101 | StringBuilder builder = new StringBuilder("host: ").append(this.apiHost).append("\n"). 102 | append("date: ").append(date).append("\n"). 103 | append("GET ").append(this.apiPath).append(" HTTP/1.1"); 104 | 105 | // 获得signatue 106 | Charset charset = Charset.forName("UTF-8"); 107 | Mac mac = Mac.getInstance("hmacsha256"); 108 | SecretKeySpec sp = new SecretKeySpec(this.apiSecret.getBytes(charset),"hmacsha256"); 109 | mac.init(sp); 110 | byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset)); 111 | String signature = Base64.getEncoder().encodeToString(basebefore); 112 | //获得 authorization_origin 113 | String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"",this.apiKey,"hmac-sha256","host date request-line",signature); 114 | //获得authorization 115 | String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset)); 116 | // 获取httpUrl 117 | Map param = new HashMap<>(); 118 | param.put("authorization",authorization); 119 | param.put("date",date); 120 | param.put("host",this.apiHost); 121 | 122 | String toParams = HttpUtil.toParams(param); 123 | 124 | return "wss://" + this.apiHost + this.apiPath + "?" + toParams; 125 | }catch (Exception e){ 126 | e.printStackTrace(); 127 | } 128 | 129 | return ""; 130 | } 131 | 132 | /** 133 | * 获取请求参数 134 | * @param uid 135 | * @param msgList 136 | * @return 137 | */ 138 | public RequestDTO getRequestParam(String uid, List msgList) { 139 | RequestDTO dto = new RequestDTO(); 140 | dto.setHeader(new RequestDTO.HeaderDTO(this.appId,uid)); 141 | dto.setParameter(new RequestDTO.ParameterDTO(new RequestDTO.ParameterDTO.ChatDTO())); 142 | dto.setPayload(new RequestDTO.PayloadDTO(new RequestDTO.PayloadDTO.MessageDTO(msgList))); 143 | return dto; 144 | } 145 | 146 | /** 147 | * 发送消息 148 | * @param uid 149 | * @param msgList 150 | * @return 151 | */ 152 | public WebSocket sendMsg(String uid, List msgList, WebSocketListener listener) { 153 | // 获取鉴权url 154 | String authorizationUrl = this.getAuthorizationUrl(); 155 | OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); 156 | Request request = new Request.Builder().url(authorizationUrl).build(); 157 | WebSocket webSocket = okHttpClient.newWebSocket(request,listener); 158 | 159 | 160 | RequestDTO requestDTO = this.getRequestParam(uid,msgList); 161 | System.out.println("param=============="); 162 | System.out.println(JSONObject.toJSONString(requestDTO)); 163 | 164 | webSocket.send(JSONObject.toJSONString(requestDTO)); 165 | 166 | return webSocket; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/top/hualuo/dto/MsgDTO.java: -------------------------------------------------------------------------------- 1 | package top.hualuo.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.*; 5 | 6 | /** 7 | * 消息对象 8 | * @author hualuo 9 | */ 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class MsgDTO { 15 | /** 16 | * 角色 17 | */ 18 | private String role; 19 | /** 20 | * 消息内容 21 | */ 22 | private String content; 23 | private Integer index; 24 | 25 | @Getter 26 | public static enum Role { 27 | SYSTEM("system"), 28 | USER("user"), 29 | ASSISTANT("assistant"); 30 | 31 | private String name; 32 | 33 | private Role(String name) { 34 | this.name = name; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/top/hualuo/dto/RequestDTO.java: -------------------------------------------------------------------------------- 1 | package top.hualuo.dto; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 请求参数 13 | * @author hualuo 14 | */ 15 | @NoArgsConstructor 16 | @Data 17 | public class RequestDTO { 18 | 19 | @JsonProperty("header") 20 | private HeaderDTO header; 21 | @JsonProperty("parameter") 22 | private ParameterDTO parameter; 23 | @JsonProperty("payload") 24 | private PayloadDTO payload; 25 | 26 | @NoArgsConstructor 27 | @Data 28 | @AllArgsConstructor 29 | public static class HeaderDTO { 30 | @JSONField(name = "app_id") 31 | private String appId; 32 | @JSONField(name = "uid") 33 | private String uid; 34 | } 35 | 36 | @NoArgsConstructor 37 | @Data 38 | @AllArgsConstructor 39 | public static class ParameterDTO { 40 | private ChatDTO chat; 41 | 42 | @NoArgsConstructor 43 | @Data 44 | public static class ChatDTO { 45 | @JsonProperty("domain") 46 | private String domain = "general"; 47 | @JsonProperty("temperature") 48 | private Double temperature = 0.5; 49 | @JSONField(name = "max_tokens") 50 | private Integer maxTokens = 2048; 51 | } 52 | } 53 | 54 | @NoArgsConstructor 55 | @Data 56 | @AllArgsConstructor 57 | public static class PayloadDTO { 58 | @JsonProperty("message") 59 | private MessageDTO message; 60 | 61 | @NoArgsConstructor 62 | @Data 63 | @AllArgsConstructor 64 | public static class MessageDTO { 65 | @JsonProperty("text") 66 | private List text; 67 | 68 | 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/top/hualuo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package top.hualuo.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 返回参数 11 | * @author hualuo 12 | */ 13 | @NoArgsConstructor 14 | @Data 15 | public class ResponseDTO { 16 | 17 | @JsonProperty("header") 18 | private HeaderDTO header; 19 | @JsonProperty("payload") 20 | private PayloadDTO payload; 21 | 22 | @NoArgsConstructor 23 | @Data 24 | public static class HeaderDTO { 25 | @JsonProperty("code") 26 | private Integer code; 27 | @JsonProperty("message") 28 | private String message; 29 | @JsonProperty("sid") 30 | private String sid; 31 | @JsonProperty("status") 32 | private Integer status; 33 | } 34 | 35 | @NoArgsConstructor 36 | @Data 37 | public static class PayloadDTO { 38 | @JsonProperty("choices") 39 | private ChoicesDTO choices; 40 | @JsonProperty("usage") 41 | private UsageDTO usage; 42 | 43 | @NoArgsConstructor 44 | @Data 45 | public static class ChoicesDTO { 46 | @JsonProperty("status") 47 | private Integer status; 48 | @JsonProperty("seq") 49 | private Integer seq; 50 | @JsonProperty("text") 51 | private List text; 52 | 53 | } 54 | 55 | @NoArgsConstructor 56 | @Data 57 | public static class UsageDTO { 58 | @JsonProperty("text") 59 | private TextDTO text; 60 | 61 | @NoArgsConstructor 62 | @Data 63 | public static class TextDTO { 64 | @JsonProperty("question_tokens") 65 | private Integer questionTokens; 66 | @JsonProperty("prompt_tokens") 67 | private Integer promptTokens; 68 | @JsonProperty("completion_tokens") 69 | private Integer completionTokens; 70 | @JsonProperty("total_tokens") 71 | private Integer totalTokens; 72 | } 73 | } 74 | } 75 | } 76 | --------------------------------------------------------------------------------