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