├── .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 |
--------------------------------------------------------------------------------