Commit 6e64ae774e28a017e41306e53637be0d29366063

Authored by YunaiV
1 parent 59c853b5

【代码新增】AI:接入智谱 GLM-4 模型

yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -20,9 +20,10 @@ @@ -20,9 +20,10 @@
20 <dependencies> 20 <dependencies>
21 <dependency> 21 <dependency>
22 <groupId>org.springframework.ai</groupId> 22 <groupId>org.springframework.ai</groupId>
23 - <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> 23 + <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
24 <version>${spring-ai.version}</version> 24 <version>${spring-ai.version}</version>
25 </dependency> 25 </dependency>
  26 +
26 <dependency> 27 <dependency>
27 <groupId>org.springframework.ai</groupId> 28 <groupId>org.springframework.ai</groupId>
28 <artifactId>spring-ai-openai-spring-boot-starter</artifactId> 29 <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
@@ -30,6 +31,11 @@ @@ -30,6 +31,11 @@
30 </dependency> 31 </dependency>
31 <dependency> 32 <dependency>
32 <groupId>org.springframework.ai</groupId> 33 <groupId>org.springframework.ai</groupId>
  34 + <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
  35 + <version>${spring-ai.version}</version>
  36 + </dependency>
  37 + <dependency>
  38 + <groupId>org.springframework.ai</groupId>
33 <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId> 39 <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId>
34 <version>${spring-ai.version}</version> 40 <version>${spring-ai.version}</version>
35 </dependency> 41 </dependency>
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java
@@ -17,6 +17,7 @@ public enum AiPlatformEnum { @@ -17,6 +17,7 @@ public enum AiPlatformEnum {
17 TONG_YI("TongYi", "通义千问"), // 阿里 17 TONG_YI("TongYi", "通义千问"), // 阿里
18 YI_YAN("YiYan", "文心一言"), // 百度 18 YI_YAN("YiYan", "文心一言"), // 百度
19 DEEP_SEEK("DeepSeek", "DeepSeek"), // DeepSeek 19 DEEP_SEEK("DeepSeek", "DeepSeek"), // DeepSeek
  20 + ZHI_PU("ZhiPu", "智谱"), // 智谱 AI
20 XING_HUO("XingHuo", "星火"), // 讯飞 21 XING_HUO("XingHuo", "星火"), // 讯飞
21 22
22 // ========== 国外平台 ========== 23 // ========== 国外平台 ==========
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -23,8 +23,12 @@ import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; @@ -23,8 +23,12 @@ import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration;
23 import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; 23 import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration;
24 import org.springframework.ai.autoconfigure.qianfan.QianFanChatProperties; 24 import org.springframework.ai.autoconfigure.qianfan.QianFanChatProperties;
25 import org.springframework.ai.autoconfigure.qianfan.QianFanConnectionProperties; 25 import org.springframework.ai.autoconfigure.qianfan.QianFanConnectionProperties;
  26 +import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration;
  27 +import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiChatProperties;
  28 +import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties;
26 import org.springframework.ai.chat.model.ChatModel; 29 import org.springframework.ai.chat.model.ChatModel;
27 import org.springframework.ai.image.ImageModel; 30 import org.springframework.ai.image.ImageModel;
  31 +import org.springframework.ai.model.function.FunctionCallbackContext;
28 import org.springframework.ai.ollama.OllamaChatModel; 32 import org.springframework.ai.ollama.OllamaChatModel;
29 import org.springframework.ai.ollama.api.OllamaApi; 33 import org.springframework.ai.ollama.api.OllamaApi;
30 import org.springframework.ai.openai.OpenAiChatModel; 34 import org.springframework.ai.openai.OpenAiChatModel;
@@ -36,6 +40,8 @@ import org.springframework.ai.qianfan.QianFanChatModel; @@ -36,6 +40,8 @@ import org.springframework.ai.qianfan.QianFanChatModel;
36 import org.springframework.ai.qianfan.api.QianFanApi; 40 import org.springframework.ai.qianfan.api.QianFanApi;
37 import org.springframework.ai.stabilityai.StabilityAiImageModel; 41 import org.springframework.ai.stabilityai.StabilityAiImageModel;
38 import org.springframework.ai.stabilityai.api.StabilityAiApi; 42 import org.springframework.ai.stabilityai.api.StabilityAiApi;
  43 +import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
  44 +import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
39 import org.springframework.retry.support.RetryTemplate; 45 import org.springframework.retry.support.RetryTemplate;
40 import org.springframework.web.client.ResponseErrorHandler; 46 import org.springframework.web.client.ResponseErrorHandler;
41 import org.springframework.web.client.RestClient; 47 import org.springframework.web.client.RestClient;
@@ -61,6 +67,8 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -61,6 +67,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
61 return buildYiYanChatModel(apiKey); 67 return buildYiYanChatModel(apiKey);
62 case DEEP_SEEK: 68 case DEEP_SEEK:
63 return buildDeepSeekChatModel(apiKey); 69 return buildDeepSeekChatModel(apiKey);
  70 + case ZHI_PU:
  71 + return buildZhiPuChatModel(apiKey, url);
64 case XING_HUO: 72 case XING_HUO:
65 return buildXingHuoChatModel(apiKey); 73 return buildXingHuoChatModel(apiKey);
66 case OPENAI: 74 case OPENAI:
@@ -81,6 +89,10 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -81,6 +89,10 @@ public class AiModelFactoryImpl implements AiModelFactory {
81 return SpringUtil.getBean(TongYiChatModel.class); 89 return SpringUtil.getBean(TongYiChatModel.class);
82 case YI_YAN: 90 case YI_YAN:
83 return SpringUtil.getBean(QianFanChatModel.class); 91 return SpringUtil.getBean(QianFanChatModel.class);
  92 + case DEEP_SEEK:
  93 + return SpringUtil.getBean(DeepSeekChatModel.class);
  94 + case ZHI_PU:
  95 + return SpringUtil.getBean(ZhiPuAiChatModel.class);
84 case XING_HUO: 96 case XING_HUO:
85 return SpringUtil.getBean(XingHuoChatModel.class); 97 return SpringUtil.getBean(XingHuoChatModel.class);
86 case OPENAI: 98 case OPENAI:
@@ -176,6 +188,15 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -176,6 +188,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
176 } 188 }
177 189
178 /** 190 /**
  191 + * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
  192 + */
  193 + private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
  194 + url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
  195 + ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi(url, apiKey);
  196 + return new ZhiPuAiChatModel(zhiPuAiApi);
  197 + }
  198 +
  199 + /**
179 * 可参考 {@link YudaoAiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)} 200 * 可参考 {@link YudaoAiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)}
180 */ 201 */
181 private static XingHuoChatModel buildXingHuoChatModel(String key) { 202 private static XingHuoChatModel buildXingHuoChatModel(String key) {
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java
@@ -10,6 +10,7 @@ import org.springframework.ai.chat.prompt.ChatOptions; @@ -10,6 +10,7 @@ import org.springframework.ai.chat.prompt.ChatOptions;
10 import org.springframework.ai.ollama.api.OllamaOptions; 10 import org.springframework.ai.ollama.api.OllamaOptions;
11 import org.springframework.ai.openai.OpenAiChatOptions; 11 import org.springframework.ai.openai.OpenAiChatOptions;
12 import org.springframework.ai.qianfan.QianFanChatOptions; 12 import org.springframework.ai.qianfan.QianFanChatOptions;
  13 +import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
13 14
14 /** 15 /**
15 * Spring AI 工具类 16 * Spring AI 工具类
@@ -22,18 +23,20 @@ public class AiUtils { @@ -22,18 +23,20 @@ public class AiUtils {
22 Float temperatureF = temperature != null ? temperature.floatValue() : null; 23 Float temperatureF = temperature != null ? temperature.floatValue() : null;
23 //noinspection EnhancedSwitchMigration 24 //noinspection EnhancedSwitchMigration
24 switch (platform) { 25 switch (platform) {
25 - case OPENAI:  
26 - return OpenAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();  
27 - case OLLAMA:  
28 - return OllamaOptions.create().withModel(model).withTemperature(temperatureF).withNumPredict(maxTokens);  
29 - case YI_YAN:  
30 - return QianFanChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();  
31 - case XING_HUO:  
32 - return XingHuoChatOptions.builder().model(model).temperature(temperatureF).maxTokens(maxTokens).build();  
33 case TONG_YI: 26 case TONG_YI:
34 return TongYiChatOptions.builder().withModel(model).withTemperature(temperature).withMaxTokens(maxTokens).build(); 27 return TongYiChatOptions.builder().withModel(model).withTemperature(temperature).withMaxTokens(maxTokens).build();
  28 + case YI_YAN:
  29 + return QianFanChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();
35 case DEEP_SEEK: 30 case DEEP_SEEK:
36 return DeepSeekChatOptions.builder().model(model).temperature(temperatureF).maxTokens(maxTokens).build(); 31 return DeepSeekChatOptions.builder().model(model).temperature(temperatureF).maxTokens(maxTokens).build();
  32 + case ZHI_PU:
  33 + return ZhiPuAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();
  34 + case XING_HUO:
  35 + return XingHuoChatOptions.builder().model(model).temperature(temperatureF).maxTokens(maxTokens).build();
  36 + case OPENAI:
  37 + return OpenAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();
  38 + case OLLAMA:
  39 + return OllamaOptions.create().withModel(model).withTemperature(temperatureF).withNumPredict(maxTokens);
37 default: 40 default:
38 throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); 41 throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
39 } 42 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DeepSeekChatModelTests.java
1 package cn.iocoder.yudao.framework.ai.chat; 1 package cn.iocoder.yudao.framework.ai.chat;
2 2
3 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; 3 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
  4 +import org.junit.jupiter.api.Disabled;
4 import org.junit.jupiter.api.Test; 5 import org.junit.jupiter.api.Test;
5 import org.springframework.ai.chat.messages.Message; 6 import org.springframework.ai.chat.messages.Message;
6 import org.springframework.ai.chat.messages.SystemMessage; 7 import org.springframework.ai.chat.messages.SystemMessage;
@@ -22,6 +23,7 @@ public class DeepSeekChatModelTests { @@ -22,6 +23,7 @@ public class DeepSeekChatModelTests {
22 private final DeepSeekChatModel chatModel = new DeepSeekChatModel("sk-e94db327cc7d457d99a8de8810fc6b12"); 23 private final DeepSeekChatModel chatModel = new DeepSeekChatModel("sk-e94db327cc7d457d99a8de8810fc6b12");
23 24
24 @Test 25 @Test
  26 + @Disabled
25 public void testCall() { 27 public void testCall() {
26 // 准备参数 28 // 准备参数
27 List<Message> messages = new ArrayList<>(); 29 List<Message> messages = new ArrayList<>();
@@ -35,6 +37,7 @@ public class DeepSeekChatModelTests { @@ -35,6 +37,7 @@ public class DeepSeekChatModelTests {
35 } 37 }
36 38
37 @Test 39 @Test
  40 + @Disabled
38 public void testStream() { 41 public void testStream() {
39 // 准备参数 42 // 准备参数
40 List<Message> messages = new ArrayList<>(); 43 List<Message> messages = new ArrayList<>();
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java
1 package cn.iocoder.yudao.framework.ai.chat; 1 package cn.iocoder.yudao.framework.ai.chat;
2 2
3 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; 3 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
  4 +import org.junit.jupiter.api.Disabled;
4 import org.junit.jupiter.api.Test; 5 import org.junit.jupiter.api.Test;
5 import org.springframework.ai.chat.messages.Message; 6 import org.springframework.ai.chat.messages.Message;
6 import org.springframework.ai.chat.messages.SystemMessage; 7 import org.springframework.ai.chat.messages.SystemMessage;
@@ -24,6 +25,7 @@ public class XingHuoChatModelTests { @@ -24,6 +25,7 @@ public class XingHuoChatModelTests {
24 "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh"); 25 "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh");
25 26
26 @Test 27 @Test
  28 + @Disabled
27 public void testCall() { 29 public void testCall() {
28 // 准备参数 30 // 准备参数
29 List<Message> messages = new ArrayList<>(); 31 List<Message> messages = new ArrayList<>();
@@ -37,6 +39,7 @@ public class XingHuoChatModelTests { @@ -37,6 +39,7 @@ public class XingHuoChatModelTests {
37 } 39 }
38 40
39 @Test 41 @Test
  42 + @Disabled
40 public void testStream() { 43 public void testStream() {
41 // 准备参数 44 // 准备参数
42 List<Message> messages = new ArrayList<>(); 45 List<Message> messages = new ArrayList<>();
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/ZhiPuAiChatModelTests.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.chat;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.ai.chat.messages.Message;
  6 +import org.springframework.ai.chat.messages.SystemMessage;
  7 +import org.springframework.ai.chat.messages.UserMessage;
  8 +import org.springframework.ai.chat.model.ChatResponse;
  9 +import org.springframework.ai.chat.prompt.Prompt;
  10 +import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
  11 +import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
  12 +import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
  13 +import reactor.core.publisher.Flux;
  14 +
  15 +import java.util.ArrayList;
  16 +import java.util.List;
  17 +
  18 +/**
  19 + * {@link ZhiPuAiChatModel} 的集成测试
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +public class ZhiPuAiChatModelTests {
  24 +
  25 + private final ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi("32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs");
  26 + private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(zhiPuAiApi,
  27 + ZhiPuAiChatOptions.builder().withModel(ZhiPuAiApi.ChatModel.GLM_4.getModelName()).build());
  28 +
  29 + @Test
  30 + @Disabled
  31 + public void testCall() {
  32 + // 准备参数
  33 + List<Message> messages = new ArrayList<>();
  34 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  35 + messages.add(new UserMessage("1 + 1 = ?"));
  36 +
  37 + // 调用
  38 + ChatResponse response = chatModel.call(new Prompt(messages));
  39 + // 打印结果
  40 + System.out.println(response);
  41 + System.out.println(response.getResult().getOutput());
  42 + }
  43 +
  44 + @Test
  45 + @Disabled
  46 + public void testStream() {
  47 + // 准备参数
  48 + List<Message> messages = new ArrayList<>();
  49 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  50 + messages.add(new UserMessage("1 + 1 = ?"));
  51 +
  52 + // 调用
  53 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
  54 + // 打印结果
  55 + flux.doOnNext(response -> {
  56 +// System.out.println(response);
  57 + System.out.println(response.getResult().getOutput());
  58 + }).then().block();
  59 + }
  60 +
  61 +}
yudao-server/src/main/resources/application.yaml
@@ -156,13 +156,15 @@ spring: @@ -156,13 +156,15 @@ spring:
156 qianfan: # 文心一言 156 qianfan: # 文心一言
157 api-key: x0cuLZ7XsaTCU08vuJWO87Lg 157 api-key: x0cuLZ7XsaTCU08vuJWO87Lg
158 secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK 158 secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK
  159 + zhipuai: # 智谱 AI
  160 + api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs
  161 + openai:
  162 + api-key: sk-yzKea6d8e8212c3bdd99f9f44ced1cae37c097e5aa3BTS7z
  163 + base-url: https://api.gptsapi.net
159 ollama: 164 ollama:
160 base-url: http://127.0.0.1:11434 165 base-url: http://127.0.0.1:11434
161 chat: 166 chat:
162 model: llama3 167 model: llama3
163 - openai:  
164 - api-key: sk-yzKea6d8e8212c3bdd99f9f44ced1cae37c097e5aa3BTS7z  
165 - base-url: https://api.gptsapi.net  
166 stabilityai: 168 stabilityai:
167 api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx 169 api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx
168 cloud: 170 cloud:
@@ -173,11 +175,11 @@ spring: @@ -173,11 +175,11 @@ spring:
173 175
174 yudao: 176 yudao:
175 ai: 177 ai:
176 - deep-seek: 178 + deep-seek: # DeepSeek
177 enable: true 179 enable: true
178 api-key: sk-e94db327cc7d457d99a8de8810fc6b12 180 api-key: sk-e94db327cc7d457d99a8de8810fc6b12
179 model: deepseek-chat 181 model: deepseek-chat
180 - xinghuo: 182 + xinghuo: # 讯飞星火
181 enable: true 183 enable: true
182 appId: 13c8cca6 184 appId: 13c8cca6
183 appKey: cb6415c19d6162cda07b47316fcb0416 185 appKey: cb6415c19d6162cda07b47316fcb0416