Commit cd4813f7dd78c9ce6bf256bbc351ff622905641f
1 parent
ef5e56d5
【功能新增】AI:百川模型的接入
Showing
8 changed files
with
177 additions
and
1 deletions
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 | +} | ... | ... |