From 24681c81c09022f584a57006f2534b5f74723414 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 30 六月 2026 09:27:31 +0800
Subject: [PATCH] 初始化项目

---
 yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java |  283 ++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 222 insertions(+), 61 deletions(-)

diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
index 559da13..17c3c42 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
@@ -6,29 +6,42 @@
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
 import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
-import com.openai.client.okhttp.OpenAIOkHttpClient;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
 import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
 import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
 import cn.iocoder.yudao.module.ai.tool.method.PersonService;
+import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
+import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
+import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel;
+import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions;
+import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
+import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
 import io.micrometer.observation.ObservationRegistry;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.ai.chat.model.ChatModel;
 import org.springframework.ai.deepseek.DeepSeekChatModel;
 import org.springframework.ai.deepseek.DeepSeekChatOptions;
 import org.springframework.ai.deepseek.api.DeepSeekApi;
+import org.springframework.ai.document.MetadataMode;
 import org.springframework.ai.embedding.BatchingStrategy;
 import org.springframework.ai.embedding.TokenCountBatchingStrategy;
 import org.springframework.ai.model.tool.ToolCallingManager;
 import org.springframework.ai.openai.OpenAiChatModel;
 import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.retry.RetryUtils;
 import org.springframework.ai.support.ToolCallbacks;
 import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
 import org.springframework.ai.tokenizer.TokenCountEstimator;
@@ -40,11 +53,11 @@
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 import java.util.List;
-import java.util.Optional;
 
 /**
  * 鑺嬮亾 AI 鑷姩閰嶇疆
@@ -75,30 +88,70 @@
     // ========== 鍚勭 AI Client 鍒涘缓 ==========
 
     @Bean
-    @ConditionalOnProperty(value = "yudao.ai.gemini.enable", havingValue = "true")
-    public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.Gemini properties = yudaoAiProperties.getGemini();
-        return buildGeminiChatClient(properties);
+    @ConditionalOnMissingBean
+    @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
+    public DashScopeChatModel dashScopeChatModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
+                                                 ToolCallingManager toolCallingManager,
+                                                 ObservationRegistry observationRegistry) {
+        return buildTongYiChatModel(apiKey, toolCallingManager, observationRegistry);
     }
 
-    public GeminiChatModel buildGeminiChatClient(YudaoAiProperties.Gemini properties) {
-        if (StrUtil.isEmpty(properties.getModel())) {
-            properties.setModel(GeminiChatModel.MODEL_DEFAULT);
-        }
-        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
-                .openAiClient(OpenAIOkHttpClient.builder()
-                        .baseUrl(GeminiChatModel.BASE_URL)
-                        .apiKey(properties.getApiKey())
-                        .build())
-                .options(OpenAiChatOptions.builder()
-                        .model(properties.getModel())
-                        .temperature(properties.getTemperature())
-                        .maxTokens(properties.getMaxTokens())
-                        .topP(properties.getTopP())
-                        .build())
-                .toolCallingManager(getToolCallingManager())
+    @Bean
+    @ConditionalOnMissingBean
+    @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
+    public DashScopeImageModel dashScopeImageModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
+                                                   ObservationRegistry observationRegistry) {
+        return buildTongYiImagesModel(apiKey, observationRegistry);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
+    public DashScopeEmbeddingModel dashScopeEmbeddingModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
+                                                           ObservationRegistry observationRegistry) {
+        return buildTongYiEmbeddingModel(apiKey, null, observationRegistry);
+    }
+
+    public static DashScopeChatModel buildTongYiChatModel(String apiKey) {
+        return buildTongYiChatModel(apiKey, getToolCallingManager(), getObservationRegistry());
+    }
+
+    private static DashScopeChatModel buildTongYiChatModel(String apiKey, ToolCallingManager toolCallingManager,
+                                                           ObservationRegistry observationRegistry) {
+        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
+        DashScopeChatOptions options = DashScopeChatOptions.builder()
+                .model(DashScopeApi.DEFAULT_CHAT_MODEL)
+                .temperature(0.7)
                 .build();
-        return new GeminiChatModel(openAiChatModel);
+        return new DashScopeChatModel(dashScopeApi, options, toolCallingManager, RetryUtils.DEFAULT_RETRY_TEMPLATE,
+                observationRegistry);
+    }
+
+    public static DashScopeImageModel buildTongYiImagesModel(String apiKey) {
+        return buildTongYiImagesModel(apiKey, getObservationRegistry());
+    }
+
+    private static DashScopeImageModel buildTongYiImagesModel(String apiKey, ObservationRegistry observationRegistry) {
+        DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(apiKey).build();
+        DashScopeImageOptions options = DashScopeImageOptions.builder()
+                .model(DashScopeImageApi.DEFAULT_IMAGE_MODEL)
+                .build();
+        return new DashScopeImageModel(dashScopeImageApi, options, RetryUtils.DEFAULT_RETRY_TEMPLATE,
+                observationRegistry);
+    }
+
+    public static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
+        return buildTongYiEmbeddingModel(apiKey, model, getObservationRegistry());
+    }
+
+    private static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model,
+                                                                     ObservationRegistry observationRegistry) {
+        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
+        DashScopeEmbeddingOptions options = DashScopeEmbeddingOptions.builder()
+                .model(StrUtil.blankToDefault(model, DashScopeApi.DEFAULT_EMBEDDING_MODEL))
+                .build();
+        return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, options, RetryUtils.DEFAULT_RETRY_TEMPLATE,
+                observationRegistry);
     }
 
     @Bean
@@ -112,18 +165,18 @@
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
         }
-        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
-                .openAiClient(OpenAIOkHttpClient.builder()
+        DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
+                .deepSeekApi(DeepSeekApi.builder()
                         .baseUrl(DouBaoChatModel.BASE_URL)
+                        .completionsPath(DouBaoChatModel.COMPLETE_PATH)
                         .apiKey(properties.getApiKey())
                         .build())
-                .options(OpenAiChatOptions.builder()
+                .options(DeepSeekChatOptions.builder()
                         .model(properties.getModel())
                         .temperature(properties.getTemperature())
                         .maxTokens(properties.getMaxTokens())
                         .topP(properties.getTopP())
                         .build())
-                .toolCallingManager(getToolCallingManager())
                 .build();
         return new DouBaoChatModel(openAiChatModel);
     }
@@ -144,13 +197,12 @@
                         .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
                         .apiKey(properties.getApiKey())
                         .build())
-                .defaultOptions(DeepSeekChatOptions.builder()
+                .options(DeepSeekChatOptions.builder()
                         .model(properties.getModel())
                         .temperature(properties.getTemperature())
                         .maxTokens(properties.getMaxTokens())
                         .topP(properties.getTopP())
                         .build())
-                .toolCallingManager(getToolCallingManager())
                 .build();
         return new SiliconFlowChatModel(openAiChatModel);
     }
@@ -166,11 +218,8 @@
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
         }
-        // 鐗规畩锛氱敱浜庢贩鍏冨ぇ妯″瀷涓嶆彁渚� deepseek锛岃�屾槸閫氳繃鐭ヨ瘑寮曟搸锛屾墍浠ラ渶瑕佸尯鍒嗕笅 URL
         if (StrUtil.isEmpty(properties.getBaseUrl())) {
-            properties.setBaseUrl(
-                    StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
-                            : HunYuanChatModel.BASE_URL);
+            properties.setBaseUrl(HunYuanChatModel.BASE_URL);
         }
         // 鍒涘缓 DeepSeekChatModel銆丠unYuanChatModel 瀵硅薄
         DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
@@ -179,13 +228,12 @@
                         .completionsPath(HunYuanChatModel.COMPLETE_PATH)
                         .apiKey(properties.getApiKey())
                         .build())
-                .defaultOptions(DeepSeekChatOptions.builder()
+                .options(DeepSeekChatOptions.builder()
                         .model(properties.getModel())
                         .temperature(properties.getTemperature())
                         .maxTokens(properties.getMaxTokens())
                         .topP(properties.getTopP())
                         .build())
-                .toolCallingManager(getToolCallingManager())
                 .build();
         return new HunYuanChatModel(openAiChatModel);
     }
@@ -201,24 +249,15 @@
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
         }
-        OpenAIOkHttpClient.Builder builder = OpenAIOkHttpClient.builder()
-                .baseUrl(XingHuoChatModel.BASE_URL_V1)
-                .apiKey(properties.getAppKey() + ":" + properties.getSecretKey());
-        if ("x1".equals(properties.getModel())) {
-            builder.baseUrl(XingHuoChatModel.BASE_URL_V2);
-        }
-        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
-                .openAiClient(builder.build())
-                .options(OpenAiChatOptions.builder()
+        return XingHuoChatModel.builder()
+                .apiKey(properties.getApiKey())
+                .options(DeepSeekChatOptions.builder()
                         .model(properties.getModel())
                         .temperature(properties.getTemperature())
                         .maxTokens(properties.getMaxTokens())
                         .topP(properties.getTopP())
                         .build())
-                // TODO @鑺嬭壙锛氭槦鐏殑 function call 鏈� bug锛屼細鎶� ToolResponseMessage must have an id 閿欒锛侊紒锛�
-                .toolCallingManager(getToolCallingManager())
                 .build();
-        return new XingHuoChatModel(openAiChatModel);
     }
 
     @Bean
@@ -232,20 +271,135 @@
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
         }
-        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
-                .openAiClient(OpenAIOkHttpClient.builder()
+        DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
+                .deepSeekApi(DeepSeekApi.builder()
                         .baseUrl(BaiChuanChatModel.BASE_URL)
                         .apiKey(properties.getApiKey())
+                        .completionsPath(BaiChuanChatModel.COMPLETE_PATH)
                         .build())
-                .options(OpenAiChatOptions.builder()
+                .options(DeepSeekChatOptions.builder()
                         .model(properties.getModel())
                         .temperature(properties.getTemperature())
                         .maxTokens(properties.getMaxTokens())
                         .topP(properties.getTopP())
                         .build())
-                .toolCallingManager(getToolCallingManager())
                 .build();
-        return new BaiChuanChatModel(openAiChatModel);
+        return new BaiChuanChatModel(deepSeekChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.yiyan.enable", havingValue = "true")
+    public YiYanChatModel yiYanChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.YiYan properties = yudaoAiProperties.getYiyan();
+        return buildYiYanChatClient(properties);
+    }
+
+    public YiYanChatModel buildYiYanChatClient(YudaoAiProperties.YiYan properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(YiYanChatModel.MODEL_DEFAULT);
+        }
+        return new YiYanChatModel(buildDeepSeekCompatibleChatModel(
+                StrUtil.blankToDefault(properties.getBaseUrl(), YiYanChatModel.BASE_URL),
+                null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
+                properties.getMaxTokens(), properties.getTopP()));
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.zhipu.enable", havingValue = "true")
+    public ZhiPuChatModel zhiPuChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.ZhiPu properties = yudaoAiProperties.getZhipu();
+        return buildZhiPuChatClient(properties);
+    }
+
+    public ZhiPuChatModel buildZhiPuChatClient(YudaoAiProperties.ZhiPu properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(ZhiPuChatModel.MODEL_DEFAULT);
+        }
+        DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
+                .deepSeekApi(DeepSeekApi.builder()
+                        .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), ZhiPuChatModel.BASE_URL))
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .options(DeepSeekChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .build();
+        return new ZhiPuChatModel(deepSeekChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.minimax.enable", havingValue = "true")
+    public MiniMaxChatModel miniMaxChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.MiniMax properties = yudaoAiProperties.getMinimax();
+        return buildMiniMaxChatClient(properties);
+    }
+
+    public MiniMaxChatModel buildMiniMaxChatClient(YudaoAiProperties.MiniMax properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(MiniMaxChatModel.MODEL_DEFAULT);
+        }
+        return new MiniMaxChatModel(buildDeepSeekCompatibleChatModel(
+                StrUtil.blankToDefault(properties.getBaseUrl(), MiniMaxChatModel.BASE_URL),
+                null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
+                properties.getMaxTokens(), properties.getTopP()));
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.moonshot.enable", havingValue = "true")
+    public MoonshotChatModel moonshotChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.Moonshot properties = yudaoAiProperties.getMoonshot();
+        return buildMoonshotChatClient(properties);
+    }
+
+    public MoonshotChatModel buildMoonshotChatClient(YudaoAiProperties.Moonshot properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(MoonshotChatModel.MODEL_DEFAULT);
+        }
+        return new MoonshotChatModel(buildDeepSeekCompatibleChatModel(
+                StrUtil.blankToDefault(properties.getBaseUrl(), MoonshotChatModel.BASE_URL),
+                MoonshotChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
+                properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.stepfun.enable", havingValue = "true")
+    public StepFunChatModel stepFunChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.StepFun properties = yudaoAiProperties.getStepfun();
+        return buildStepFunChatClient(properties);
+    }
+
+    public StepFunChatModel buildStepFunChatClient(YudaoAiProperties.StepFun properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(StepFunChatModel.MODEL_DEFAULT);
+        }
+        return new StepFunChatModel(buildDeepSeekCompatibleChatModel(
+                StrUtil.blankToDefault(properties.getBaseUrl(), StepFunChatModel.BASE_URL),
+                StepFunChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
+                properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
+    }
+
+    private static DeepSeekChatModel buildDeepSeekCompatibleChatModel(String baseUrl, String completionsPath,
+                                                                     String apiKey, String model,
+                                                                     Double temperature, Integer maxTokens,
+                                                                     Double topP) {
+        DeepSeekApi.Builder apiBuilder = DeepSeekApi.builder()
+                .baseUrl(baseUrl)
+                .apiKey(apiKey);
+        if (StrUtil.isNotEmpty(completionsPath)) {
+            apiBuilder.completionsPath(completionsPath);
+        }
+        return DeepSeekChatModel.builder()
+                .deepSeekApi(apiBuilder.build())
+                .options(DeepSeekChatOptions.builder()
+                        .model(model)
+                        .temperature(temperature)
+                        .maxTokens(maxTokens)
+                        .topP(topP)
+                        .build())
+                .build();
     }
 
     @Bean
@@ -261,25 +415,28 @@
         return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
     }
 
-    public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
+    @Bean
+    @ConditionalOnProperty(value = "yudao.ai.grok.enable", havingValue = "true")
+    public GrokChatModel grokChatClient(YudaoAiProperties yudaoAiProperties) {
+        YudaoAiProperties.Grok properties = yudaoAiProperties.getGrok();
+        return buildGrokChatClient(properties);
+    }
+
+    public GrokChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(GrokChatModel.MODEL_DEFAULT);
         }
         OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
-                .openAiClient(OpenAIOkHttpClient.builder()
-                        .baseUrl(Optional.ofNullable(properties.getBaseUrl())
-                                .orElse(GrokChatModel.BASE_URL))
-                        .apiKey(properties.getApiKey())
-                        .build())
                 .options(OpenAiChatOptions.builder()
+                        .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), GrokChatModel.BASE_URL))
+                        .apiKey(properties.getApiKey())
                         .model(properties.getModel())
                         .temperature(properties.getTemperature())
                         .maxTokens(properties.getMaxTokens())
                         .topP(properties.getTopP())
                         .build())
-                .toolCallingManager(getToolCallingManager())
                 .build();
-        return new DouBaoChatModel(openAiChatModel);
+        return new GrokChatModel(openAiChatModel);
     }
 
     // ========== RAG 鐩稿叧 ==========
@@ -298,6 +455,10 @@
         return SpringUtil.getBean(ToolCallingManager.class);
     }
 
+    private static ObservationRegistry getObservationRegistry() {
+        return SpringUtil.getBean(ObservationRegistry.class);
+    }
+
     // ========== Web Search 鐩稿叧 ==========
 
     @Bean

--
Gitblit v1.9.3