├── .gitignore
├── LICENSE
├── README.md
├── docs
├── chatglm.drawio
└── curl
│ ├── curl-cogview-3.sh
│ ├── curl-glm-3-turbo.sh
│ ├── curl-glm-4.sh
│ ├── curl-glm-4v.sh
│ └── curl.sh
├── pom.xml
└── src
├── main
└── java
│ └── cn
│ └── bugstack
│ └── chatglm
│ ├── IOpenAiApi.java
│ ├── executor
│ ├── Executor.java
│ ├── aigc
│ │ ├── GLMExecutor.java
│ │ └── GLMOldExecutor.java
│ └── result
│ │ └── ResultHandler.java
│ ├── interceptor
│ └── OpenAiHTTPInterceptor.java
│ ├── model
│ ├── ChatCompletionRequest.java
│ ├── ChatCompletionResponse.java
│ ├── ChatCompletionSyncResponse.java
│ ├── EventType.java
│ ├── ImageCompletionRequest.java
│ ├── ImageCompletionResponse.java
│ ├── Model.java
│ └── Role.java
│ ├── session
│ ├── Configuration.java
│ ├── OpenAiSession.java
│ ├── OpenAiSessionFactory.java
│ └── defaults
│ │ ├── DefaultOpenAiSession.java
│ │ └── DefaultOpenAiSessionFactory.java
│ └── utils
│ └── BearerTokenUtils.java
└── test
└── java
└── cn
└── bugstack
└── chatglm
└── test
├── ApiTest.java
└── JSONTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 智谱Ai大模型开放SDK - By 小傅哥版本
2 |
3 | 为了让研发伙伴更快,更方便的接入使用智谱Ai大模型。从而开发的 chatglm-sdk-java 也欢迎👏🏻大家基于智谱API接口补充需要的功能。
4 |
5 | 此SDK设计,以 Session 会话模型,提供工厂🏭创建服务。代码非常清晰,易于扩展、易于维护。你的PR/ISSUE贡献💐会让AI更加璀璨,[感谢智谱AI团队](https://www.zhipuai.cn/)。
6 |
7 | ---
8 |
9 | >**作者**:小傅哥 - 个人博客 [**bugstack.cn**](https://bugstack.cn/),互联网大厂架构师,《重学Java设计模式》、《手写MyBatis:渐进式源码实践》图书作者。`欢迎百度搜索:小傅哥bugstack`
10 |
11 | ## 👣目录
12 |
13 | 1. 组件配置
14 | 2. 功能测试
15 | 1. 代码执行 - `使用:代码的方式主要用于程序接入`
16 | 2. 脚本测试 - `测试:生成Token,直接通过HTTP访问Ai服务`
17 | 3. 程序接入
18 |
19 | ## 1. 组件配置
20 |
21 | - 申请ApiKey:[https://open.bigmodel.cn/usercenter/apikeys](https://open.bigmodel.cn/usercenter/apikeys) - 注册申请开通,即可获得 ApiKey
22 | - 运行环境:JDK 1.8+
23 | - 支持模型:chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro、chatglm_turbo、glm-3-turbo、glm-4、glm-4v、cogview-3
24 | - maven pom - `已发布到Maven仓库`
25 |
26 | ```pom
27 |
28 | cn.bugstack
29 | chatglm-sdk-java
30 | 2.2
31 |
32 | ```
33 |
34 | - 源码(Github):[https://github.com/fuzhengwei/chatglm-sdk-java](https://github.com/fuzhengwei/chatglm-sdk-java)
35 | - 源码(Gitee):[https://gitee.com/fustack/chatglm-sdk-java](https://gitee.com/fustack/chatglm-sdk-java)
36 | - 源码(Gitcode):[https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java](https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java)
37 |
38 | ## 2. 功能测试
39 |
40 | ### 2.1 代码执行
41 |
42 | ```java
43 | private OpenAiSession openAiSession;
44 |
45 | @Before
46 | public void test_OpenAiSessionFactory() {
47 | // 1. 配置文件
48 | Configuration configuration = new Configuration();
49 | configuration.setApiHost("https://open.bigmodel.cn/");
50 | configuration.setApiSecretKey("62ddec38b1d0b9a7b0fddaf271e6ed90.HpD0SUBUlvqd05ey");
51 | configuration.setLevel(HttpLoggingInterceptor.Level.BODY);
52 | // 2. 会话工厂
53 | OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
54 | // 3. 开启会话
55 | this.openAiSession = factory.openSession();
56 | }
57 | ```
58 |
59 | - 测试前申请你的 ApiKey 填写到 setApiSecretKey 中使用。
60 |
61 | #### 2.1.1 流式对话 - 兼容旧版模式运行
62 |
63 | ```java
64 | /**
65 | * 流式对话;
66 | * 1. 默认 isCompatible = true 会兼容新旧版数据格式
67 | * 2. GLM_3_5_TURBO、GLM_4 支持联网等插件
68 | */
69 | @Test
70 | public void test_completions() throws Exception {
71 | CountDownLatch countDownLatch = new CountDownLatch(1);
72 | // 入参;模型、请求信息
73 | ChatCompletionRequest request = new ChatCompletionRequest();
74 | request.setModel(Model.GLM_3_5_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
75 | request.setIncremental(false);
76 | request.setIsCompatible(true); // 是否对返回结果数据做兼容,24年1月发布的 GLM_3_5_TURBO、GLM_4 模型,与之前的模型在返回结果上有差异。开启 true 可以做兼容。
77 | // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能
78 | request.setTools(new ArrayList() {
79 | private static final long serialVersionUID = -7988151926241837899L;
80 | {
81 | add(ChatCompletionRequest.Tool.builder()
82 | .type(ChatCompletionRequest.Tool.Type.web_search)
83 | .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build())
84 | .build());
85 | }
86 | });
87 | request.setPrompt(new ArrayList() {
88 | private static final long serialVersionUID = -7988151926241837899L;
89 | {
90 | add(ChatCompletionRequest.Prompt.builder()
91 | .role(Role.user.getCode())
92 | .content("小傅哥的是谁")
93 | .build());
94 | }
95 | });
96 | // 请求
97 | openAiSession.completions(request, new EventSourceListener() {
98 | @Override
99 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
100 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
101 | log.info("测试结果 onEvent:{}", response.getData());
102 | // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断
103 | if (EventType.finish.getCode().equals(type)) {
104 | ChatCompletionResponse.Meta meta = JSON.parseObject(response.getMeta(), ChatCompletionResponse.Meta.class);
105 | log.info("[输出结束] Tokens {}", JSON.toJSONString(meta));
106 | }
107 | }
108 | @Override
109 | public void onClosed(EventSource eventSource) {
110 | log.info("对话完成");
111 | countDownLatch.countDown();
112 | }
113 | @Override
114 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
115 | log.info("对话异常");
116 | countDownLatch.countDown();
117 | }
118 | });
119 | // 等待
120 | countDownLatch.await();
121 | }
122 | ```
123 |
124 | #### 2.1.2 流式对话 - 新版调用
125 |
126 | 👉查看代码
127 |
128 | ```java
129 | /**
130 | * 流式对话;
131 | * 1. 与 test_completions 测试类相比,只是设置 isCompatible = false 这样就是使用了新的数据结构。onEvent 处理接收数据有差异
132 | * 2. 不兼容旧版格式的话,仅支持 GLM_3_5_TURBO、GLM_4 其他模型会有解析错误
133 | */
134 | @Test
135 | public void test_completions_new() throws Exception {
136 | CountDownLatch countDownLatch = new CountDownLatch(1);
137 | // 入参;模型、请求信息
138 | ChatCompletionRequest request = new ChatCompletionRequest();
139 | request.setModel(Model.GLM_3_5_TURBO); // GLM_3_5_TURBO、GLM_4
140 | request.setIsCompatible(false);
141 | // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能
142 | request.setTools(new ArrayList() {
143 | private static final long serialVersionUID = -7988151926241837899L;
144 | {
145 | add(ChatCompletionRequest.Tool.builder()
146 | .type(ChatCompletionRequest.Tool.Type.web_search)
147 | .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build())
148 | .build());
149 | }
150 | });
151 | request.setMessages(new ArrayList() {
152 | private static final long serialVersionUID = -7988151926241837899L;
153 | {
154 | add(ChatCompletionRequest.Prompt.builder()
155 | .role(Role.user.getCode())
156 | .content("小傅哥的是谁")
157 | .build());
158 | }
159 | });
160 | // 请求
161 | openAiSession.completions(request, new EventSourceListener() {
162 | @Override
163 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
164 | if ("[DONE]".equals(data)) {
165 | log.info("[输出结束] Tokens {}", JSON.toJSONString(data));
166 | return;
167 | }
168 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
169 | log.info("测试结果:{}", JSON.toJSONString(response));
170 | }
171 | @Override
172 | public void onClosed(EventSource eventSource) {
173 | log.info("对话完成");
174 | countDownLatch.countDown();
175 | }
176 | @Override
177 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
178 | log.error("对话失败", t);
179 | countDownLatch.countDown();
180 | }
181 | });
182 | // 等待
183 | countDownLatch.await();
184 | }
185 | ```
186 |
187 |
188 |
189 | #### 2.1.3 流式对话 - 多模态图片识别 4v(vision)
190 |
191 | 👉查看代码
192 |
193 | ```java
194 | @Test
195 | public void test_completions_4v() throws Exception {
196 | CountDownLatch countDownLatch = new CountDownLatch(1);
197 | // 入参;模型、请求信息
198 | ChatCompletionRequest request = new ChatCompletionRequest();
199 | request.setModel(Model.GLM_4V); // GLM_3_5_TURBO、GLM_4
200 | request.setStream(true);
201 | request.setMessages(new ArrayList() {
202 | private static final long serialVersionUID = -7988151926241837899L;
203 | {
204 | // content 字符串格式
205 | add(ChatCompletionRequest.Prompt.builder()
206 | .role(Role.user.getCode())
207 | .content("这个图片写了什么")
208 | .build());
209 | // content 对象格式
210 | add(ChatCompletionRequest.Prompt.builder()
211 | .role(Role.user.getCode())
212 | .content(ChatCompletionRequest.Prompt.Content.builder()
213 | .type(ChatCompletionRequest.Prompt.Content.Type.text.getCode())
214 | .text("这是什么图片")
215 | .build())
216 | .build());
217 | // content 对象格式,上传图片;图片支持url、basde64
218 | add(ChatCompletionRequest.Prompt.builder()
219 | .role(Role.user.getCode())
220 | .content(ChatCompletionRequest.Prompt.Content.builder()
221 | .type(ChatCompletionRequest.Prompt.Content.Type.image_url.getCode())
222 | .imageUrl(ChatCompletionRequest.Prompt.Content.ImageUrl.builder().url("https://bugstack.cn/images/article/project/chatgpt/chatgpt-extra-231011-01.png").build())
223 | .build())
224 | .build());
225 | }
226 | });
227 | openAiSession.completions(request, new EventSourceListener() {
228 | @Override
229 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
230 | if ("[DONE]".equals(data)) {
231 | log.info("[输出结束] Tokens {}", JSON.toJSONString(data));
232 | return;
233 | }
234 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
235 | log.info("测试结果:{}", JSON.toJSONString(response));
236 | }
237 | @Override
238 | public void onClosed(EventSource eventSource) {
239 | log.info("对话完成");
240 | countDownLatch.countDown();
241 | }
242 | @Override
243 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
244 | log.error("对话失败", t);
245 | countDownLatch.countDown();
246 | }
247 | });
248 | // 等待
249 | countDownLatch.await();
250 | }
251 | ```
252 |
253 |
254 |
255 | #### 2.1.4 同步请求 - future 模式
256 |
257 | 👉查看代码
258 |
259 | ```java
260 | @Test
261 | public void test_completions_future() throws Exception {
262 | // 入参;模型、请求信息
263 | ChatCompletionRequest request = new ChatCompletionRequest();
264 | request.setModel(Model.CHATGLM_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
265 | request.setPrompt(new ArrayList() {
266 | private static final long serialVersionUID = -7988151926241837899L;
267 | {
268 | add(ChatCompletionRequest.Prompt.builder()
269 | .role(Role.user.getCode())
270 | .content("1+1")
271 | .build());
272 | }
273 | });
274 | CompletableFuture future = openAiSession.completions(request);
275 | String response = future.get();
276 | log.info("测试结果:{}", response);
277 | }
278 | ```
279 |
280 |
281 |
282 |
283 | #### 2.1.5 同步请求 - 普通模式
284 |
285 | 👉查看代码
286 |
287 | ```java
288 | @Test
289 | public void test_completions_sync() throws Exception {
290 | // 入参;模型、请求信息
291 | ChatCompletionRequest request = new ChatCompletionRequest();
292 | request.setModel(Model.GLM_4V); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
293 | request.setPrompt(new ArrayList() {
294 | private static final long serialVersionUID = -7988151926241837899L;
295 | {
296 | add(ChatCompletionRequest.Prompt.builder()
297 | .role(Role.user.getCode())
298 | .content("小傅哥是谁")
299 | .build());
300 | }
301 | });
302 | // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能
303 | request.setTools(new ArrayList() {
304 | private static final long serialVersionUID = -7988151926241837899L;
305 | {
306 | add(ChatCompletionRequest.Tool.builder()
307 | .type(ChatCompletionRequest.Tool.Type.web_search)
308 | .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build())
309 | .build());
310 | }
311 | });
312 | ChatCompletionSyncResponse response = openAiSession.completionsSync(request);
313 | log.info("测试结果:{}", JSON.toJSONString(response));
314 | }
315 | ```
316 |
317 |
318 |
319 | #### 2.1.6 文生图
320 |
321 | 👉查看代码
322 |
323 | ```java
324 | @Test
325 | public void test_genImages() throws Exception {
326 | ImageCompletionRequest request = new ImageCompletionRequest();
327 | request.setModel(Model.COGVIEW_3);
328 | request.setPrompt("画个小狗");
329 | ImageCompletionResponse response = openAiSession.genImages(request);
330 | log.info("测试结果:{}", JSON.toJSONString(response));
331 | }
332 | ```
333 |
334 |
335 |
336 |
337 | ### 2.2 脚本测试
338 |
339 | ```java
340 | @Test
341 | public void test_curl() {
342 | // 1. 配置文件
343 | Configuration configuration = new Configuration();
344 | configuration.setApiHost("https://open.bigmodel.cn/");
345 | configuration.setApiSecretKey("4e087e4135306ef4a676f0cce3cee560.sgP2D****");
346 | // 2. 获取Token
347 | String token = BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret());
348 | log.info("1. 在智谱Ai官网,申请 ApiSeretKey 配置到此测试类中,替换 setApiSecretKey 值。 https://open.bigmodel.cn/usercenter/apikeys");
349 | log.info("2. 运行 test_curl 获取 token:{}", token);
350 | log.info("3. 将获得的 token 值,复制到 curl.sh 中,填写到 Authorization: Bearer 后面");
351 | log.info("4. 执行完步骤3以后,可以复制直接运行 curl.sh 文件,或者复制 curl.sh 文件内容到控制台/终端/ApiPost中运行");
352 | }
353 | ```
354 |
355 | ```java
356 | curl -X POST \
357 | -H "Authorization: Bearer <把获得的Token填写这,并去掉两个尖括号>" \
358 | -H "Content-Type: application/json" \
359 | -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
360 | -H "Accept: text/event-stream" \
361 | -d '{
362 | "top_p": 0.7,
363 | "sseFormat": "data",
364 | "temperature": 0.9,
365 | "incremental": true,
366 | "request_id": "xfg-1696992276607",
367 | "prompt": [
368 | {
369 | "role": "user",
370 | "content": "写个java冒泡排序"
371 | }
372 | ]
373 | }' \
374 | http://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke
375 | ```
376 |
377 | - 运行后你会获得一个 Token 信息,之后在 curl.sh 中替换 Authorization: Bearer 后面的值。就可以执行测试了。
378 | - [curl.sh](https://github.com/fuzhengwei/chatglm-sdk-java/blob/master/docs/curl/curl.sh) | [curl-cogview-3.sh](https://github.com/fuzhengwei/chatglm-sdk-java/blob/master/docs/curl/curl-cogview-3.sh) | [curl-glm-3-turbo.sh](https://github.com/fuzhengwei/chatglm-sdk-java/blob/master/docs/curl/curl-glm-3-turbo.sh) | [curl-glm-4.sh](https://github.com/fuzhengwei/chatglm-sdk-java/blob/master/docs/curl/curl-glm-4.sh) | [curl-glm-4v.sh](https://github.com/fuzhengwei/chatglm-sdk-java/blob/master/docs/curl/curl-glm-4v.sh)
379 |
380 | ## 3. 程序接入
381 |
382 | SpringBoot 配置类
383 |
384 | ```java
385 | @Configuration
386 | @EnableConfigurationProperties(ChatGLMSDKConfigProperties.class)
387 | public class ChatGLMSDKConfig {
388 |
389 | @Bean
390 | @ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false)
391 | public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) {
392 | // 1. 配置文件
393 | cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration();
394 | configuration.setApiHost(properties.getApiHost());
395 | configuration.setApiSecretKey(properties.getApiSecretKey());
396 |
397 | // 2. 会话工厂
398 | OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
399 |
400 | // 3. 开启会话
401 | return factory.openSession();
402 | }
403 |
404 | }
405 |
406 | @Data
407 | @ConfigurationProperties(prefix = "chatglm.sdk.config", ignoreInvalidFields = true)
408 | public class ChatGLMSDKConfigProperties {
409 |
410 | /** 状态;open = 开启、close 关闭 */
411 | private boolean enable;
412 | /** 转发地址 */
413 | private String apiHost;
414 | /** 可以申请 sk-*** */
415 | private String apiSecretKey;
416 |
417 | }
418 | ```
419 |
420 | ```java
421 | @Autowired(required = false)
422 | private OpenAiSession openAiSession;
423 | ```
424 |
425 | - 注意:如果你在服务中配置了关闭启动 ChatGLM SDK 那么注入 openAiSession 为 null
426 |
427 | yml 配置
428 |
429 | ```pom
430 | # ChatGLM SDK Config
431 | chatglm:
432 | sdk:
433 | config:
434 | # 状态;true = 开启、false 关闭
435 | enable: false
436 | # 官网地址
437 | api-host: https://open.bigmodel.cn/
438 | # 官网申请 https://open.bigmodel.cn/usercenter/apikeys
439 | api-secret-key: 4e087e4135306ef4a676f0cce3cee560.sgP2DUs*****
440 | ```
441 |
442 | ---
443 |
444 | **对接案例**:[https://bugstack.cn/md/road-map/mock.html](https://bugstack.cn/md/road-map/mock.html)
445 |
446 | ---
447 |
448 | - 👨💻 成长:[关于我,从小白到架构师的成长经历](https://www.bilibili.com/video/BV1FF41137q5)
449 | - 🚌 作品:[`CodeGuide | 程序员编码指南`](https://github.com/fuzhengwei/CodeGuide) | [`RoadMap 编程路书`](https://github.com/fuzhengwei/RoadMap) | [`Java 数据结构和算法`](https://github.com/fuzhengwei/java-algorithms) | [`IM 仿微信`](https://github.com/fuzhengwei/NaiveChat) | [`Java 面经手册`](https://github.com/fuzhengwei/interview) | [`IntelliJ IDEA 插件开发`](https://github.com/fuzhengwei/guide-idea-plugin) | [`Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践`](https://github.com/fuzhengwei/Lottery) | [`API网关`](https://github.com/fuzhengwei/api-gateway) | [`手写MyBatis`](https://github.com/fuzhengwei/small-mybatis) | [`重学Java设计模式`](https://github.com/fuzhengwei/itstack-demo-design) | [`Netty 实战案例`](https://github.com/fuzhengwei/itstack-demo-netty) | [`字节码编程`](https://github.com/fuzhengwei/itstack-demo-bytecode) | [`ChatGPT AI 问答助手`](https://github.com/fuzhengwei/chatbot-api) | [更多搜索...](https://github.com/fuzhengwei?tab=repositories)
450 | - 🌱 干货:[公众号『 bugstack虫洞栈 』](https://bugstack.cn/images/personal/qrcode.png)
451 | - 📝 博客:[bugstack.cn](https://bugstack.cn/) - 足够硬核,内容老狠了!
452 | - 📺 视频:[B站 小傅哥の码场](https://space.bilibili.com/15637440)
453 | - 💌 微信:[fustack](https://bugstack.cn/images/personal/fustack.png) - 备注来意
454 | - 🐾 我的编程知识星球:[实战生产级项目、手写框架级源码,可以向我 1对1 提问,解答技术/职场/规划问题](https://bugstack.cn/md/zsxq/introduce.html)
455 |
456 | ---
457 |
458 | ```shell
459 | md5 chatglm-sdk-java-2.2.pom > chatglm-sdk-java-2.2.pom.md5
460 | shasum chatglm-sdk-java-2.2.pom > chatglm-sdk-java-2.2.pom.sha1
461 | ```
--------------------------------------------------------------------------------
/docs/chatglm.drawio:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/docs/curl/curl-cogview-3.sh:
--------------------------------------------------------------------------------
1 | curl -X POST \
2 | -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiNjJkZGVjMzhiMWQwYjlhN2IwZmRkYWYyNzFlNmVkOTAiLCJleHAiOjE3MDU1NTA5NjIwNjksInRpbWVzdGFtcCI6MTcwNTU0OTE2MjA2OX0.Rq_6NfmiBYVH6vs34jcMpn584ovOoY0rMB5hT-Bsr5c" \
3 | -H "Content-Type: application/json" \
4 | -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
5 | -d '{
6 | "model":"cogview-3",
7 | "prompt":"画一个小狗狗"
8 | }' \
9 | https://open.bigmodel.cn/api/paas/v4/images/generations
--------------------------------------------------------------------------------
/docs/curl/curl-glm-3-turbo.sh:
--------------------------------------------------------------------------------
1 | curl -X POST \
2 | -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiNjJkZGVjMzhiMWQwYjlhN2IwZmRkYWYyNzFlNmVkOTAiLCJleHAiOjE3MDU0ODc4Mjc1NDcsInRpbWVzdGFtcCI6MTcwNTQ4NjAyNzU0N30.wDi-JBIZZ6VZ28KuQgBoVpSoe4BZBAi0224fwLNBxdQ" \
3 | -H "Content-Type: application/json" \
4 | -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
5 | -d '{
6 | "model":"glm-3-turbo",
7 | "stream": "true",
8 | "messages": [
9 | {
10 | "role": "user",
11 | "content": "写个java冒泡排序"
12 | }
13 | ]
14 | }' \
15 | https://open.bigmodel.cn/api/paas/v4/chat/completions
--------------------------------------------------------------------------------
/docs/curl/curl-glm-4.sh:
--------------------------------------------------------------------------------
1 | curl -X POST \
2 | -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiNjJkZGVjMzhiMWQwYjlhN2IwZmRkYWYyNzFlNmVkOTAiLCJleHAiOjE3MDU0ODc4Mjc1NDcsInRpbWVzdGFtcCI6MTcwNTQ4NjAyNzU0N30.wDi-JBIZZ6VZ28KuQgBoVpSoe4BZBAi0224fwLNBxdQ" \
3 | -H "Content-Type: application/json" \
4 | -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
5 | -d '{
6 | "model":"glm-4",
7 | "stream": "true",
8 | "messages": [
9 | {
10 | "role": "user",
11 | "content": "写个java冒泡排序"
12 | }
13 | ]
14 | }' \
15 | https://open.bigmodel.cn/api/paas/v4/chat/completions
--------------------------------------------------------------------------------
/docs/curl/curl-glm-4v.sh:
--------------------------------------------------------------------------------
1 | # url 支持base64和图片地址;https://bugstack.cn/images/article/project/chatgpt/chatgpt-extra-231011-01.png
2 | curl -X POST \
3 | -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiMzk1ODBlMzRlMTc1MDE5YzIzMGZkZDUxOTgxN2IzODEiLCJleHAiOjE3MTczNzE2MzE4MDgsInRpbWVzdGFtcCI6MTcxNzM2OTgzMTgwOH0.2lk57oALzFxx2eAo-dKojKnVpn_MvJ8VJ4NQWTMYPHQ" \
4 | -H "Content-Type: application/json" \
5 | -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
6 | -d '{
7 | "messages": [
8 | {
9 | "role": "user",
10 | "content": [
11 | {
12 | "type": "text",
13 | "text": "这是什么图片"
14 | },
15 | {
16 | "type": "image_url",
17 | "image_url": {
18 | "url" : "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"
19 | }
20 | }
21 | ]
22 | }
23 | ],
24 | "model": "glm-4v",
25 | "stream": "true"
26 | }' \
27 | https://open.bigmodel.cn/api/paas/v4/chat/completions
--------------------------------------------------------------------------------
/docs/curl/curl.sh:
--------------------------------------------------------------------------------
1 | curl -X POST \
2 | -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiNzdkNDg2YTMyOGQ1ZGRlNTBiOTE0Y2UxMDJlNzFkNGYiLCJleHAiOjE2OTczNTU1NTgwMTQsInRpbWVzdGFtcCI6MTY5NzM1Mzc1ODAxNH0.KNOXxlwULWcvNZ9CvdxGLgYlf4pDNA0az2FoFV7DX7Q" \
3 | -H "Content-Type: application/json" \
4 | -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
5 | -H "Accept: text/event-stream" \
6 | -d '{
7 | "top_p": 0.7,
8 | "sseFormat": "data",
9 | "temperature": 0.9,
10 | "incremental": true,
11 | "request_id": "xfg-1696992276607",
12 | "prompt": [
13 | {
14 | "role": "user",
15 | "content": "写个java冒泡排序"
16 | }
17 | ]
18 | }' \
19 | http://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cn.bugstack
8 | chatglm-sdk-java
9 | 2.2
10 |
11 | chatglm-sdk-java
12 | OpenAI Java SDK, ZhiPuAi ChatGLM Java SDK . Copyright © 2023 bugstack虫洞栈 All rights reserved. 版权所有(C)小傅哥 https://github.com/fuzhengwei/chatglm-sdk-java
13 | https://github.com/fuzhengwei/chatglm-sdk-java
14 |
15 |
16 |
17 | Apache License
18 | https://opensource.org/license/apache-2-0/
19 | repo
20 |
21 |
22 |
23 |
24 |
25 | Xiaofuge
26 | Xiaofuge
27 | 184172133@qq.com
28 | https://github.com/fuzhengwei
29 | chatglm ai
30 | https://github.com/fuzhengwei/chatglm-sdk-java
31 |
32 | architect
33 | developer
34 |
35 | Asia/Shanghai
36 |
37 |
38 |
39 | scm:git:https://github.com/fuzhengwei/chatglm-sdk-java.git
40 | scm:git:https://github.com/fuzhengwei/chatglm-sdk-java.git
41 | HEAD
42 | https://github.com/fuzhengwei/chatglm-sdk-java
43 |
44 |
45 |
46 | 1.8
47 | UTF-8
48 | 1.8
49 | 1.8
50 | 2.9.0
51 | 2.0.6
52 | 3.2.0
53 | 3.2.1
54 | 1.6
55 | 1.10
56 |
57 |
58 |
59 |
60 | org.slf4j
61 | slf4j-api
62 | ${slf4j.version}
63 |
64 |
65 | org.slf4j
66 | slf4j-simple
67 | ${slf4j.version}
68 |
69 |
70 | com.squareup.okhttp3
71 | okhttp-sse
72 | 3.14.9
73 |
74 |
75 | com.squareup.okhttp3
76 | logging-interceptor
77 | 3.14.9
78 |
79 |
80 | com.squareup.retrofit2
81 | retrofit
82 | ${retrofit2.version}
83 |
84 |
85 | com.squareup.retrofit2
86 | converter-jackson
87 | ${retrofit2.version}
88 |
89 |
90 | com.squareup.retrofit2
91 | adapter-rxjava2
92 | ${retrofit2.version}
93 |
94 |
95 | junit
96 | junit
97 | 4.13.2
98 | test
99 |
100 |
101 | org.jetbrains
102 | annotations
103 | RELEASE
104 | compile
105 |
106 |
107 | com.knuddels
108 | jtokkit
109 | 0.2.0
110 |
111 |
112 | org.projectlombok
113 | lombok
114 | 1.18.24
115 | compile
116 |
117 |
118 | org.apache.commons
119 | commons-lang3
120 | 3.9
121 |
122 |
123 | com.auth0
124 | java-jwt
125 | 4.2.2
126 |
127 |
128 |
129 | com.google.guava
130 | guava
131 | 32.1.2-jre
132 |
133 |
134 |
135 | com.alibaba
136 | fastjson
137 | 2.0.41
138 | provided
139 | true
140 |
141 |
142 |
143 | org.apache.httpcomponents
144 | httpclient
145 | 4.5.14
146 |
147 |
148 | org.apache.httpcomponents
149 | httpmime
150 | 4.5.10
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | org.apache.maven.plugins
159 | maven-surefire-plugin
160 | 2.12.4
161 |
162 | true
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/IOpenAiApi.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm;
2 |
3 | import cn.bugstack.chatglm.model.*;
4 | import io.reactivex.Single;
5 | import retrofit2.http.Body;
6 | import retrofit2.http.POST;
7 | import retrofit2.http.Path;
8 |
9 | /**
10 | * @author 小傅哥,微信:fustack
11 | * @description OpenAi 接口,用于扩展通用类服务
12 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
13 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
14 | */
15 | public interface IOpenAiApi {
16 |
17 | String v3_completions = "api/paas/v3/model-api/{model}/sse-invoke";
18 | String v3_completions_sync = "api/paas/v3/model-api/{model}/invoke";
19 |
20 | @POST(v3_completions)
21 | Single completions(@Path("model") String model, @Body ChatCompletionRequest chatCompletionRequest);
22 |
23 | @POST(v3_completions_sync)
24 | Single completions(@Body ChatCompletionRequest chatCompletionRequest);
25 |
26 | String v4 = "api/paas/v4/chat/completions";
27 |
28 | String cogview3 = "api/paas/v4/images/generations";
29 |
30 | @POST(cogview3)
31 | Single genImages(@Body ImageCompletionRequest imageCompletionRequest);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/executor/Executor.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.executor;
2 |
3 | import cn.bugstack.chatglm.model.ChatCompletionRequest;
4 | import cn.bugstack.chatglm.model.ChatCompletionSyncResponse;
5 | import cn.bugstack.chatglm.model.ImageCompletionRequest;
6 | import cn.bugstack.chatglm.model.ImageCompletionResponse;
7 | import okhttp3.sse.EventSource;
8 | import okhttp3.sse.EventSourceListener;
9 |
10 | import java.io.IOException;
11 | import java.util.concurrent.CompletableFuture;
12 |
13 | public interface Executor {
14 |
15 | /**
16 | * 问答模式,流式反馈
17 | *
18 | * @param chatCompletionRequest 请求信息
19 | * @param eventSourceListener 实现监听;通过监听的 onEvent 方法接收数据
20 | * @return 应答结果
21 | * @throws Exception 异常
22 | */
23 | EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception;
24 |
25 | /**
26 | * 问答模式,同步反馈 —— 用流式转化 Future
27 | *
28 | * @param chatCompletionRequest 请求信息
29 | * @return 应答结果
30 | */
31 | CompletableFuture completions(ChatCompletionRequest chatCompletionRequest) throws InterruptedException;
32 |
33 | /**
34 | * 同步应答接口
35 | *
36 | * @param chatCompletionRequest 请求信息
37 | * @return ChatCompletionSyncResponse
38 | * @throws IOException 异常
39 | */
40 | ChatCompletionSyncResponse completionsSync(ChatCompletionRequest chatCompletionRequest) throws Exception;
41 |
42 | /**
43 | * 图片生成接口
44 | *
45 | * @param request 请求信息
46 | * @return 应答结果
47 | */
48 | ImageCompletionResponse genImages(ImageCompletionRequest request) throws Exception;
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/executor/aigc/GLMExecutor.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.executor.aigc;
2 |
3 | import cn.bugstack.chatglm.IOpenAiApi;
4 | import cn.bugstack.chatglm.executor.Executor;
5 | import cn.bugstack.chatglm.executor.result.ResultHandler;
6 | import cn.bugstack.chatglm.model.*;
7 | import cn.bugstack.chatglm.session.Configuration;
8 | import com.alibaba.fastjson.JSON;
9 | import com.fasterxml.jackson.databind.ObjectMapper;
10 | import lombok.extern.slf4j.Slf4j;
11 | import okhttp3.*;
12 | import okhttp3.sse.EventSource;
13 | import okhttp3.sse.EventSourceListener;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.io.IOException;
17 | import java.util.List;
18 | import java.util.concurrent.CompletableFuture;
19 |
20 | /**
21 | * 智谱AI 通用大模型 glm-3-turbo、glm-4 执行器
22 | * https://open.bigmodel.cn/dev/api
23 | */
24 | @Slf4j
25 | public class GLMExecutor implements Executor, ResultHandler {
26 |
27 | /**
28 | * OpenAi 接口
29 | */
30 | private final Configuration configuration;
31 | /**
32 | * 工厂事件
33 | */
34 | private final EventSource.Factory factory;
35 | /**
36 | * 统一接口
37 | */
38 | private IOpenAiApi openAiApi;
39 |
40 | private OkHttpClient okHttpClient;
41 |
42 | public GLMExecutor(Configuration configuration) {
43 | this.configuration = configuration;
44 | this.factory = configuration.createRequestFactory();
45 | this.openAiApi = configuration.getOpenAiApi();
46 | this.okHttpClient = configuration.getOkHttpClient();
47 | }
48 |
49 | @Override
50 | public EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception {
51 | // 构建请求信息
52 | Request request = new Request.Builder()
53 | .url(configuration.getApiHost().concat(IOpenAiApi.v4))
54 | .post(RequestBody.create(MediaType.parse(Configuration.JSON_CONTENT_TYPE), chatCompletionRequest.toString()))
55 | .build();
56 |
57 | // 返回事件结果
58 | return factory.newEventSource(request, chatCompletionRequest.getIsCompatible() ? eventSourceListener(eventSourceListener) : eventSourceListener);
59 | }
60 |
61 | @Override
62 | public CompletableFuture completions(ChatCompletionRequest chatCompletionRequest) throws InterruptedException {
63 | // 用于执行异步任务并获取结果
64 | CompletableFuture future = new CompletableFuture<>();
65 | StringBuffer dataBuffer = new StringBuffer();
66 |
67 | // 构建请求信息
68 | Request request = new Request.Builder()
69 | .url(configuration.getApiHost().concat(IOpenAiApi.v4))
70 | .post(RequestBody.create(MediaType.parse("application/json;charset=utf-8"), chatCompletionRequest.toString()))
71 | .build();
72 |
73 | factory.newEventSource(request, new EventSourceListener() {
74 | @Override
75 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
76 | if ("[DONE]".equals(data)) {
77 | log.info("[输出结束] Tokens {}", JSON.toJSONString(data));
78 | return;
79 | }
80 |
81 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
82 | log.info("测试结果:{}", JSON.toJSONString(response));
83 | List choices = response.getChoices();
84 | for (ChatCompletionResponse.Choice choice : choices) {
85 | if (!"stop".equals(choice.getFinishReason())) {
86 | dataBuffer.append(choice.getDelta().getContent());
87 | }
88 | }
89 |
90 | }
91 |
92 | @Override
93 | public void onClosed(EventSource eventSource) {
94 | future.complete(dataBuffer.toString());
95 | }
96 |
97 | @Override
98 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
99 | future.completeExceptionally(new RuntimeException("Request closed before completion"));
100 | }
101 | });
102 |
103 | return future;
104 | }
105 |
106 | @Override
107 | public ChatCompletionSyncResponse completionsSync(ChatCompletionRequest chatCompletionRequest) throws Exception {
108 | // sync 同步请求,stream 为 false
109 | chatCompletionRequest.setStream(false);
110 | // 构建请求信息
111 | Request request = new Request.Builder()
112 | .url(configuration.getApiHost().concat(IOpenAiApi.v4))
113 | .post(RequestBody.create(MediaType.parse(Configuration.JSON_CONTENT_TYPE), chatCompletionRequest.toString()))
114 | .build();
115 | OkHttpClient okHttpClient = configuration.getOkHttpClient();
116 | Response response = okHttpClient.newCall(request).execute();
117 | if(!response.isSuccessful()){
118 | throw new RuntimeException("Request failed");
119 | }
120 | return JSON.parseObject(response.body().string(),ChatCompletionSyncResponse.class);
121 | }
122 |
123 | /**
124 | * 图片生成,注释的方式留作扩展使用
125 | *
126 | * Request request = new Request.Builder()
127 | * .url(configuration.getApiHost().concat(IOpenAiApi.cogview3))
128 | * .post(RequestBody.create(MediaType.parse(Configuration.JSON_CONTENT_TYPE), imageCompletionRequest.toString()))
129 | * .build();
130 | * // 请求结果
131 | * Call call = okHttpClient.newCall(request);
132 | * Response execute = call.execute();
133 | * ResponseBody body = execute.body();
134 | * if (execute.isSuccessful() && body != null) {
135 | * String responseBody = body.string();
136 | * ObjectMapper objectMapper = new ObjectMapper();
137 | * return objectMapper.readValue(responseBody, ImageCompletionResponse.class);
138 | * } else {
139 | * throw new IOException("Failed to get image response");
140 | * }
141 | * @param imageCompletionRequest 请求信息
142 | * @return
143 | * @throws Exception
144 | */
145 | @Override
146 | public ImageCompletionResponse genImages(ImageCompletionRequest imageCompletionRequest) throws Exception {
147 | return openAiApi.genImages(imageCompletionRequest).blockingGet();
148 | }
149 |
150 | @Override
151 | public EventSourceListener eventSourceListener(EventSourceListener eventSourceListener) {
152 | return new EventSourceListener() {
153 | @Override
154 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
155 | if ("[DONE]".equals(data)) {
156 | return;
157 | }
158 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
159 | if (response.getChoices() != null && 1 == response.getChoices().size() && "stop".equals(response.getChoices().get(0).getFinishReason())) {
160 | eventSourceListener.onEvent(eventSource, id, EventType.finish.getCode(), data);
161 | return;
162 | }
163 | eventSourceListener.onEvent(eventSource, id, EventType.add.getCode(), data);
164 | }
165 |
166 | @Override
167 | public void onClosed(EventSource eventSource) {
168 | eventSourceListener.onClosed(eventSource);
169 | }
170 |
171 | @Override
172 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
173 | eventSourceListener.onFailure(eventSource, t, response);
174 | }
175 | };
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/executor/aigc/GLMOldExecutor.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.executor.aigc;
2 |
3 | import cn.bugstack.chatglm.IOpenAiApi;
4 | import cn.bugstack.chatglm.executor.Executor;
5 | import cn.bugstack.chatglm.model.*;
6 | import cn.bugstack.chatglm.session.Configuration;
7 | import com.alibaba.fastjson.JSON;
8 | import okhttp3.*;
9 | import okhttp3.sse.EventSource;
10 | import okhttp3.sse.EventSourceListener;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | import java.io.IOException;
14 | import java.util.concurrent.CompletableFuture;
15 |
16 | /**
17 | * 智谱AI旧版接口模型; chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
18 | * https://open.bigmodel.cn/dev/api
19 | */
20 | public class GLMOldExecutor implements Executor {
21 |
22 | /**
23 | * OpenAi 接口
24 | */
25 | private final Configuration configuration;
26 | /**
27 | * 工厂事件
28 | */
29 | private final EventSource.Factory factory;
30 |
31 | public GLMOldExecutor(Configuration configuration) {
32 | this.configuration = configuration;
33 | this.factory = configuration.createRequestFactory();
34 | }
35 |
36 | @Override
37 | public EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception {
38 | // 构建请求信息
39 | Request request = new Request.Builder()
40 | .url(configuration.getApiHost().concat(IOpenAiApi.v3_completions).replace("{model}", chatCompletionRequest.getModel().getCode()))
41 | .post(RequestBody.create(MediaType.parse("application/json"), chatCompletionRequest.toString()))
42 | .build();
43 |
44 | // 返回事件结果
45 | return factory.newEventSource(request, eventSourceListener);
46 | }
47 |
48 | @Override
49 | public CompletableFuture completions(ChatCompletionRequest chatCompletionRequest) throws InterruptedException {
50 | // 用于执行异步任务并获取结果
51 | CompletableFuture future = new CompletableFuture<>();
52 | StringBuffer dataBuffer = new StringBuffer();
53 |
54 | // 构建请求信息
55 | Request request = new Request.Builder()
56 | .url(configuration.getApiHost().concat(IOpenAiApi.v3_completions).replace("{model}", chatCompletionRequest.getModel().getCode()))
57 | .post(RequestBody.create(MediaType.parse("application/json"), chatCompletionRequest.toString()))
58 | .build();
59 |
60 | // 异步响应请求
61 | factory.newEventSource(request, new EventSourceListener() {
62 | @Override
63 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
64 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
65 | // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断
66 | if (EventType.add.getCode().equals(type)) {
67 | dataBuffer.append(response.getData());
68 | } else if (EventType.finish.getCode().equals(type)) {
69 | future.complete(dataBuffer.toString());
70 | }
71 | }
72 |
73 | @Override
74 | public void onClosed(EventSource eventSource) {
75 | future.completeExceptionally(new RuntimeException("Request closed before completion"));
76 | }
77 |
78 | @Override
79 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
80 | future.completeExceptionally(new RuntimeException("Request closed before completion"));
81 | }
82 | });
83 |
84 | return future;
85 | }
86 |
87 | @Override
88 | public ChatCompletionSyncResponse completionsSync(ChatCompletionRequest chatCompletionRequest) throws IOException {
89 | // 构建请求信息
90 | Request request = new Request.Builder()
91 | .url(configuration.getApiHost().concat(IOpenAiApi.v3_completions_sync).replace("{model}", chatCompletionRequest.getModel().getCode()))
92 | .header("Accept",Configuration.APPLICATION_JSON)
93 | .post(RequestBody.create(MediaType.parse("application/json"), chatCompletionRequest.toString()))
94 | .build();
95 | OkHttpClient okHttpClient = configuration.getOkHttpClient();
96 | Response response = okHttpClient.newCall(request).execute();
97 | if(!response.isSuccessful()){
98 | new RuntimeException("Request failed");
99 | }
100 | return JSON.parseObject(response.body().string(),ChatCompletionSyncResponse.class);
101 | }
102 |
103 | @Override
104 | public ImageCompletionResponse genImages(ImageCompletionRequest request) {
105 | throw new RuntimeException("旧版无图片生成接口");
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/executor/result/ResultHandler.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.executor.result;
2 |
3 | import okhttp3.sse.EventSourceListener;
4 |
5 | /**
6 | * 结果封装器
7 | */
8 | public interface ResultHandler {
9 |
10 | EventSourceListener eventSourceListener(EventSourceListener eventSourceListener);
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/interceptor/OpenAiHTTPInterceptor.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.interceptor;
2 |
3 | import cn.bugstack.chatglm.session.Configuration;
4 | import cn.bugstack.chatglm.utils.BearerTokenUtils;
5 | import okhttp3.Interceptor;
6 | import okhttp3.Request;
7 | import okhttp3.Response;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.io.IOException;
11 |
12 | /**
13 | * @author 小傅哥,微信:fustack
14 | * @description 接口拦截器
15 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
16 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
17 | */
18 | public class OpenAiHTTPInterceptor implements Interceptor {
19 |
20 | /**
21 | * 智普Ai,Jwt加密Token
22 | */
23 | private final Configuration configuration;
24 |
25 | public OpenAiHTTPInterceptor(Configuration configuration) {
26 | this.configuration = configuration;
27 | }
28 |
29 | @Override
30 | public @NotNull Response intercept(Chain chain) throws IOException {
31 | // 1. 获取原始 Request
32 | Request original = chain.request();
33 | // 2. 构建请求
34 | Request request = original.newBuilder()
35 | .url(original.url())
36 | .header("Authorization", "Bearer " + BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret()))
37 | .header("Content-Type", Configuration.JSON_CONTENT_TYPE)
38 | .header("User-Agent", Configuration.DEFAULT_USER_AGENT)
39 | // .header("Accept", null != original.header("Accept") ? original.header("Accept") : Configuration.SSE_CONTENT_TYPE)
40 | .method(original.method(), original.body())
41 | .build();
42 |
43 | // 3. 返回执行结果
44 | return chain.proceed(request);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/ChatCompletionRequest.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.fasterxml.jackson.annotation.JsonInclude;
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 | import com.fasterxml.jackson.core.JsonProcessingException;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import lombok.*;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | /**
16 | * @author 小傅哥,微信:fustack
17 | * @description 请求参数
18 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
19 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
20 | */
21 | @Data
22 | @Slf4j
23 | @JsonInclude(JsonInclude.Include.NON_NULL)
24 | @Builder
25 | @NoArgsConstructor
26 | @AllArgsConstructor
27 | public class ChatCompletionRequest {
28 |
29 | /**
30 | * 是否对返回结果数据做兼容,24年1月发布的 GLM_3_5_TURBO、GLM_4 模型,与之前的模型在返回结果上有差异。开启 true 可以做兼容。
31 | */
32 | private Boolean isCompatible = true;
33 | /**
34 | * 模型
35 | */
36 | private Model model = Model.GLM_3_5_TURBO;
37 | /**
38 | * 请求参数 {"role": "user", "content": "你好"}
39 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
40 | */
41 | private List messages;
42 | /**
43 | * 请求ID
44 | */
45 | @JsonProperty("request_id")
46 | private String requestId = String.format("xfg-%d", System.currentTimeMillis());
47 | /**
48 | * do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效
49 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
50 | */
51 | @JsonProperty("do_sample")
52 | private Boolean doSample = true;
53 | /**
54 | * 使用同步调用时,此参数应当设置为 Fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。
55 | * 如果设置为 True,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data: [DONE]消息。
56 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
57 | */
58 | private Boolean stream = true;
59 | /**
60 | * 控制温度【随机性】
61 | */
62 | private float temperature = 0.9f;
63 | /**
64 | * 多样性控制;
65 | */
66 | @JsonProperty("top_p")
67 | private float topP = 0.7f;
68 | /**
69 | * 模型输出最大tokens
70 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
71 | */
72 | @JsonProperty("max_tokens")
73 | private Integer maxTokens = 1024;
74 | /**
75 | * 模型在遇到stop所制定的字符时将停止生成,目前仅支持单个停止词,格式为["stop_word1"]
76 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
77 | */
78 | private List stop;
79 | /**
80 | * 可供模型调用的工具列表,tools字段会计算 tokens ,同样受到tokens长度的限制
81 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
82 | */
83 | private List tools;
84 | /**
85 | * 用于控制模型是如何选择要调用的函数,仅当工具类型为function时补充。默认为auto,当前仅支持auto。
86 | * 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
87 | */
88 | @JsonProperty("tool_choice")
89 | private String toolChoice = "auto";
90 | /**
91 | * 输入给模型的会话信息
92 | * 用户输入的内容;role=user
93 | * 挟带历史的内容;role=assistant
94 | */
95 | private List prompt;
96 | /**
97 | * 智普AI sse 固定参数 incremental = true 【增量返回】
98 | */
99 | private boolean incremental = true;
100 | /**
101 | * sseformat, 用于兼容解决sse增量模式okhttpsse截取data:后面空格问题, [data: hello]。只在增量模式下使用sseFormat。
102 | */
103 | private String sseFormat = "data";
104 |
105 | @Data
106 | @NoArgsConstructor
107 | @AllArgsConstructor
108 | public static class Prompt {
109 | private String role;
110 | private String content;
111 |
112 | public static PromptBuilder builder() {
113 | return new PromptBuilder();
114 | }
115 |
116 | public static class PromptBuilder {
117 | private String role;
118 | private String content;
119 |
120 | PromptBuilder() {
121 | }
122 |
123 | public PromptBuilder role(String role) {
124 | this.role = role;
125 | return this;
126 | }
127 |
128 | public PromptBuilder content(String content) {
129 | this.content = content;
130 | return this;
131 | }
132 |
133 | public PromptBuilder content(Content content) {
134 | this.content = JSON.toJSONString(content);
135 | return this;
136 | }
137 |
138 | public Prompt build() {
139 | return new Prompt(this.role, this.content);
140 | }
141 |
142 | public String toString() {
143 | return "ChatCompletionRequest.Prompt.PromptBuilder(role=" + this.role + ", content=" + this.content + ")";
144 | }
145 | }
146 |
147 | @Data
148 | @Builder
149 | @NoArgsConstructor
150 | @AllArgsConstructor
151 | public static class Content {
152 | private String type = Type.text.code;
153 | private String text;
154 | @JsonProperty("image_url")
155 | private ImageUrl imageUrl;
156 |
157 | @Getter
158 | @AllArgsConstructor
159 | public static enum Type {
160 | text("text", "文本"),
161 | image_url("image_url", "图"),
162 | ;
163 | private final String code;
164 | private final String info;
165 | }
166 |
167 | @Data
168 | @Builder
169 | @NoArgsConstructor
170 | @AllArgsConstructor
171 | public static class ImageUrl {
172 | private String url;
173 | }
174 |
175 | }
176 | }
177 |
178 | @Data
179 | @Builder
180 | @NoArgsConstructor
181 | @AllArgsConstructor
182 | public static class Tool {
183 | private Type type;
184 | private Function function;
185 | private Retrieval retrieval;
186 | @JsonProperty("web_search")
187 | private WebSearch webSearch;
188 |
189 | public String getType() {
190 | return type.code;
191 | }
192 |
193 | @Getter
194 | @AllArgsConstructor
195 | public static enum Type {
196 | function("function", "函数功能"),
197 | retrieval("retrieval", "知识库"),
198 | web_search("web_search", "联网"),
199 | ;
200 | private final String code;
201 | private final String info;
202 | }
203 |
204 | @Data
205 | @Builder
206 | @NoArgsConstructor
207 | @AllArgsConstructor
208 | public static class Function {
209 | // 函数名称,只能包含a-z,A-Z,0-9,下划线和中横线。最大长度限制为64
210 | private String name;
211 | // 用于描述函数功能。模型会根据这段描述决定函数调用方式。
212 | private String description;
213 | // parameter 字段需要传入一个 Json Schema 对象,以准确地定义函数所接受的参数。https://open.bigmodel.cn/dev/api#glm-3-turbo
214 | private Object parameters;
215 | }
216 |
217 | @Data
218 | @Builder
219 | @NoArgsConstructor
220 | @AllArgsConstructor
221 | public static class Retrieval {
222 | // 当涉及到知识库ID时,请前往开放平台的知识库模块进行创建或获取。
223 | @JsonProperty("knowledge_id")
224 | private String knowledgeId;
225 | // 请求模型时的知识库模板,默认模板:
226 | @JsonProperty("prompt_template")
227 | private String promptTemplate = "\"\"\"\n" +
228 | "{{ knowledge}}\n" +
229 | "\"\"\"\n" +
230 | "中找问题\n" +
231 | "\"\"\"\n" +
232 | "{{question}}\n" +
233 | "\"\"\"";
234 | }
235 |
236 | // 仅当工具类型为web_search时补充,如果tools中存在类型retrieval,此时web_search不生效。
237 | @Data
238 | @Builder
239 | @NoArgsConstructor
240 | @AllArgsConstructor
241 | public static class WebSearch {
242 | // 是否启用搜索,默认启用搜索 enable = true/false
243 | private Boolean enable = true;
244 | // 强制搜索自定义关键内容,此时模型会根据自定义搜索关键内容返回的结果作为背景知识来回答用户发起的对话。
245 | @JsonProperty("search_query")
246 | private String searchQuery;
247 | }
248 |
249 |
250 | }
251 |
252 | @Override
253 | public String toString() {
254 | try {
255 | // 24年1月发布新模型后调整
256 | if (!Model.isOldModel(this.model)) {
257 | Map paramsMap = new HashMap<>();
258 | paramsMap.put("model", this.model.getCode());
259 | if (null == this.messages && null == this.prompt) {
260 | throw new RuntimeException("One of messages or prompt must not be empty!");
261 | }
262 | paramsMap.put("messages", this.messages != null ? this.messages : this.prompt);
263 | if (null != this.requestId) {
264 | paramsMap.put("request_id", this.requestId);
265 | }
266 | if (null != this.doSample) {
267 | paramsMap.put("do_sample", this.doSample);
268 | }
269 | paramsMap.put("stream", this.stream);
270 | paramsMap.put("temperature", this.temperature);
271 | paramsMap.put("top_p", this.topP);
272 | paramsMap.put("max_tokens", this.maxTokens);
273 | if (null != this.stop && this.stop.size() > 0) {
274 | paramsMap.put("stop", this.stop);
275 | }
276 | if (null != this.tools && this.tools.size() > 0) {
277 | paramsMap.put("tools", this.tools);
278 | paramsMap.put("tool_choice", this.toolChoice);
279 | }
280 | return new ObjectMapper().writeValueAsString(paramsMap);
281 | }
282 |
283 | // 默认
284 | Map paramsMap = new HashMap<>();
285 | paramsMap.put("request_id", requestId);
286 | paramsMap.put("prompt", prompt);
287 | paramsMap.put("incremental", incremental);
288 | paramsMap.put("temperature", temperature);
289 | paramsMap.put("top_p", topP);
290 | paramsMap.put("sseFormat", sseFormat);
291 | return new ObjectMapper().writeValueAsString(paramsMap);
292 | } catch (JsonProcessingException e) {
293 | throw new RuntimeException(e);
294 | }
295 | }
296 |
297 | }
298 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/ChatCompletionResponse.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import com.alibaba.fastjson2.JSON;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * @author 小傅哥,微信:fustack
14 | * @description 返回结果
15 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
16 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
17 | */
18 | @Data
19 | public class ChatCompletionResponse {
20 |
21 | // 旧版获得的数据方式
22 | private String data;
23 | private String meta;
24 |
25 | // 24年1月发布的 GLM_3_5_TURBO、GLM_4 模型时新增
26 | private String id;
27 | private Long created;
28 | private String model;
29 | private List choices;
30 | private Usage usage;
31 |
32 | // 封装 setChoices 对 data 属性赋值,兼容旧版使用方式
33 | public void setChoices(List choices) {
34 | this.choices = choices;
35 | for (Choice choice : choices) {
36 | if ("stop".equals(choice.finishReason)) {
37 | continue;
38 | }
39 | if (null == this.data) {
40 | this.data = "";
41 | }
42 | this.data = this.data.concat(choice.getDelta().getContent());
43 | }
44 | }
45 |
46 | // 封装 setChoices 对 meta 属性赋值,兼容旧版使用方式
47 | public void setUsage(Usage usage) {
48 | this.usage = usage;
49 | if (null != usage) {
50 | this.meta = JSON.toJSONString(Meta.builder().usage(usage).build());
51 | }
52 | }
53 |
54 | @Data
55 | public static class Choice {
56 | private Long index;
57 | @JsonProperty("finish_reason")
58 | private String finishReason;
59 | private Delta delta;
60 | }
61 |
62 | @Data
63 | public static class Delta {
64 | private String role;
65 | private String content;
66 | }
67 |
68 | @Data
69 | @Builder
70 | @NoArgsConstructor
71 | @AllArgsConstructor
72 | public static class Meta {
73 | private String task_status;
74 | private Usage usage;
75 | private String task_id;
76 | private String request_id;
77 | }
78 |
79 | @Data
80 | public static class Usage {
81 | private int completion_tokens;
82 | private int prompt_tokens;
83 | private int total_tokens;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/ChatCompletionSyncResponse.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * 同步调用响应
9 | *
10 | * @author max
11 | * @date 2023/12/14 15:41
12 | */
13 | @Data
14 | public class ChatCompletionSyncResponse {
15 |
16 | private Integer code;
17 | private String msg;
18 | private Boolean success;
19 | private ChatGLMData data;
20 |
21 | // 24年1月发布模型新增字段 GLM3、GLM4
22 | private String task_status;
23 | private List choices;
24 |
25 | public void setChoices(List choices) {
26 | this.choices = choices;
27 | this.data = new ChatGLMData();
28 | this.data.setChoices(choices);
29 | }
30 |
31 |
32 | @Data
33 | public static class ChatGLMData {
34 | private List choices;
35 | private String task_status;
36 | private Usage usage;
37 | private String task_id;
38 | private String request_id;
39 | }
40 |
41 | @Data
42 | public static class Usage {
43 | private int completion_tokens;
44 | private int prompt_tokens;
45 | private int total_tokens;
46 | }
47 |
48 | @Data
49 | public static class Choice {
50 |
51 | private String role;
52 | private String content;
53 |
54 | // 24年1月发布模型新增字段 GLM3、GLM4
55 | private String finish_reason;
56 | private int index;
57 | private Message message;
58 | }
59 |
60 | @Data
61 | public static class Message {
62 | private String role;
63 | private String content;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/EventType.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | /**
7 | * @author 小傅哥,微信:fustack
8 | * @description 消息类型 chatglm_lite
9 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
10 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
11 | */
12 | @Getter
13 | @AllArgsConstructor
14 | public enum EventType {
15 |
16 | add("add", "增量"),
17 | finish("finish", "结束"),
18 | error("error", "错误"),
19 | interrupted("interrupted", "中断"),
20 |
21 | ;
22 | private final String code;
23 | private final String info;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/ImageCompletionRequest.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.fasterxml.jackson.core.JsonProcessingException;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import lombok.AllArgsConstructor;
7 | import lombok.Builder;
8 | import lombok.Data;
9 | import lombok.NoArgsConstructor;
10 |
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | /**
15 | * CogView 根据用户的文字描述生成图像,使用同步调用方式请求接口
16 | */
17 | @Data
18 | @Builder
19 | @NoArgsConstructor
20 | @AllArgsConstructor
21 | @JsonInclude(JsonInclude.Include.NON_NULL)
22 | public class ImageCompletionRequest {
23 |
24 | /**
25 | * 模型;24年1月发布了 cogview-3 生成图片模型
26 | */
27 | private Model model = Model.COGVIEW_3;
28 |
29 | /**
30 | * 所需图像的文本描述
31 | */
32 | private String prompt;
33 |
34 | public String getModel() {
35 | return model.getCode();
36 | }
37 |
38 | public Model getModelEnum() {
39 | return model;
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | Map paramsMap = new HashMap<>();
45 | paramsMap.put("model", model.getCode());
46 | paramsMap.put("prompt", prompt);
47 | try {
48 | return new ObjectMapper().writeValueAsString(paramsMap);
49 | } catch (JsonProcessingException e) {
50 | throw new RuntimeException(e);
51 | }
52 | }
53 |
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/ImageCompletionResponse.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * CogView 根据用户的文字描述生成图像,使用同步调用方式请求接口
9 | */
10 | @Data
11 | public class ImageCompletionResponse {
12 |
13 | /**
14 | * 请求创建时间,是以秒为单位的Unix时间戳。
15 | */
16 | private Long created;
17 |
18 | private List data;
19 |
20 | @Data
21 | public static class Image{
22 | private String url;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/Model.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | /**
7 | * @author 小傅哥,微信:fustack
8 | * @description 会话模型
9 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
10 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
11 | */
12 | @Getter
13 | @AllArgsConstructor
14 | public enum Model {
15 |
16 | @Deprecated
17 | CHATGLM_6B_SSE("chatGLM_6b_SSE", "ChatGLM-6B 测试模型"),
18 | @Deprecated
19 | CHATGLM_LITE("chatglm_lite", "轻量版模型,适用对推理速度和成本敏感的场景"),
20 | @Deprecated
21 | CHATGLM_LITE_32K("chatglm_lite_32k", "标准版模型,适用兼顾效果和成本的场景"),
22 | @Deprecated
23 | CHATGLM_STD("chatglm_std", "适用于对知识量、推理能力、创造力要求较高的场景"),
24 | @Deprecated
25 | CHATGLM_PRO("chatglm_pro", "适用于对知识量、推理能力、创造力要求较高的场景"),
26 | /**
27 | * 智谱AI 23年06月发布
28 | */
29 | CHATGLM_TURBO("chatglm_turbo", "适用于对知识量、推理能力、创造力要求较高的场景"),
30 | /**
31 | * 智谱AI 24年01月发布
32 | */
33 | GLM_3_5_TURBO("glm-3-turbo", "适用于对知识量、推理能力、创造力要求较高的场景"),
34 | GLM_4("glm-4", "适用于复杂的对话交互和深度内容创作设计的场景"),
35 | GLM_4V("glm-4v", "根据输入的自然语言指令和图像信息完成任务,推荐使用 SSE 或同步调用方式请求接口"),
36 | GLM_4_Plus("glm-4-plus", "高智能旗舰: 性能全面提升,长文本和复杂任务能力显著增强"),
37 | GLM_4_0520("glm-4-0520", "高智能模型:适用于处理高度复杂和多样化的任务"),
38 | GLM_4_Lng("glm-4-long", "超长输入:专为处理超长文本和记忆型任务设计"),
39 | GLM_4_AirX("glm-4-airx", "极速推理:具有超快的推理速度和强大的推理效果"),
40 | GLM_4_Air("glm-4-air", "高性价比:推理能力和价格之间最平衡的模型"),
41 | GLM_4_FlashX("glm-4-flashx", "高速低价:Flash增强版本,超快推理速度。"),
42 | GLM_4_Flash("glm-4-flash", "免费调用:智谱AI首个免费API,零成本调用大模型。"),
43 | GLM_4_AllTools("glm-4-alltools", "Agent模型:自主规划和执行复杂任务"),
44 |
45 | COGVIEW_3("cogview-3", "根据用户的文字描述生成图像,使用同步调用方式请求接口"),
46 | ;
47 |
48 | private final String code;
49 | private final String info;
50 |
51 | public static boolean isOldModel(Model model) {
52 | return CHATGLM_6B_SSE.equals(model) || CHATGLM_LITE.equals(model) || CHATGLM_LITE_32K.equals(model) || CHATGLM_STD.equals(model) || CHATGLM_PRO.equals(model) || CHATGLM_TURBO.equals(model);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/model/Role.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | /**
7 | * @author 小傅哥,微信:fustack
8 | * @description 角色
9 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
10 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
11 | */
12 | @Getter
13 | @AllArgsConstructor
14 | public enum Role {
15 | /**
16 | * user 用户输入的内容,role位user
17 | */
18 | user("user"),
19 | /**
20 | * 模型生成的内容,role位assistant
21 | */
22 | assistant("assistant"),
23 |
24 | /**
25 | * 系统
26 | */
27 | system("system"),
28 |
29 | ;
30 | private final String code;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/session/Configuration.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.session;
2 |
3 | import cn.bugstack.chatglm.IOpenAiApi;
4 | import cn.bugstack.chatglm.executor.Executor;
5 | import cn.bugstack.chatglm.executor.aigc.GLMOldExecutor;
6 | import cn.bugstack.chatglm.executor.aigc.GLMExecutor;
7 | import cn.bugstack.chatglm.model.Model;
8 | import lombok.*;
9 | import lombok.extern.slf4j.Slf4j;
10 | import okhttp3.OkHttpClient;
11 | import okhttp3.logging.HttpLoggingInterceptor;
12 | import okhttp3.sse.EventSource;
13 | import okhttp3.sse.EventSources;
14 |
15 | import java.util.HashMap;
16 |
17 | /**
18 | * @author 小傅哥,微信:fustack
19 | * @description 配置文件
20 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
21 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
22 | */
23 | @Slf4j
24 | @NoArgsConstructor
25 | @AllArgsConstructor
26 | public class Configuration {
27 |
28 | // 智普Ai ChatGlM 请求地址
29 | @Getter
30 | @Setter
31 | private String apiHost = "https://open.bigmodel.cn/";
32 |
33 | // 智普Ai https://open.bigmodel.cn/usercenter/apikeys - apiSecretKey = {apiKey}.{apiSecret}
34 | private String apiSecretKey;
35 |
36 | public void setApiSecretKey(String apiSecretKey) {
37 | this.apiSecretKey = apiSecretKey;
38 | String[] arrStr = apiSecretKey.split("\\.");
39 | if (arrStr.length != 2) {
40 | throw new RuntimeException("invalid apiSecretKey");
41 | }
42 | this.apiKey = arrStr[0];
43 | this.apiSecret = arrStr[1];
44 | }
45 |
46 | @Getter
47 | private String apiKey;
48 | @Getter
49 | private String apiSecret;
50 |
51 | // Api 服务
52 | @Setter
53 | @Getter
54 | private IOpenAiApi openAiApi;
55 |
56 | @Getter
57 | @Setter
58 | private OkHttpClient okHttpClient;
59 |
60 | public EventSource.Factory createRequestFactory() {
61 | return EventSources.createFactory(okHttpClient);
62 | }
63 |
64 | // OkHttp 配置信息
65 | @Setter
66 | @Getter
67 | private HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.HEADERS;
68 | @Setter
69 | @Getter
70 | private long connectTimeout = 450;
71 | @Setter
72 | @Getter
73 | private long writeTimeout = 450;
74 | @Setter
75 | @Getter
76 | private long readTimeout = 450;
77 |
78 | private HashMap executorGroup;
79 |
80 | // http keywords
81 | public static final String SSE_CONTENT_TYPE = "text/event-stream";
82 | public static final String DEFAULT_USER_AGENT = "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)";
83 | public static final String APPLICATION_JSON = "application/json";
84 | public static final String JSON_CONTENT_TYPE = APPLICATION_JSON + "; charset=utf-8";
85 |
86 | public HashMap newExecutorGroup() {
87 | this.executorGroup = new HashMap<>();
88 | // 旧版模型,兼容
89 | Executor glmOldExecutor = new GLMOldExecutor(this);
90 | this.executorGroup.put(Model.CHATGLM_6B_SSE, glmOldExecutor);
91 | this.executorGroup.put(Model.CHATGLM_LITE, glmOldExecutor);
92 | this.executorGroup.put(Model.CHATGLM_LITE_32K, glmOldExecutor);
93 | this.executorGroup.put(Model.CHATGLM_STD, glmOldExecutor);
94 | this.executorGroup.put(Model.CHATGLM_PRO, glmOldExecutor);
95 | this.executorGroup.put(Model.CHATGLM_TURBO, glmOldExecutor);
96 | // 新版模型,配置
97 | Executor glmExecutor = new GLMExecutor(this);
98 | this.executorGroup.put(Model.GLM_3_5_TURBO, glmExecutor);
99 | this.executorGroup.put(Model.GLM_4, glmExecutor);
100 | this.executorGroup.put(Model.GLM_4V, glmExecutor);
101 | this.executorGroup.put(Model.GLM_4_Plus, glmExecutor);
102 | this.executorGroup.put(Model.GLM_4_0520, glmExecutor);
103 | this.executorGroup.put(Model.GLM_4_Lng, glmExecutor);
104 | this.executorGroup.put(Model.GLM_4_AirX, glmExecutor);
105 | this.executorGroup.put(Model.GLM_4_Air, glmExecutor);
106 | this.executorGroup.put(Model.GLM_4_FlashX, glmExecutor);
107 | this.executorGroup.put(Model.GLM_4_Flash, glmExecutor);
108 | this.executorGroup.put(Model.GLM_4_AllTools, glmExecutor);
109 | this.executorGroup.put(Model.COGVIEW_3, glmExecutor);
110 | return this.executorGroup;
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/session/OpenAiSession.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.session;
2 |
3 | import cn.bugstack.chatglm.model.*;
4 | import com.fasterxml.jackson.core.JsonProcessingException;
5 | import okhttp3.sse.EventSource;
6 | import okhttp3.sse.EventSourceListener;
7 |
8 | import java.io.IOException;
9 | import java.util.concurrent.CompletableFuture;
10 |
11 | /**
12 | * @author 小傅哥,微信:fustack
13 | * @description 会话服务接口
14 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
15 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
16 | */
17 | public interface OpenAiSession {
18 |
19 | EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception;
20 |
21 | CompletableFuture completions(ChatCompletionRequest chatCompletionRequest) throws Exception;
22 |
23 | ChatCompletionSyncResponse completionsSync(ChatCompletionRequest chatCompletionRequest) throws Exception;
24 |
25 | ImageCompletionResponse genImages(ImageCompletionRequest imageCompletionRequest) throws Exception;
26 |
27 | Configuration configuration();
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/session/OpenAiSessionFactory.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.session;
2 |
3 | /**
4 | * @author 小傅哥,微信:fustack
5 | * @description 工厂接口
6 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
7 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
8 | */
9 | public interface OpenAiSessionFactory {
10 |
11 | OpenAiSession openSession();
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/session/defaults/DefaultOpenAiSession.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.session.defaults;
2 |
3 | import cn.bugstack.chatglm.executor.Executor;
4 | import cn.bugstack.chatglm.model.*;
5 | import cn.bugstack.chatglm.session.Configuration;
6 | import cn.bugstack.chatglm.session.OpenAiSession;
7 | import lombok.extern.slf4j.Slf4j;
8 | import okhttp3.sse.EventSource;
9 | import okhttp3.sse.EventSourceListener;
10 |
11 | import java.io.IOException;
12 | import java.util.Map;
13 | import java.util.concurrent.CompletableFuture;
14 |
15 | /**
16 | * @author 小傅哥,微信:fustack
17 | * @description 会话服务
18 | * @github https://github.com/fuzhengwei/chatglm-sdk-java/chatglm-sdk-java
19 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
20 | */
21 | @Slf4j
22 | public class DefaultOpenAiSession implements OpenAiSession {
23 |
24 | private final Configuration configuration;
25 | private final Map executorGroup;
26 |
27 | public DefaultOpenAiSession(Configuration configuration, Map executorGroup) {
28 | this.configuration = configuration;
29 | this.executorGroup = executorGroup;
30 | }
31 | @Override
32 | public EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception {
33 | Executor executor = executorGroup.get(chatCompletionRequest.getModel());
34 | if (null == executor) throw new RuntimeException(chatCompletionRequest.getModel() + " 模型执行器尚未实现!");
35 | return executor.completions(chatCompletionRequest, eventSourceListener);
36 | }
37 |
38 | @Override
39 | public CompletableFuture completions(ChatCompletionRequest chatCompletionRequest) throws Exception {
40 | Executor executor = executorGroup.get(chatCompletionRequest.getModel());
41 | if (null == executor) throw new RuntimeException(chatCompletionRequest.getModel() + " 模型执行器尚未实现!");
42 | return executor.completions(chatCompletionRequest);
43 | }
44 |
45 | @Override
46 | public ChatCompletionSyncResponse completionsSync(ChatCompletionRequest chatCompletionRequest) throws Exception {
47 | Executor executor = executorGroup.get(chatCompletionRequest.getModel());
48 | if (null == executor) throw new RuntimeException(chatCompletionRequest.getModel() + " 模型执行器尚未实现!");
49 | return executor.completionsSync(chatCompletionRequest);
50 | }
51 |
52 | @Override
53 | public ImageCompletionResponse genImages(ImageCompletionRequest imageCompletionRequest) throws Exception {
54 | Executor executor = executorGroup.get(imageCompletionRequest.getModelEnum());
55 | if (null == executor) throw new RuntimeException(imageCompletionRequest.getModel() + " 模型执行器尚未实现!");
56 | return executor.genImages(imageCompletionRequest);
57 | }
58 |
59 | @Override
60 | public Configuration configuration() {
61 | return configuration;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/session/defaults/DefaultOpenAiSessionFactory.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.session.defaults;
2 |
3 | import cn.bugstack.chatglm.IOpenAiApi;
4 | import cn.bugstack.chatglm.executor.Executor;
5 | import cn.bugstack.chatglm.interceptor.OpenAiHTTPInterceptor;
6 | import cn.bugstack.chatglm.model.Model;
7 | import cn.bugstack.chatglm.session.Configuration;
8 | import cn.bugstack.chatglm.session.OpenAiSession;
9 | import cn.bugstack.chatglm.session.OpenAiSessionFactory;
10 | import okhttp3.OkHttpClient;
11 | import okhttp3.logging.HttpLoggingInterceptor;
12 | import retrofit2.Retrofit;
13 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
14 | import retrofit2.converter.jackson.JacksonConverterFactory;
15 |
16 | import java.util.HashMap;
17 | import java.util.concurrent.TimeUnit;
18 |
19 | /**
20 | * @author 小傅哥,微信:fustack
21 | * @description 会话工厂
22 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
23 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
24 | */
25 | public class DefaultOpenAiSessionFactory implements OpenAiSessionFactory {
26 |
27 | private final Configuration configuration;
28 |
29 | public DefaultOpenAiSessionFactory(Configuration configuration) {
30 | this.configuration = configuration;
31 | }
32 |
33 | @Override
34 | public OpenAiSession openSession() {
35 | // 1. 日志配置
36 | HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
37 | httpLoggingInterceptor.setLevel(configuration.getLevel());
38 |
39 | // 2. 开启 Http 客户端
40 | OkHttpClient okHttpClient = new OkHttpClient
41 | .Builder()
42 | .addInterceptor(httpLoggingInterceptor)
43 | .addInterceptor(new OpenAiHTTPInterceptor(configuration))
44 | .connectTimeout(configuration.getConnectTimeout(), TimeUnit.SECONDS)
45 | .writeTimeout(configuration.getWriteTimeout(), TimeUnit.SECONDS)
46 | .readTimeout(configuration.getReadTimeout(), TimeUnit.SECONDS)
47 | .build();
48 |
49 | configuration.setOkHttpClient(okHttpClient);
50 |
51 | // 3. 创建 API 服务
52 | IOpenAiApi openAiApi = new Retrofit.Builder()
53 | .baseUrl(configuration.getApiHost())
54 | .client(okHttpClient)
55 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
56 | .addConverterFactory(JacksonConverterFactory.create())
57 | .build().create(IOpenAiApi.class);
58 |
59 | configuration.setOpenAiApi(openAiApi);
60 |
61 | // 4. 实例化执行器
62 | HashMap executorGroup = configuration.newExecutorGroup();
63 |
64 | return new DefaultOpenAiSession(configuration, executorGroup);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/cn/bugstack/chatglm/utils/BearerTokenUtils.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.utils;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.algorithms.Algorithm;
5 | import com.google.common.cache.Cache;
6 | import com.google.common.cache.CacheBuilder;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | import java.nio.charset.StandardCharsets;
10 | import java.util.Calendar;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | /**
16 | * @author 小傅哥,微信:fustack
17 | * @description 签名工具包;过期时间30分钟,缓存时间29分钟
18 | * @github https://github.com/fuzhengwei/chatglm-sdk-java
19 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
20 | */
21 | @Slf4j
22 | public class BearerTokenUtils {
23 |
24 | // 过期时间;默认30分钟
25 | private static final long expireMillis = 30 * 60 * 1000L;
26 |
27 | // 缓存服务
28 | public static Cache cache = CacheBuilder.newBuilder()
29 | .expireAfterWrite(expireMillis - (60 * 1000L), TimeUnit.MILLISECONDS)
30 | .build();
31 |
32 | /**
33 | * 对 ApiKey 进行签名
34 | *
35 | * @param apiKey 登录创建 ApiKey apikeys
36 | * @param apiSecret apiKey的后半部分 828902ec516c45307619708d3e780ae1.w5eKiLvhnLP8MtIf 取 w5eKiLvhnLP8MtIf 使用
37 | * @return Token
38 | */
39 | public static String getToken(String apiKey, String apiSecret) {
40 | // 缓存Token
41 | String token = cache.getIfPresent(apiKey);
42 | if (null != token) return token;
43 | // 创建Token
44 | Algorithm algorithm = Algorithm.HMAC256(apiSecret.getBytes(StandardCharsets.UTF_8));
45 | Map payload = new HashMap<>();
46 | payload.put("api_key", apiKey);
47 | payload.put("exp", System.currentTimeMillis() + expireMillis);
48 | payload.put("timestamp", Calendar.getInstance().getTimeInMillis());
49 | Map headerClaims = new HashMap<>();
50 | headerClaims.put("alg", "HS256");
51 | headerClaims.put("sign_type", "SIGN");
52 | token = JWT.create().withPayload(payload).withHeader(headerClaims).sign(algorithm);
53 | cache.put(apiKey, token);
54 | return token;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/java/cn/bugstack/chatglm/test/ApiTest.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.test;
2 |
3 | import cn.bugstack.chatglm.model.*;
4 | import cn.bugstack.chatglm.session.Configuration;
5 | import cn.bugstack.chatglm.session.OpenAiSession;
6 | import cn.bugstack.chatglm.session.OpenAiSessionFactory;
7 | import cn.bugstack.chatglm.session.defaults.DefaultOpenAiSessionFactory;
8 | import cn.bugstack.chatglm.utils.BearerTokenUtils;
9 | import com.alibaba.fastjson.JSON;
10 | import com.fasterxml.jackson.core.JsonProcessingException;
11 | import lombok.extern.slf4j.Slf4j;
12 | import okhttp3.Response;
13 | import okhttp3.logging.HttpLoggingInterceptor;
14 | import okhttp3.sse.EventSource;
15 | import okhttp3.sse.EventSourceListener;
16 | import org.apache.commons.lang3.StringUtils;
17 | import org.jetbrains.annotations.Nullable;
18 | import org.junit.Before;
19 | import org.junit.Test;
20 |
21 | import java.io.IOException;
22 | import java.util.ArrayList;
23 | import java.util.concurrent.CompletableFuture;
24 | import java.util.concurrent.CountDownLatch;
25 | import java.util.concurrent.ExecutionException;
26 |
27 | /**
28 | * @author 小傅哥,微信:fustack
29 | * @description 在官网申请 ApiSecretKey ApiSecretKey
30 | * @github chatglm-sdk-java
31 | * @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
32 | */
33 | @Slf4j
34 | public class ApiTest {
35 |
36 | private OpenAiSession openAiSession;
37 |
38 | @Before
39 | public void test_OpenAiSessionFactory() {
40 | // 1. 配置文件
41 | Configuration configuration = new Configuration();
42 | configuration.setApiHost("https://open.bigmodel.cn/");
43 | configuration.setApiSecretKey("764d6c3f7231f0dcfb2daba77215f112.n23GxnTrlVlKBDvH");
44 | configuration.setLevel(HttpLoggingInterceptor.Level.BODY);
45 | // 2. 会话工厂
46 | OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
47 | // 3. 开启会话
48 | this.openAiSession = factory.openSession();
49 | }
50 |
51 | /**
52 | * 流式对话;
53 | * 1. 默认 isCompatible = true 会兼容新旧版数据格式
54 | * 2. GLM_3_5_TURBO、GLM_4 支持联网等插件
55 | */
56 | @Test
57 | public void test_completions() throws Exception {
58 | CountDownLatch countDownLatch = new CountDownLatch(1);
59 |
60 | // 入参;模型、请求信息
61 | ChatCompletionRequest request = new ChatCompletionRequest();
62 | request.setModel(Model.GLM_3_5_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
63 | request.setIncremental(false);
64 | request.setIsCompatible(true); // 是否对返回结果数据做兼容,24年1月发布的 GLM_3_5_TURBO、GLM_4 模型,与之前的模型在返回结果上有差异。开启 true 可以做兼容。
65 | // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能
66 | request.setTools(new ArrayList() {
67 | private static final long serialVersionUID = -7988151926241837899L;
68 |
69 | {
70 | add(ChatCompletionRequest.Tool.builder()
71 | .type(ChatCompletionRequest.Tool.Type.web_search)
72 | .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build())
73 | .build());
74 | }
75 | });
76 | request.setPrompt(new ArrayList() {
77 | private static final long serialVersionUID = -7988151926241837899L;
78 |
79 | {
80 | add(ChatCompletionRequest.Prompt.builder()
81 | .role(Role.user.getCode())
82 | .content("小傅哥的是谁")
83 | .build());
84 | }
85 | });
86 |
87 | // 请求
88 | openAiSession.completions(request, new EventSourceListener() {
89 | @Override
90 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
91 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
92 | log.info("测试结果 onEvent:{}", response.getData());
93 | // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断
94 | if (EventType.finish.getCode().equals(type)) {
95 | ChatCompletionResponse.Meta meta = JSON.parseObject(response.getMeta(), ChatCompletionResponse.Meta.class);
96 | log.info("[输出结束] Tokens {}", JSON.toJSONString(meta));
97 | }
98 | }
99 |
100 | @Override
101 | public void onClosed(EventSource eventSource) {
102 | log.info("对话完成");
103 | countDownLatch.countDown();
104 | }
105 |
106 | @Override
107 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
108 | log.info("对话异常");
109 | countDownLatch.countDown();
110 | }
111 | });
112 |
113 | // 等待
114 | countDownLatch.await();
115 | }
116 |
117 | /**
118 | * 流式对话;
119 | * 1. 与 test_completions 测试类相比,只是设置 isCompatible = false 这样就是使用了新的数据结构。onEvent 处理接收数据有差异
120 | * 2. 不兼容旧版格式的话,仅支持 GLM_3_5_TURBO、GLM_4 其他模型会有解析错误
121 | */
122 | @Test
123 | public void test_completions_new() throws Exception {
124 | CountDownLatch countDownLatch = new CountDownLatch(1);
125 |
126 | // 入参;模型、请求信息
127 | ChatCompletionRequest request = new ChatCompletionRequest();
128 | request.setModel(Model.GLM_4V); // GLM_3_5_TURBO、GLM_4
129 | request.setIsCompatible(false);
130 | // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能
131 | request.setTools(new ArrayList() {
132 | private static final long serialVersionUID = -7988151926241837899L;
133 |
134 | {
135 | add(ChatCompletionRequest.Tool.builder()
136 | .type(ChatCompletionRequest.Tool.Type.web_search)
137 | .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build())
138 | .build());
139 | }
140 | });
141 | request.setMessages(new ArrayList() {
142 | private static final long serialVersionUID = -7988151926241837899L;
143 |
144 | {
145 | add(ChatCompletionRequest.Prompt.builder()
146 | .role(Role.user.getCode())
147 | .content("小傅哥的是谁")
148 | .build());
149 | }
150 | });
151 |
152 | // 请求
153 | openAiSession.completions(request, new EventSourceListener() {
154 | @Override
155 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
156 | if ("[DONE]".equals(data)) {
157 | log.info("[输出结束] Tokens {}", JSON.toJSONString(data));
158 | return;
159 | }
160 |
161 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
162 | log.info("测试结果:{}", JSON.toJSONString(response));
163 | }
164 |
165 | @Override
166 | public void onClosed(EventSource eventSource) {
167 | log.info("对话完成");
168 | countDownLatch.countDown();
169 | }
170 |
171 | @Override
172 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
173 | log.error("对话失败", t);
174 | countDownLatch.countDown();
175 | }
176 | });
177 |
178 | // 等待
179 | countDownLatch.await();
180 | }
181 |
182 | @Test
183 | public void test_completions_4() throws Exception {
184 | CountDownLatch countDownLatch = new CountDownLatch(1);
185 | // 入参;模型、请求信息
186 | ChatCompletionRequest request = new ChatCompletionRequest();
187 | request.setModel(Model.GLM_4_Flash); // GLM_4_Flash 等模型校验
188 | request.setStream(true);
189 |
190 | request.setMessages(new ArrayList() {
191 | private static final long serialVersionUID = -7988151926241837899L;
192 |
193 | {
194 | // content 字符串格式
195 | add(ChatCompletionRequest.Prompt.builder()
196 | .role(Role.user.getCode())
197 | .content("1+1")
198 | .build());
199 | }
200 | });
201 |
202 | openAiSession.completions(request, new EventSourceListener() {
203 | @Override
204 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
205 | if ("[DONE]".equals(data)) {
206 | log.info("[输出结束] Tokens {}", JSON.toJSONString(data));
207 | return;
208 | }
209 |
210 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
211 | log.info("测试结果:{}", JSON.toJSONString(response));
212 | }
213 |
214 | @Override
215 | public void onClosed(EventSource eventSource) {
216 | log.info("对话完成");
217 | countDownLatch.countDown();
218 | }
219 |
220 | @Override
221 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
222 | log.error("对话失败", t);
223 | countDownLatch.countDown();
224 | }
225 | });
226 |
227 | // 等待
228 | countDownLatch.await();
229 |
230 | }
231 |
232 | /**
233 | * 模型编码:glm-4v
234 | * 根据输入的自然语言指令和图像信息完成任务,推荐使用 SSE 或同步调用方式请求接口
235 | * https://open.bigmodel.cn/dev/api#glm-4v
236 | */
237 | @Test
238 | public void test_completions_4v() throws Exception {
239 | CountDownLatch countDownLatch = new CountDownLatch(1);
240 | // 入参;模型、请求信息
241 | ChatCompletionRequest request = new ChatCompletionRequest();
242 | request.setModel(Model.GLM_4V); // GLM_3_5_TURBO、GLM_4
243 | request.setStream(true);
244 | request.setMessages(new ArrayList() {
245 | private static final long serialVersionUID = -7988151926241837899L;
246 |
247 | {
248 | // content 字符串格式
249 | // add(ChatCompletionRequest.Prompt.builder()
250 | // .role(Role.user.getCode())
251 | // .content("这个图片写了什么")
252 | // .build());
253 |
254 | // content 对象格式
255 | add(ChatCompletionRequest.Prompt.builder()
256 | .role(Role.user.getCode())
257 | .content(ChatCompletionRequest.Prompt.Content.builder()
258 | .type(ChatCompletionRequest.Prompt.Content.Type.text.getCode())
259 | .text("这是什么图片")
260 | .build())
261 | .build());
262 |
263 | // content 对象格式,上传图片;图片支持url、basde64
264 | add(ChatCompletionRequest.Prompt.builder()
265 | .role(Role.user.getCode())
266 | .content(ChatCompletionRequest.Prompt.Content.builder()
267 | .type(ChatCompletionRequest.Prompt.Content.Type.image_url.getCode())
268 | .imageUrl(ChatCompletionRequest.Prompt.Content.ImageUrl.builder().url("https://bugstack.cn/images/article/project/chatgpt/chatgpt-extra-231011-01.png").build())
269 | .build())
270 | .build());
271 | }
272 | });
273 |
274 | openAiSession.completions(request, new EventSourceListener() {
275 | @Override
276 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
277 | if ("[DONE]".equals(data)) {
278 | log.info("[输出结束] Tokens {}", JSON.toJSONString(data));
279 | return;
280 | }
281 |
282 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
283 | log.info("测试结果:{}", JSON.toJSONString(response));
284 | }
285 |
286 | @Override
287 | public void onClosed(EventSource eventSource) {
288 | log.info("对话完成");
289 | countDownLatch.countDown();
290 | }
291 |
292 | @Override
293 | public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
294 | log.error("对话失败", t);
295 | countDownLatch.countDown();
296 | }
297 | });
298 |
299 | // 等待
300 | countDownLatch.await();
301 |
302 | }
303 |
304 | /**
305 | * 同步请求
306 | */
307 | @Test
308 | public void test_completions_future() throws Exception {
309 | // 入参;模型、请求信息
310 | ChatCompletionRequest request = new ChatCompletionRequest();
311 | request.setModel(Model.CHATGLM_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
312 | request.setPrompt(new ArrayList() {
313 | private static final long serialVersionUID = -7988151926241837899L;
314 |
315 | {
316 | add(ChatCompletionRequest.Prompt.builder()
317 | .role(Role.user.getCode())
318 | .content("1+1")
319 | .build());
320 | }
321 | });
322 |
323 | CompletableFuture future = openAiSession.completions(request);
324 | String response = future.get();
325 |
326 | log.info("测试结果:{}", response);
327 | }
328 |
329 | /**
330 | * 同步请求
331 | */
332 | @Test
333 | public void test_completions_sync_01() throws Exception {
334 | // 入参;模型、请求信息
335 | ChatCompletionRequest request = new ChatCompletionRequest();
336 | request.setModel(Model.GLM_3_5_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
337 | request.setPrompt(new ArrayList() {
338 | private static final long serialVersionUID = -7988151926241837899L;
339 |
340 | {
341 | add(ChatCompletionRequest.Prompt.builder()
342 | .role(Role.user.getCode())
343 | .content("小傅哥是谁")
344 | .build());
345 | }
346 | });
347 |
348 | // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能
349 | request.setTools(new ArrayList() {
350 | private static final long serialVersionUID = -7988151926241837899L;
351 |
352 | {
353 | add(ChatCompletionRequest.Tool.builder()
354 | .type(ChatCompletionRequest.Tool.Type.web_search)
355 | .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build())
356 | .build());
357 | }
358 | });
359 |
360 | ChatCompletionSyncResponse response = openAiSession.completionsSync(request);
361 |
362 | log.info("测试结果:{}", JSON.toJSONString(response));
363 | }
364 |
365 | @Test
366 | public void test_completions_sync_02() throws Exception {
367 | // 入参;模型、请求信息
368 | ChatCompletionRequest request = new ChatCompletionRequest();
369 | request.setModel(Model.GLM_3_5_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
370 | request.setPrompt(new ArrayList() {
371 | private static final long serialVersionUID = -7988151926241837899L;
372 |
373 | {
374 | add(ChatCompletionRequest.Prompt.builder()
375 | .role(Role.user.getCode())
376 | .content("1+1")
377 | .build());
378 | }
379 | });
380 |
381 | ChatCompletionSyncResponse response = openAiSession.completionsSync(request);
382 |
383 | log.info("测试结果:{}", JSON.toJSONString(response));
384 | System.out.println(response.getChoices().get(0).getMessage().getContent());
385 | }
386 |
387 | @Test
388 | public void test_genImages() throws Exception {
389 | ImageCompletionRequest request = new ImageCompletionRequest();
390 | request.setModel(Model.COGVIEW_3);
391 | request.setPrompt("画个小狗");
392 | ImageCompletionResponse response = openAiSession.genImages(request);
393 | log.info("测试结果:{}", JSON.toJSONString(response));
394 | }
395 |
396 | @Test
397 | public void test_curl() {
398 | // 1. 配置文件
399 | Configuration configuration = new Configuration();
400 | configuration.setApiHost("https://open.bigmodel.cn/");
401 | configuration.setApiSecretKey("39580e34e175019c230fdd519817b381.*****");
402 |
403 | // 2. 获取Token
404 | String token = BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret());
405 | log.info("1. 在智谱Ai官网,申请 ApiSeretKey 配置到此测试类中,替换 setApiSecretKey 值。 https://open.bigmodel.cn/usercenter/apikeys");
406 | log.info("2. 运行 test_curl 获取 token:{}", token);
407 | log.info("3. 将获得的 token 值,复制到 curl.sh 中,填写到 Authorization: Bearer 后面");
408 | log.info("4. 执行完步骤3以后,可以复制直接运行 curl.sh 文件,或者复制 curl.sh 文件内容到控制台/终端/ApiPost中运行");
409 | }
410 |
411 | }
412 |
--------------------------------------------------------------------------------
/src/test/java/cn/bugstack/chatglm/test/JSONTest.java:
--------------------------------------------------------------------------------
1 | package cn.bugstack.chatglm.test;
2 |
3 | import cn.bugstack.chatglm.model.ChatCompletionResponse;
4 | import cn.bugstack.chatglm.model.ImageCompletionResponse;
5 | import com.alibaba.fastjson.JSON;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.junit.Test;
8 |
9 | @Slf4j
10 | public class JSONTest {
11 |
12 | @Test
13 | public void test_glm_json() {
14 | String json01 = "{\n" +
15 | " \"id\": \"8305987191663349153\",\n" +
16 | " \"created\": 1705487423,\n" +
17 | " \"model\": \"glm-3-turbo\",\n" +
18 | " \"choices\": [\n" +
19 | " {\n" +
20 | " \"index\": 0,\n" +
21 | " \"delta\": {\n" +
22 | " \"role\": \"assistant\",\n" +
23 | " \"content\": \"1\"\n" +
24 | " }\n" +
25 | " }\n" +
26 | " ]\n" +
27 | "}";
28 |
29 | String json02 = "{\n" +
30 | " \"id\": \"8308763664682731117\",\n" +
31 | " \"created\": 1705490859,\n" +
32 | " \"model\": \"glm-3-turbo\",\n" +
33 | " \"choices\": [\n" +
34 | " {\n" +
35 | " \"index\": 0,\n" +
36 | " \"finish_reason\": \"stop\",\n" +
37 | " \"delta\": {\n" +
38 | " \"role\": \"assistant\",\n" +
39 | " \"content\": \"\"\n" +
40 | " }\n" +
41 | " }\n" +
42 | " ],\n" +
43 | " \"usage\": {\n" +
44 | " \"prompt_tokens\": 8,\n" +
45 | " \"completion_tokens\": 12,\n" +
46 | " \"total_tokens\": 20\n" +
47 | " }\n" +
48 | "}";
49 |
50 | ChatCompletionResponse response = JSON.parseObject(json01, ChatCompletionResponse.class);
51 | log.info("测试结果:{}", JSON.toJSONString(response.getChoices()));
52 | }
53 |
54 | @Test
55 | public void test_image_json(){
56 | String json = "{\n" +
57 | " \"created\": 1705549253,\n" +
58 | " \"data\": [\n" +
59 | " {\n" +
60 | " \"url\": \"https://sfile.chatglm.cn/testpath/cbffcbf4-ac63-50a3-9d1e-b644c77ffaa2_0.png\"\n" +
61 | " }\n" +
62 | " ]\n" +
63 | "}";
64 | ImageCompletionResponse response = JSON.parseObject(json, ImageCompletionResponse.class);
65 | log.info("测试结果:{}", response.getData().get(0).getUrl());
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------