Commit cd4813f7dd78c9ce6bf256bbc351ff622905641f

Authored by YunaiV
1 parent ef5e56d5

【功能新增】AI:百川模型的接入

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil;
4 4 import cn.hutool.extra.spring.SpringUtil;
5 5 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
6 6 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
  7 +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel;
7 8 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
8 9 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
9 10 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
... ... @@ -194,6 +195,33 @@ public class YudaoAiAutoConfiguration {
194 195 }
195 196  
196 197 @Bean
  198 + @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true")
  199 + public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) {
  200 + YudaoAiProperties.BaiChuanProperties properties = yudaoAiProperties.getBaichuan();
  201 + return buildBaiChuanChatClient(properties);
  202 + }
  203 +
  204 + public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuanProperties properties) {
  205 + if (StrUtil.isEmpty(properties.getModel())) {
  206 + properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
  207 + }
  208 + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
  209 + .openAiApi(OpenAiApi.builder()
  210 + .baseUrl(BaiChuanChatModel.BASE_URL)
  211 + .apiKey(properties.getApiKey())
  212 + .build())
  213 + .defaultOptions(OpenAiChatOptions.builder()
  214 + .model(properties.getModel())
  215 + .temperature(properties.getTemperature())
  216 + .maxTokens(properties.getMaxTokens())
  217 + .topP(properties.getTopP())
  218 + .build())
  219 + .toolCallingManager(getToolCallingManager())
  220 + .build();
  221 + return new BaiChuanChatModel(openAiChatModel);
  222 + }
  223 +
  224 + @Bean
197 225 @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true")
198 226 public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {
199 227 YudaoAiProperties.MidjourneyProperties config = yudaoAiProperties.getMidjourney();
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java
... ... @@ -44,6 +44,12 @@ public class YudaoAiProperties {
44 44 private XingHuoProperties xinghuo;
45 45  
46 46 /**
  47 + * 百川
  48 + */
  49 + @SuppressWarnings("SpellCheckingInspection")
  50 + private BaiChuanProperties baichuan;
  51 +
  52 + /**
47 53 * Midjourney 绘图
48 54 */
49 55 private MidjourneyProperties midjourney;
... ... @@ -123,6 +129,19 @@ public class YudaoAiProperties {
123 129 }
124 130  
125 131 @Data
  132 + public static class BaiChuanProperties {
  133 +
  134 + private String enable;
  135 + private String apiKey;
  136 +
  137 + private String model;
  138 + private Double temperature;
  139 + private Integer maxTokens;
  140 + private Double topP;
  141 +
  142 + }
  143 +
  144 + @Data
126 145 public static class MidjourneyProperties {
127 146  
128 147 private String enable;
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java
... ... @@ -27,6 +27,7 @@ public enum AiPlatformEnum implements ArrayValuable<String> {
27 27 SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动
28 28 MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技
29 29 MOONSHOT("Moonshot", "月之暗灭"), // KIMI
  30 + BAI_CHUAN("BaiChuan", "百川智能"), // 百川智能
30 31  
31 32 // ========== 国外平台 ==========
32 33  
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -11,6 +11,7 @@ import cn.hutool.extra.spring.SpringUtil;
11 11 import cn.iocoder.yudao.framework.ai.config.YudaoAiAutoConfiguration;
12 12 import cn.iocoder.yudao.framework.ai.config.YudaoAiProperties;
13 13 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  14 +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel;
14 15 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
15 16 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
16 17 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
... ... @@ -150,6 +151,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
150 151 return buildMoonshotChatModel(apiKey, url);
151 152 case XING_HUO:
152 153 return buildXingHuoChatModel(apiKey);
  154 + case BAI_CHUAN:
  155 + return buildBaiChuanChatModel(apiKey);
153 156 case OPENAI:
154 157 return buildOpenAiChatModel(apiKey, url);
155 158 case AZURE_OPENAI:
... ... @@ -186,6 +189,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
186 189 return SpringUtil.getBean(MoonshotChatModel.class);
187 190 case XING_HUO:
188 191 return SpringUtil.getBean(XingHuoChatModel.class);
  192 + case BAI_CHUAN:
  193 + return SpringUtil.getBean(AzureOpenAiChatModel.class);
189 194 case OPENAI:
190 195 return SpringUtil.getBean(OpenAiChatModel.class);
191 196 case AZURE_OPENAI:
... ... @@ -442,6 +447,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
442 447 }
443 448  
444 449 /**
  450 + * 可参考 {@link YudaoAiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)}
  451 + */
  452 + private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) {
  453 + YudaoAiProperties.BaiChuanProperties properties = new YudaoAiProperties.BaiChuanProperties()
  454 + .setApiKey(apiKey);
  455 + return new YudaoAiAutoConfiguration().buildBaiChuanChatClient(properties);
  456 + }
  457 +
  458 + /**
445 459 * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法
446 460 */
447 461 private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) {
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/baichuan/BaiChuanChatModel.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.core.model.baichuan;
  2 +
  3 +import lombok.RequiredArgsConstructor;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.springframework.ai.chat.model.ChatModel;
  6 +import org.springframework.ai.chat.model.ChatResponse;
  7 +import org.springframework.ai.chat.prompt.ChatOptions;
  8 +import org.springframework.ai.chat.prompt.Prompt;
  9 +import org.springframework.ai.openai.OpenAiChatModel;
  10 +import reactor.core.publisher.Flux;
  11 +
  12 +/**
  13 + * 百川 {@link ChatModel} 实现类
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Slf4j
  18 +@RequiredArgsConstructor
  19 +public class BaiChuanChatModel implements ChatModel {
  20 +
  21 + public static final String BASE_URL = "https://api.baichuan-ai.com";
  22 +
  23 + public static final String MODEL_DEFAULT = "Baichuan4-Turbo";
  24 +
  25 + /**
  26 + * 兼容 OpenAI 接口,进行复用
  27 + */
  28 + private final OpenAiChatModel openAiChatModel;
  29 +
  30 + @Override
  31 + public ChatResponse call(Prompt prompt) {
  32 + return openAiChatModel.call(prompt);
  33 + }
  34 +
  35 + @Override
  36 + public Flux<ChatResponse> stream(Prompt prompt) {
  37 + return openAiChatModel.stream(prompt);
  38 + }
  39 +
  40 + @Override
  41 + public ChatOptions getDefaultOptions() {
  42 + return openAiChatModel.getDefaultOptions();
  43 + }
  44 +
  45 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
... ... @@ -149,7 +149,7 @@ public class SiliconFlowImageModel implements ImageModel {
149 149 .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN()))
150 150 .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth()))
151 151 .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight()))
152   - // Handle OpenAI specific image options
  152 + // Handle SiliconFlow specific image options
153 153 .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt()))
154 154 .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps()))
155 155 .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale()))
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java
... ... @@ -50,6 +50,7 @@ public class AiUtils {
50 50 case HUN_YUAN: // 复用 OpenAI 客户端
51 51 case XING_HUO: // 复用 OpenAI 客户端
52 52 case SILICON_FLOW: // 复用 OpenAI 客户端
  53 + case BAI_CHUAN: // 复用 OpenAI 客户端
53 54 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
54 55 .toolNames(toolNames).build();
55 56 case AZURE_OPENAI:
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/BaiChuanChatModelTests.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.chat;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel;
  4 +import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
  5 +import org.junit.jupiter.api.Disabled;
  6 +import org.junit.jupiter.api.Test;
  7 +import org.springframework.ai.chat.messages.Message;
  8 +import org.springframework.ai.chat.messages.SystemMessage;
  9 +import org.springframework.ai.chat.messages.UserMessage;
  10 +import org.springframework.ai.chat.model.ChatResponse;
  11 +import org.springframework.ai.chat.prompt.Prompt;
  12 +import org.springframework.ai.openai.OpenAiChatModel;
  13 +import org.springframework.ai.openai.OpenAiChatOptions;
  14 +import org.springframework.ai.openai.api.OpenAiApi;
  15 +import reactor.core.publisher.Flux;
  16 +
  17 +import java.util.ArrayList;
  18 +import java.util.List;
  19 +
  20 +/**
  21 + * {@link BaiChuanChatModel} 集成测试
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +public class BaiChuanChatModelTests {
  26 +
  27 + private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
  28 + .openAiApi(OpenAiApi.builder()
  29 + .baseUrl(BaiChuanChatModel.BASE_URL)
  30 + .apiKey("sk-61b6766a94c70786ed02673f5e16af3c") // apiKey
  31 + .build())
  32 + .defaultOptions(OpenAiChatOptions.builder()
  33 + .model("Baichuan4-Turbo") // 模型(https://platform.baichuan-ai.com/docs/api)
  34 + .temperature(0.7)
  35 + .build())
  36 + .build();
  37 +
  38 + private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel);
  39 +
  40 + @Test
  41 + @Disabled
  42 + public void testCall() {
  43 + // 准备参数
  44 + List<Message> messages = new ArrayList<>();
  45 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  46 + messages.add(new UserMessage("1 + 1 = ?"));
  47 +
  48 + // 调用
  49 + ChatResponse response = chatModel.call(new Prompt(messages));
  50 + // 打印结果
  51 + System.out.println(response);
  52 + }
  53 +
  54 + @Test
  55 + @Disabled
  56 + public void testStream() {
  57 + // 准备参数
  58 + List<Message> messages = new ArrayList<>();
  59 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
  60 + messages.add(new UserMessage("1 + 1 = ?"));
  61 +
  62 + // 调用
  63 + Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
  64 + // 打印结果
  65 + flux.doOnNext(System.out::println).then().block();
  66 + }
  67 +
  68 +}
... ...