Эх сурвалжийг харах

【代码新增】AI:轨迹流动的接入

YunaiV 5 сар өмнө
parent
commit
a7e5aaec3b

BIN
.image/common/ai-feature.png


+ 27 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
 import lombok.extern.slf4j.Slf4j;
@@ -91,6 +92,32 @@ public class YudaoAiAutoConfiguration {
         return new DouBaoChatModel(openAiChatModel);
     }
 
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true")
+    public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.SiliconFlowProperties properties = yudaoAiProperties.getSiliconflow();
+        return buildSiliconFlowChatClient(properties);
+    }
+
+    public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(SiliconFlowChatModel.MODEL_DEFAULT);
+        }
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(SiliconFlowChatModel.BASE_URL)
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .build();
+        return new SiliconFlowChatModel(openAiChatModel);
+    }
+
     @Bean
     @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true")
     public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {

+ 19 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java

@@ -31,6 +31,12 @@ public class YudaoAiProperties {
     @SuppressWarnings("SpellCheckingInspection")
     private HunYuanProperties hunyuan;
 
+    /**
+     * 硅基流动
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private SiliconFlowProperties siliconflow;
+
     /**
      * 讯飞星火
      */
@@ -88,6 +94,19 @@ public class YudaoAiProperties {
 
     }
 
+    @Data
+    public static class SiliconFlowProperties {
+
+        private String enable;
+        private String apiKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
     @Data
     public static class XingHuoProperties {
 

+ 1 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java

@@ -21,6 +21,7 @@ public enum AiPlatformEnum {
     XING_HUO("XingHuo", "星火"), // 讯飞
     DOU_BAO("DouBao", "豆包"), // 字节
     HUN_YUAN("HunYuan", "混元"), // 腾讯
+    SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动
 
     // ========== 国外平台 ==========
 

+ 14 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java

@@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
 import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration;
@@ -80,6 +81,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
                     return buildDouBaoChatModel(apiKey);
                 case HUN_YUAN:
                     return buildHunYuanChatModel(apiKey, url);
+                case SILICON_FLOW:
+                    return buildSiliconFlowChatModel(apiKey);
                 case ZHI_PU:
                     return buildZhiPuChatModel(apiKey, url);
                 case XING_HUO:
@@ -110,6 +113,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
                 return SpringUtil.getBean(DouBaoChatModel.class);
             case HUN_YUAN:
                 return SpringUtil.getBean(HunYuanChatModel.class);
+            case SILICON_FLOW:
+                return SpringUtil.getBean(SiliconFlowChatModel.class);
             case ZHI_PU:
                 return SpringUtil.getBean(ZhiPuAiChatModel.class);
             case XING_HUO:
@@ -290,6 +295,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
         return new YudaoAiAutoConfiguration().buildHunYuanChatClient(properties);
     }
 
+    /**
+     * 可参考 {@link YudaoAiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)}
+     */
+    private ChatModel buildSiliconFlowChatModel(String apiKey) {
+        YudaoAiProperties.SiliconFlowProperties properties = new YudaoAiProperties.SiliconFlowProperties()
+               .setApiKey(apiKey);
+        return new YudaoAiAutoConfiguration().buildSiliconFlowChatClient(properties);
+    }
+
     /**
      * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiChatModel 方法
      */

+ 47 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java

@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.framework.ai.core.model.siliconflow;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * 硅基流动 {@link ChatModel} 实现类
+ *
+ * 1. API 文档:<a href="https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions">API 文档</a>
+ *
+ * @author fansili
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class SiliconFlowChatModel implements ChatModel {
+
+    public static final String BASE_URL = "https://api.siliconflow.cn";
+
+    public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B";
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}

+ 1 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java

@@ -33,6 +33,7 @@ public class AiUtils {
             case DOU_BAO: // 复用 OpenAI 客户端
             case HUN_YUAN: // 复用 OpenAI 客户端
             case XING_HUO: // 复用 OpenAI 客户端
+            case SILICON_FLOW: // 复用 OpenAI 客户端
                 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
             case AZURE_OPENAI:
                 // TODO 芋艿:貌似没 model 字段???!

+ 1 - 2
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DouBaoChatModelTests.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.framework.ai.chat;
 
-import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -18,7 +17,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * {@link DeepSeekChatModel} 集成测试
+ * {@link DouBaoChatModel} 集成测试
  *
  * @author 芋道源码
  */

+ 1 - 2
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/HunYuanChatModelTests.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.framework.ai.chat;
 
-import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
 import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -18,7 +17,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * {@link DeepSeekChatModel} 集成测试
+ * {@link HunYuanChatModel} 集成测试
  *
  * @author 芋道源码
  */

+ 69 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java

@@ -0,0 +1,69 @@
+package cn.iocoder.yudao.framework.ai.chat;
+
+import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link SiliconFlowChatModel} 集成测试
+ *
+ * @author 芋道源码
+ */
+public class SiliconFlowChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(SiliconFlowChatModel.BASE_URL)
+                    .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model(SiliconFlowChatModel.MODEL_DEFAULT) // 模型
+//                    .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费
+//                    .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final SiliconFlowChatModel chatModel = new SiliconFlowChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}

+ 4 - 0
yudao-server/src/main/resources/application.yaml

@@ -192,6 +192,10 @@ yudao:
       enable: true
       api-key: sk-abc
       model: hunyuan-turbo
+    siliconflow: # 硅基流动
+      enable: true
+      api-key: sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz
+      model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
     xinghuo: # 讯飞星火
       enable: true
       appKey: 75b161ed2aef4719b275d6e7f2a4d4cd