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,6 +4,7 @@ import cn.hutool.core.util.StrUtil; | ||
| 4 | import cn.hutool.extra.spring.SpringUtil; | 4 | import cn.hutool.extra.spring.SpringUtil; |
| 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; | 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; |
| 6 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; | 6 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; |
| 7 | +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel; | ||
| 7 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; | 8 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; |
| 8 | import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; | 9 | import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; |
| 9 | import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; | 10 | import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| @@ -194,6 +195,33 @@ public class YudaoAiAutoConfiguration { | @@ -194,6 +195,33 @@ public class YudaoAiAutoConfiguration { | ||
| 194 | } | 195 | } |
| 195 | 196 | ||
| 196 | @Bean | 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 | @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true") | 225 | @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true") |
| 198 | public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) { | 226 | public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) { |
| 199 | YudaoAiProperties.MidjourneyProperties config = yudaoAiProperties.getMidjourney(); | 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,6 +44,12 @@ public class YudaoAiProperties { | ||
| 44 | private XingHuoProperties xinghuo; | 44 | private XingHuoProperties xinghuo; |
| 45 | 45 | ||
| 46 | /** | 46 | /** |
| 47 | + * 百川 | ||
| 48 | + */ | ||
| 49 | + @SuppressWarnings("SpellCheckingInspection") | ||
| 50 | + private BaiChuanProperties baichuan; | ||
| 51 | + | ||
| 52 | + /** | ||
| 47 | * Midjourney 绘图 | 53 | * Midjourney 绘图 |
| 48 | */ | 54 | */ |
| 49 | private MidjourneyProperties midjourney; | 55 | private MidjourneyProperties midjourney; |
| @@ -123,6 +129,19 @@ public class YudaoAiProperties { | @@ -123,6 +129,19 @@ public class YudaoAiProperties { | ||
| 123 | } | 129 | } |
| 124 | 130 | ||
| 125 | @Data | 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 | public static class MidjourneyProperties { | 145 | public static class MidjourneyProperties { |
| 127 | 146 | ||
| 128 | private String enable; | 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,6 +27,7 @@ public enum AiPlatformEnum implements ArrayValuable<String> { | ||
| 27 | SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动 | 27 | SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动 |
| 28 | MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技 | 28 | MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技 |
| 29 | MOONSHOT("Moonshot", "月之暗灭"), // KIMI | 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,6 +11,7 @@ import cn.hutool.extra.spring.SpringUtil; | ||
| 11 | import cn.iocoder.yudao.framework.ai.config.YudaoAiAutoConfiguration; | 11 | import cn.iocoder.yudao.framework.ai.config.YudaoAiAutoConfiguration; |
| 12 | import cn.iocoder.yudao.framework.ai.config.YudaoAiProperties; | 12 | import cn.iocoder.yudao.framework.ai.config.YudaoAiProperties; |
| 13 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 13 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 14 | +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel; | ||
| 14 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; | 15 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; |
| 15 | import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; | 16 | import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; |
| 16 | import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; | 17 | import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| @@ -150,6 +151,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -150,6 +151,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 150 | return buildMoonshotChatModel(apiKey, url); | 151 | return buildMoonshotChatModel(apiKey, url); |
| 151 | case XING_HUO: | 152 | case XING_HUO: |
| 152 | return buildXingHuoChatModel(apiKey); | 153 | return buildXingHuoChatModel(apiKey); |
| 154 | + case BAI_CHUAN: | ||
| 155 | + return buildBaiChuanChatModel(apiKey); | ||
| 153 | case OPENAI: | 156 | case OPENAI: |
| 154 | return buildOpenAiChatModel(apiKey, url); | 157 | return buildOpenAiChatModel(apiKey, url); |
| 155 | case AZURE_OPENAI: | 158 | case AZURE_OPENAI: |
| @@ -186,6 +189,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -186,6 +189,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 186 | return SpringUtil.getBean(MoonshotChatModel.class); | 189 | return SpringUtil.getBean(MoonshotChatModel.class); |
| 187 | case XING_HUO: | 190 | case XING_HUO: |
| 188 | return SpringUtil.getBean(XingHuoChatModel.class); | 191 | return SpringUtil.getBean(XingHuoChatModel.class); |
| 192 | + case BAI_CHUAN: | ||
| 193 | + return SpringUtil.getBean(AzureOpenAiChatModel.class); | ||
| 189 | case OPENAI: | 194 | case OPENAI: |
| 190 | return SpringUtil.getBean(OpenAiChatModel.class); | 195 | return SpringUtil.getBean(OpenAiChatModel.class); |
| 191 | case AZURE_OPENAI: | 196 | case AZURE_OPENAI: |
| @@ -442,6 +447,15 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -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 | * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法 | 459 | * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法 |
| 446 | */ | 460 | */ |
| 447 | private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { | 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,7 +149,7 @@ public class SiliconFlowImageModel implements ImageModel { | ||
| 149 | .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN())) | 149 | .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN())) |
| 150 | .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth())) | 150 | .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth())) |
| 151 | .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight())) | 151 | .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight())) |
| 152 | - // Handle OpenAI specific image options | 152 | + // Handle SiliconFlow specific image options |
| 153 | .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt())) | 153 | .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt())) |
| 154 | .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps())) | 154 | .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps())) |
| 155 | .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale())) | 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,6 +50,7 @@ public class AiUtils { | ||
| 50 | case HUN_YUAN: // 复用 OpenAI 客户端 | 50 | case HUN_YUAN: // 复用 OpenAI 客户端 |
| 51 | case XING_HUO: // 复用 OpenAI 客户端 | 51 | case XING_HUO: // 复用 OpenAI 客户端 |
| 52 | case SILICON_FLOW: // 复用 OpenAI 客户端 | 52 | case SILICON_FLOW: // 复用 OpenAI 客户端 |
| 53 | + case BAI_CHUAN: // 复用 OpenAI 客户端 | ||
| 53 | return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | 54 | return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 54 | .toolNames(toolNames).build(); | 55 | .toolNames(toolNames).build(); |
| 55 | case AZURE_OPENAI: | 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 | +} |