From ef874191a6290a07591806d6ddab1ce2edd5c0b6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 14 Jul 2025 23:35:56 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E3=80=90AI=20=E5=A4=A7=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E3=80=91=E4=BE=9D=E8=B5=96=20spring=20ai=20=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E5=88=B0=201.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-module-ai-server/pom.xml | 55 ++-- .../yudao/module/ai/AiServerApplication.java | 4 +- .../ai/config/AiAutoConfiguration.java | 36 +-- .../ai/config/YudaoAiProperties.java | 19 -- .../framework/ai/core/AiModelFactoryImpl.java | 234 +++++++++++------- .../model/deepseek/DeepSeekChatModel.java | 45 ---- .../siliconflow/SiliconFlowImageModel.java | 122 ++++----- .../ai/service/image/AiImageServiceImpl.java | 10 +- .../tool/UserProfileQueryToolFunction.java | 31 ++- .../iocoder/yudao/module/ai/util/AiUtils.java | 14 +- .../src/main/resources/application-dev.yaml | 4 +- .../src/main/resources/application-local.yaml | 4 +- .../src/main/resources/application.yaml | 11 +- .../model/chat/AzureOpenAIChatModelTests.java | 14 +- .../model/chat/BaiChuanChatModelTests.java | 3 +- .../model/chat/DeepSeekChatModelTests.java | 18 +- .../core/model/chat/LlamaChatModelTests.java | 90 +++---- .../model/chat/MoonshotChatModelTests.java | 20 +- .../core/model/chat/OllamaChatModelTests.java | 4 +- .../core/model/chat/OpenAIChatModelTests.java | 4 +- .../core/model/chat/TongYiChatModelTests.java | 11 +- .../core/model/chat/YiYanChatModelTests.java | 10 +- .../model/image/OpenAiImageModelTests.java | 6 +- .../core/model/image/QianFanImageTests.java | 6 +- .../image/StabilityAiImageModelTests.java | 4 +- .../model/ppt/wdd/WenDuoDuoPptApiTests.java | 2 +- .../model/ppt/xunfei/XunFeiPptApiTests.java | 2 +- .../src/main/resources/application-dev.yaml | 4 +- .../src/main/resources/application-local.yaml | 4 +- .../src/main/resources/application.yaml | 11 +- 30 files changed, 387 insertions(+), 415 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/deepseek/DeepSeekChatModel.java diff --git a/yudao-module-ai/yudao-module-ai-server/pom.xml b/yudao-module-ai/yudao-module-ai-server/pom.xml index 64fa331ef..10d4c3c14 100644 --- a/yudao-module-ai/yudao-module-ai-server/pom.xml +++ b/yudao-module-ai/yudao-module-ai-server/pom.xml @@ -14,11 +14,12 @@ ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维导图等功能。 目前已接入各种模型,不限于: - 国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek - 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno + 国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek + 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno - 1.0.0-M6 + 1.0.0 + 1.0.0.2 1.0.2 @@ -110,65 +111,73 @@ org.springframework.ai - spring-ai-openai-spring-boot-starter + spring-ai-starter-model-openai ${spring-ai.version} org.springframework.ai - spring-ai-azure-openai-spring-boot-starter + spring-ai-starter-model-azure-openai ${spring-ai.version} org.springframework.ai - spring-ai-ollama-spring-boot-starter + spring-ai-starter-model-deepseek ${spring-ai.version} org.springframework.ai - spring-ai-stability-ai-spring-boot-starter + spring-ai-starter-model-ollama ${spring-ai.version} - - com.alibaba.cloud.ai - spring-ai-alibaba-starter - ${spring-ai.version}.1 - - - org.springframework.ai - spring-ai-qianfan-spring-boot-starter + spring-ai-starter-model-stability-ai ${spring-ai.version} org.springframework.ai - spring-ai-zhipuai-spring-boot-starter + spring-ai-starter-model-zhipuai ${spring-ai.version} org.springframework.ai - spring-ai-minimax-spring-boot-starter + spring-ai-starter-model-minimax ${spring-ai.version} + - org.springframework.ai - spring-ai-moonshot-spring-boot-starter - ${spring-ai.version} + + com.alibaba.cloud.ai + spring-ai-alibaba-starter-dashscope + ${alibaba-ai.version} + + + + + org.springaicommunity + qianfan-spring-boot-starter + 1.0.0 + + + + org.springaicommunity + moonshot-spring-boot-starter + 1.0.0 org.springframework.ai - spring-ai-qdrant-store + spring-ai-starter-vector-store-qdrant ${spring-ai.version} org.springframework.ai - spring-ai-redis-store + spring-ai-starter-vector-store-redis ${spring-ai.version} @@ -179,7 +188,7 @@ org.springframework.ai - spring-ai-milvus-store + spring-ai-starter-vector-store-milvus ${spring-ai.version} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/AiServerApplication.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/AiServerApplication.java index a890e0844..ec92b096f 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/AiServerApplication.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/AiServerApplication.java @@ -13,8 +13,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * @author 芋道源码 */ @SpringBootApplication(exclude = { - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration.class, - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration.class, + org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration.class, + org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration.class, }) // 解决 application-${profile}.yaml 配置文件下,通过 spring.autoconfigure.exclude 无法排除的问题 public class AiServerApplication { diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java index a28d726b9..4ff7c9e4d 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java @@ -5,7 +5,6 @@ import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactory; import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactoryImpl; import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; 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; @@ -14,10 +13,6 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlo 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 lombok.extern.slf4j.Slf4j; -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; -import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.model.tool.ToolCallingManager; @@ -26,6 +21,10 @@ import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; import org.springframework.ai.tokenizer.TokenCountEstimator; +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties; +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; +import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; +import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -52,33 +51,6 @@ public class AiAutoConfiguration { // ========== 各种 AI Client 创建 ========== - @Bean - @ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true") - public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) { - YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepseek(); - return buildDeepSeekChatModel(properties); - } - - public DeepSeekChatModel buildDeepSeekChatModel(YudaoAiProperties.DeepSeekProperties properties) { - if (StrUtil.isEmpty(properties.getModel())) { - properties.setModel(DeepSeekChatModel.MODEL_DEFAULT); - } - OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() - .baseUrl(DeepSeekChatModel.BASE_URL) - .apiKey(properties.getApiKey()) - .build()) - .defaultOptions(OpenAiChatOptions.builder() - .model(properties.getModel()) - .temperature(properties.getTemperature()) - .maxTokens(properties.getMaxTokens()) - .topP(properties.getTopP()) - .build()) - .toolCallingManager(getToolCallingManager()) - .build(); - return new DeepSeekChatModel(openAiChatModel); - } - @Bean @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true") public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) { diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java index 7f8046768..7c26aa89c 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java @@ -13,12 +13,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @Data public class YudaoAiProperties { - /** - * DeepSeek - */ - @SuppressWarnings("SpellCheckingInspection") - private DeepSeekProperties deepseek; - /** * 字节豆包 */ @@ -60,19 +54,6 @@ public class YudaoAiProperties { @SuppressWarnings("SpellCheckingInspection") private SunoProperties suno; - @Data - public static class DeepSeekProperties { - - private String enable; - private String apiKey; - - private String model; - private Double temperature; - private Integer maxTokens; - private Double topP; - - } - @Data public static class DouBaoProperties { diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactoryImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactoryImpl.java index f258ffaf1..f7b42e30a 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactoryImpl.java @@ -8,11 +8,11 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; +import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration; import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties; -import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; 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; @@ -22,8 +22,9 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlo import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel; 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 cn.iocoder.yudao.framework.common.util.spring.SpringUtils; -import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration; +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration; +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; @@ -32,47 +33,55 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.KeyCredential; import io.micrometer.observation.ObservationRegistry; import io.milvus.client.MilvusServiceClient; import io.qdrant.client.QdrantClient; import io.qdrant.client.QdrantGrpcClient; import lombok.SneakyThrows; -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties; -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiEmbeddingProperties; -import org.springframework.ai.autoconfigure.minimax.MiniMaxAutoConfiguration; -import org.springframework.ai.autoconfigure.moonshot.MoonshotAutoConfiguration; -import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; -import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; -import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; -import org.springframework.ai.autoconfigure.stabilityai.StabilityAiImageAutoConfiguration; -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientConnectionDetails; -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration; -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; -import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; -import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration; -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; -import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; +import org.springaicommunity.moonshot.MoonshotChatModel; +import org.springaicommunity.moonshot.MoonshotChatOptions; +import org.springaicommunity.moonshot.api.MoonshotApi; +import org.springaicommunity.moonshot.autoconfigure.MoonshotChatAutoConfiguration; +import org.springaicommunity.qianfan.QianFanChatModel; +import org.springaicommunity.qianfan.QianFanEmbeddingModel; +import org.springaicommunity.qianfan.QianFanEmbeddingOptions; +import org.springaicommunity.qianfan.QianFanImageModel; +import org.springaicommunity.qianfan.api.QianFanApi; +import org.springaicommunity.qianfan.api.QianFanImageApi; +import org.springaicommunity.qianfan.autoconfigure.QianFanChatAutoConfiguration; +import org.springaicommunity.qianfan.autoconfigure.QianFanEmbeddingAutoConfiguration; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; 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.EmbeddingModel; +import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention; import org.springframework.ai.image.ImageModel; import org.springframework.ai.minimax.MiniMaxChatModel; import org.springframework.ai.minimax.MiniMaxChatOptions; import org.springframework.ai.minimax.MiniMaxEmbeddingModel; import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; import org.springframework.ai.minimax.api.MiniMaxApi; -import org.springframework.ai.model.function.FunctionCallbackResolver; +import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration; +import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration; +import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties; +import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; +import org.springframework.ai.model.minimax.autoconfigure.MiniMaxChatAutoConfiguration; +import org.springframework.ai.model.minimax.autoconfigure.MiniMaxEmbeddingAutoConfiguration; +import org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration; +import org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration; +import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration; +import org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration; +import org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration; import org.springframework.ai.model.tool.ToolCallingManager; -import org.springframework.ai.moonshot.MoonshotChatModel; -import org.springframework.ai.moonshot.MoonshotChatOptions; -import org.springframework.ai.moonshot.api.MoonshotApi; +import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration; +import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiEmbeddingAutoConfiguration; +import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfiguration; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaEmbeddingModel; import org.springframework.ai.ollama.api.OllamaApi; @@ -84,21 +93,23 @@ import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiImageApi; import org.springframework.ai.openai.api.common.OpenAiApiConstants; -import org.springframework.ai.qianfan.QianFanChatModel; -import org.springframework.ai.qianfan.QianFanEmbeddingModel; -import org.springframework.ai.qianfan.QianFanEmbeddingOptions; -import org.springframework.ai.qianfan.QianFanImageModel; -import org.springframework.ai.qianfan.api.QianFanApi; -import org.springframework.ai.qianfan.api.QianFanImageApi; import org.springframework.ai.stabilityai.StabilityAiImageModel; import org.springframework.ai.stabilityai.api.StabilityAiApi; import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.milvus.MilvusVectorStore; +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientConnectionDetails; +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties; +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration; +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; +import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration; +import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; import org.springframework.ai.vectorstore.redis.RedisVectorStore; +import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration; +import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties; import org.springframework.ai.zhipuai.*; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; @@ -190,7 +201,7 @@ public class AiModelFactoryImpl implements AiModelFactory { case XING_HUO: return SpringUtil.getBean(XingHuoChatModel.class); case BAI_CHUAN: - return SpringUtil.getBean(AzureOpenAiChatModel.class); + return SpringUtil.getBean(BaiChuanChatModel.class); case OPENAI: return SpringUtil.getBean(OpenAiChatModel.class); case AZURE_OPENAI: @@ -319,27 +330,34 @@ public class AiModelFactoryImpl implements AiModelFactory { // ========== 各种创建 spring-ai 客户端的方法 ========== /** - * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeChatModel 方法 + * 可参考 {@link DashScopeChatAutoConfiguration} 的 dashscopeChatModel 方法 */ private static DashScopeChatModel buildTongYiChatModel(String key) { - DashScopeApi dashScopeApi = new DashScopeApi(key); + DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(key).build(); DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) .withTemperature(0.7).build(); - return new DashScopeChatModel(dashScopeApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); + return DashScopeChatModel.builder() + .dashScopeApi(dashScopeApi) + .defaultOptions(options) + .toolCallingManager(getToolCallingManager()) + .build(); } /** - * 可参考 {@link DashScopeAutoConfiguration} 的 dashScopeImageModel 方法 + * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法 */ private static DashScopeImageModel buildTongYiImagesModel(String key) { DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key); - return new DashScopeImageModel(dashScopeImageApi); + return DashScopeImageModel.builder() + .dashScopeApi(dashScopeImageApi) + .build(); } /** - * 可参考 {@link QianFanAutoConfiguration} 的 qianFanChatModel 方法 + * 可参考 {@link QianFanChatAutoConfiguration} 的 qianFanChatModel 方法 */ private static QianFanChatModel buildYiYanChatModel(String key) { + // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 List keys = StrUtil.split(key, '|'); Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); String appKey = keys.get(0); @@ -349,9 +367,10 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link QianFanAutoConfiguration} 的 qianFanImageModel 方法 + * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanImageModel 方法 */ private QianFanImageModel buildQianFanImageModel(String key) { + // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 List keys = StrUtil.split(key, '|'); Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); String appKey = keys.get(0); @@ -361,12 +380,17 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link AiAutoConfiguration#deepSeekChatModel(YudaoAiProperties)} + * 可参考 {@link DeepSeekChatAutoConfiguration} 的 deepSeekChatModel 方法 */ private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) { - YudaoAiProperties.DeepSeekProperties properties = new YudaoAiProperties.DeepSeekProperties() - .setApiKey(apiKey); - return new AiAutoConfiguration().buildDeepSeekChatModel(properties); + DeepSeekApi deepSeekApi = DeepSeekApi.builder().apiKey(apiKey).build(); + DeepSeekChatOptions options = DeepSeekChatOptions.builder().model(DeepSeekApi.DEFAULT_CHAT_MODEL) + .temperature(0.7).build(); + return DeepSeekChatModel.builder() + .deepSeekApi(deepSeekApi) + .defaultOptions(options) + .toolCallingManager(getToolCallingManager()) + .build(); } /** @@ -397,17 +421,18 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiChatModel 方法 + * 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法 */ private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) : new ZhiPuAiApi(url, apiKey); ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); - return new ZhiPuAiChatModel(zhiPuAiApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); + return new ZhiPuAiChatModel(zhiPuAiApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE, + getObservationRegistry().getIfAvailable()); } /** - * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiImageModel 方法 + * 可参考 {@link ZhiPuAiImageAutoConfiguration} 的 zhiPuAiImageModel 方法 */ private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey) @@ -416,23 +441,30 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxChatModel 方法 + * 可参考 {@link MiniMaxChatAutoConfiguration} 的 miniMaxChatModel 方法 */ private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) : new MiniMaxApi(url, apiKey); MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); - return new MiniMaxChatModel(miniMaxApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); + return new MiniMaxChatModel(miniMaxApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE); } /** - * 可参考 {@link MoonshotAutoConfiguration} 的 moonshotChatModel 方法 + * 可参考 {@link MoonshotChatAutoConfiguration} 的 moonshotChatModel 方法 */ private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { - MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) - : new MoonshotApi(url, apiKey); + MoonshotApi.Builder moonshotApiBuilder = MoonshotApi.builder() + .apiKey(apiKey); + if (StrUtil.isNotEmpty(url)) { + moonshotApiBuilder.baseUrl(url); + } MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); - return new MoonshotChatModel(moonshotApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); + return MoonshotChatModel.builder() + .moonshotApi(moonshotApiBuilder.build()) + .defaultOptions(options) + .toolCallingManager(getToolCallingManager()) + .build(); } /** @@ -456,33 +488,32 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法 + * 可参考 {@link OpenAiChatAutoConfiguration} 的 openAiChatModel 方法 */ private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); - return OpenAiChatModel.builder().openAiApi(openAiApi).toolCallingManager(getToolCallingManager()).build(); + return OpenAiChatModel.builder() + .openAiApi(openAiApi) + .toolCallingManager(getToolCallingManager()) + .build(); } - // TODO @芋艿:手头暂时没密钥,使用建议再测试下 /** - * 可参考 {@link AzureOpenAiAutoConfiguration} + * 可参考 {@link AzureOpenAiChatAutoConfiguration} */ private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) { - AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration(); - // 创建 OpenAIClient 对象 - AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties(); - connectionProperties.setApiKey(apiKey); - connectionProperties.setEndpoint(url); - OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null); - // 获取 AzureOpenAiChatProperties 对象 - AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); - return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, - getToolCallingManager(), null, null); + // TODO @芋艿:使用前,请测试,暂时没密钥!!! + OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() + .endpoint(url).credential(new KeyCredential(apiKey)); + return AzureOpenAiChatModel.builder() + .openAIClientBuilder(openAIClientBuilder) + .toolCallingManager(getToolCallingManager()) + .build(); } /** - * 可参考 {@link OpenAiAutoConfiguration} 的 openAiImageModel 方法 + * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法 */ private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); @@ -500,11 +531,14 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link OllamaAutoConfiguration} 的 ollamaApi 方法 + * 可参考 {@link OllamaChatAutoConfiguration} 的 ollamaChatModel 方法 */ private static OllamaChatModel buildOllamaChatModel(String url) { - OllamaApi ollamaApi = new OllamaApi(url); - return OllamaChatModel.builder().ollamaApi(ollamaApi).toolCallingManager(getToolCallingManager()).build(); + OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); + return OllamaChatModel.builder() + .ollamaApi(ollamaApi) + .toolCallingManager(getToolCallingManager()) + .build(); } /** @@ -519,16 +553,16 @@ public class AiModelFactoryImpl implements AiModelFactory { // ========== 各种创建 EmbeddingModel 的方法 ========== /** - * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeEmbeddingModel 方法 + * 可参考 {@link DashScopeEmbeddingAutoConfiguration} 的 dashscopeEmbeddingModel 方法 */ private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { - DashScopeApi dashScopeApi = new DashScopeApi(apiKey); + DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); } /** - * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 + * 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 */ private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) @@ -538,7 +572,7 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxEmbeddingModel 方法 + * 可参考 {@link MiniMaxEmbeddingAutoConfiguration} 的 miniMaxEmbeddingModel 方法 */ private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) { MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey) @@ -548,7 +582,7 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link QianFanAutoConfiguration} 的 qianFanEmbeddingModel 方法 + * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanEmbeddingModel 方法 */ private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) { List keys = StrUtil.split(key, '|'); @@ -561,13 +595,16 @@ public class AiModelFactoryImpl implements AiModelFactory { } private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { - OllamaApi ollamaApi = new OllamaApi(url); + OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build(); - return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).defaultOptions(ollamaOptions).build(); + return OllamaEmbeddingModel.builder() + .ollamaApi(ollamaApi) + .defaultOptions(ollamaOptions) + .build(); } /** - * 可参考 {@link OpenAiAutoConfiguration} 的 openAiEmbeddingModel 方法 + * 可参考 {@link OpenAiEmbeddingAutoConfiguration} 的 openAiEmbeddingModel 方法 */ private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) { url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); @@ -576,21 +613,19 @@ public class AiModelFactoryImpl implements AiModelFactory { return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties); } - // TODO @芋艿:手头暂时没密钥,使用建议再测试下 /** - * 可参考 {@link AzureOpenAiAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 + * 可参考 {@link AzureOpenAiEmbeddingAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 */ private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) { - AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration(); - // 创建 OpenAIClient 对象 - AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties(); - connectionProperties.setApiKey(apiKey); - connectionProperties.setEndpoint(url); - OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null); + // TODO @芋艿:手头暂时没密钥,使用建议再测试下 + AzureOpenAiEmbeddingAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiEmbeddingAutoConfiguration(); + // 创建 OpenAIClientBuilder 对象 + OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() + .endpoint(url).credential(new KeyCredential(apiKey)); // 获取 AzureOpenAiChatProperties 对象 AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class); - return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClient, embeddingProperties, - null, null); + return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClientBuilder, embeddingProperties, + getObservationRegistry(), getEmbeddingModelObservationConvention()); } // ========== 各种创建 VectorStore 的方法 ========== @@ -655,12 +690,12 @@ public class AiModelFactoryImpl implements AiModelFactory { Map> metadataFields) { // 创建 JedisPooled 对象 RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); - JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort()); + JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort(), + redisProperties.getUsername(), redisProperties.getPassword()); // 创建 RedisVectorStoreProperties 对象 - RedisVectorStoreAutoConfiguration configuration = new RedisVectorStoreAutoConfiguration(); RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class); RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel) - .indexName(properties.getIndex()).prefix(properties.getPrefix()) + .indexName(properties.getIndexName()).prefix(properties.getPrefix()) .initializeSchema(properties.isInitializeSchema()) .metadataFields(convertList(metadataFields.entrySet(), entry -> { String fieldName = entry.getKey(); @@ -730,10 +765,12 @@ public class AiModelFactoryImpl implements AiModelFactory { private static ObjectProvider getCustomObservationConvention() { return new ObjectProvider<>() { + @Override public VectorStoreObservationConvention getObject() throws BeansException { return new DefaultVectorStoreObservationConvention(); } + }; } @@ -745,8 +782,15 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(ToolCallingManager.class); } - private static FunctionCallbackResolver getFunctionCallbackResolver() { - return SpringUtil.getBean(FunctionCallbackResolver.class); + private static ObjectProvider getEmbeddingModelObservationConvention() { + return new ObjectProvider<>() { + + @Override + public EmbeddingModelObservationConvention getObject() throws BeansException { + return SpringUtil.getBean(EmbeddingModelObservationConvention.class); + } + + }; } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/deepseek/DeepSeekChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/deepseek/DeepSeekChatModel.java deleted file mode 100644 index d603abf6b..000000000 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/deepseek/DeepSeekChatModel.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek; - -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; - -/** - * DeepSeek {@link ChatModel} 实现类 - * - * @author fansili - */ -@Slf4j -@RequiredArgsConstructor -public class DeepSeekChatModel implements ChatModel { - - public static final String BASE_URL = "https://api.deepseek.com"; - - public static final String MODEL_DEFAULT = "deepseek-chat"; - - /** - * 兼容 OpenAI 接口,进行复用 - */ - private final OpenAiChatModel openAiChatModel; - - @Override - public ChatResponse call(Prompt prompt) { - return openAiChatModel.call(prompt); - } - - @Override - public Flux stream(Prompt prompt) { - return openAiChatModel.stream(prompt); - } - - @Override - public ChatOptions getDefaultOptions() { - return openAiChatModel.getDefaultOptions(); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java index 43f8ad216..f205b80fc 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java @@ -46,94 +46,94 @@ import java.util.List; */ public class SiliconFlowImageModel implements ImageModel { - private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class); + private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class); - private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention(); + private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention(); - private final SiliconFlowImageOptions defaultOptions; + private final SiliconFlowImageOptions defaultOptions; - private final RetryTemplate retryTemplate; + private final RetryTemplate retryTemplate; - private final SiliconFlowImageApi siliconFlowImageApi; + private final SiliconFlowImageApi siliconFlowImageApi; - private final ObservationRegistry observationRegistry; + private final ObservationRegistry observationRegistry; @Setter - private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION; + private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION; - public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) { - this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); - } + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) { + this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); + } - public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) { - this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP); - } + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) { + this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP); + } - public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate, + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate, ObservationRegistry observationRegistry) { - Assert.notNull(siliconFlowImageApi, "OpenAiImageApi must not be null"); - Assert.notNull(options, "options must not be null"); - Assert.notNull(retryTemplate, "retryTemplate must not be null"); - Assert.notNull(observationRegistry, "observationRegistry must not be null"); - this.siliconFlowImageApi = siliconFlowImageApi; - this.defaultOptions = options; - this.retryTemplate = retryTemplate; - this.observationRegistry = observationRegistry; - } + Assert.notNull(siliconFlowImageApi, "OpenAiImageApi must not be null"); + Assert.notNull(options, "options must not be null"); + Assert.notNull(retryTemplate, "retryTemplate must not be null"); + Assert.notNull(observationRegistry, "observationRegistry must not be null"); + this.siliconFlowImageApi = siliconFlowImageApi; + this.defaultOptions = options; + this.retryTemplate = retryTemplate; + this.observationRegistry = observationRegistry; + } - @Override - public ImageResponse call(ImagePrompt imagePrompt) { + @Override + public ImageResponse call(ImagePrompt imagePrompt) { SiliconFlowImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions); SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions); - var observationContext = ImageModelObservationContext.builder() - .imagePrompt(imagePrompt) - .provider(SiliconFlowApiConstants.PROVIDER_NAME) - .requestOptions(imagePrompt.getOptions()) - .build(); + var observationContext = ImageModelObservationContext.builder() + .imagePrompt(imagePrompt) + .provider(SiliconFlowApiConstants.PROVIDER_NAME) + .imagePrompt(imagePrompt) + .build(); - return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION - .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, - this.observationRegistry) - .observe(() -> { - ResponseEntity imageResponseEntity = this.retryTemplate - .execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest)); + return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION + .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, + this.observationRegistry) + .observe(() -> { + ResponseEntity imageResponseEntity = this.retryTemplate + .execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest)); - ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest); + ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest); - observationContext.setResponse(imageResponse); + observationContext.setResponse(imageResponse); - return imageResponse; - }); - } + return imageResponse; + }); + } - private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt, + private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt, SiliconFlowImageOptions requestImageOptions) { - String instructions = imagePrompt.getInstructions().get(0).getText(); + String instructions = imagePrompt.getInstructions().get(0).getText(); - SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions, + SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions, SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL); - return ModelOptionsUtils.merge(requestImageOptions, imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class); - } + return ModelOptionsUtils.merge(requestImageOptions, imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class); + } - private ImageResponse convertResponse(ResponseEntity imageResponseEntity, - SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) { - OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody(); - if (imageApiResponse == null) { - logger.warn("No image response returned for request: {}", siliconflowImageRequest); - return new ImageResponse(List.of()); - } + private ImageResponse convertResponse(ResponseEntity imageResponseEntity, + SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) { + OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody(); + if (imageApiResponse == null) { + logger.warn("No image response returned for request: {}", siliconflowImageRequest); + return new ImageResponse(List.of()); + } - List imageGenerationList = imageApiResponse.data() - .stream() - .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()), - new OpenAiImageGenerationMetadata(entry.revisedPrompt()))) - .toList(); + List imageGenerationList = imageApiResponse.data() + .stream() + .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()), + new OpenAiImageGenerationMetadata(entry.revisedPrompt()))) + .toList(); - ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created()); - return new ImageResponse(imageGenerationList, openAiImageResponseMetadata); - } + ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created()); + return new ImageResponse(imageGenerationList, openAiImageResponseMetadata); + } private SiliconFlowImageOptions mergeOptions(@Nullable ImageOptions runtimeOptions, SiliconFlowImageOptions defaultOptions) { var runtimeOptionsForProvider = ModelOptionsUtils.copyToTarget(runtimeOptions, ImageOptions.class, diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java index 6fe16e930..f5f0c5054 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java @@ -29,12 +29,12 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springaicommunity.qianfan.QianFanImageOptions; import org.springframework.ai.image.ImageModel; import org.springframework.ai.image.ImageOptions; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; import org.springframework.ai.openai.OpenAiImageOptions; -import org.springframework.ai.qianfan.QianFanImageOptions; import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; import org.springframework.ai.zhipuai.ZhiPuAiImageOptions; import org.springframework.scheduling.annotation.Async; @@ -140,10 +140,10 @@ public class AiImageServiceImpl implements AiImageService { private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) { if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { // https://platform.openai.com/docs/api-reference/images/create - return OpenAiImageOptions.builder().withModel(model.getModel()) - .withHeight(draw.getHeight()).withWidth(draw.getWidth()) - .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 - .withResponseFormat("b64_json") + return OpenAiImageOptions.builder().model(model.getModel()) + .height(draw.getHeight()).width(draw.getWidth()) + .style(MapUtil.getStr(draw.getOptions(), "style")) // 风格 + .responseFormat("b64_json") .build(); } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) { // https://docs.siliconflow.cn/cn/api-reference/images/images-generations diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java index da5d483aa..079f3a42f 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; import jakarta.annotation.Resource; import lombok.AllArgsConstructor; import lombok.Data; @@ -17,7 +19,7 @@ import org.springframework.stereotype.Component; import java.util.function.BiFunction; /** - * 工具:当前用户信息查询 + * 工具:用户信息查询 * * 同时,也是展示 ToolContext 上下文的使用 * @@ -31,8 +33,17 @@ public class UserProfileQueryToolFunction private AdminUserApi adminUserApi; @Data - @JsonClassDescription("当前用户信息查询") - public static class Request { } + @JsonClassDescription("用户信息查询") + public static class Request { + + /** + * 用户编号 + */ + @JsonProperty(value = "id") + @JsonPropertyDescription("用户编号,例如说:1。如果查询自己,则 id 为空") + private Long id; + + } @Data @AllArgsConstructor @@ -61,13 +72,19 @@ public class UserProfileQueryToolFunction @Override public Response apply(Request request, ToolContext toolContext) { - LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER); Long tenantId = (Long) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_TENANT_ID); - if (loginUser == null | tenantId == null) { - return null; + if (tenantId == null) { + return new Response(); + } + if (request.getId() == null) { + LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER); + if (loginUser == null) { + return new Response(); + } + request.setId(loginUser.getId()); } return TenantUtils.execute(tenantId, () -> { - AdminUserRespDTO user = adminUserApi.getUser(loginUser.getId()).getCheckedData(); + AdminUserRespDTO user = adminUserApi.getUser(request.getId()).getCheckedData(); return BeanUtils.toBean(user, Response.class); }); } diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java index ac3ff39a4..0744ff630 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java @@ -2,18 +2,18 @@ package cn.iocoder.yudao.module.ai.util; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; +import org.springaicommunity.moonshot.MoonshotChatOptions; +import org.springaicommunity.qianfan.QianFanChatOptions; import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.moonshot.MoonshotChatOptions; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.qianfan.QianFanChatOptions; import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; import java.util.Collections; @@ -43,18 +43,18 @@ public class AiUtils { switch (platform) { case TONG_YI: return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) - .withFunctions(toolNames).withToolContext(toolContext).build(); + .withToolNames(toolNames).withToolContext(toolContext).build(); case YI_YAN: return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .functions(toolNames).toolContext(toolContext).build(); + .toolNames(toolNames).toolContext(toolContext).build(); case MINI_MAX: return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .functions(toolNames).toolContext(toolContext).build(); + .toolNames(toolNames).toolContext(toolContext).build(); case MOONSHOT: return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .functions(toolNames).toolContext(toolContext).build(); + .toolNames(toolNames).toolContext(toolContext).build(); case OPENAI: case DEEP_SEEK: // 复用 OpenAI 客户端 case DOU_BAO: // 复用 OpenAI 客户端 diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml index 6c1556319..0c1b4a1f6 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-dev.yaml @@ -20,8 +20,8 @@ spring: # 数据源配置项 autoconfigure: exclude: - - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml index b0d89c036..320b18af5 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application-local.yaml @@ -21,8 +21,8 @@ spring: autoconfigure: exclude: - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 - - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml index 0d8366205..a57325fa5 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml @@ -107,7 +107,7 @@ spring: vectorstore: # 向量存储 redis: initialize-schema: true - index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行 + index-name: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行 prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构 qdrant: initialize-schema: true @@ -145,13 +145,14 @@ spring: api-key: xxxx moonshot: # 月之暗灭(KIMI) api-key: sk-abc + deepseek: # DeepSeek + api-key: sk-e94db327cc7d457d99a8de8810fc6b12 + chat: + options: + model: deepseek-chat yudao: ai: - deep-seek: # DeepSeek - enable: true - api-key: sk-e94db327cc7d457d99a8de8810fc6b12 - model: deepseek-chat doubao: # 字节豆包 enable: true api-key: 5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272 diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java index 5c924a582..69776d8e6 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; -import com.azure.core.util.ClientOptions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; @@ -17,7 +16,7 @@ import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; -import static org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME; +import static org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME; /** * {@link AzureOpenAiChatModel} 集成测试 @@ -29,10 +28,13 @@ public class AzureOpenAIChatModelTests { // TODO @芋艿:晚点在调整 private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder() .endpoint("https://eastusprejade.openai.azure.com") - .credential(new AzureKeyCredential("xxx")) - .clientOptions((new ClientOptions()).setApplicationId("spring-ai")); - private final AzureOpenAiChatModel chatModel = new AzureOpenAiChatModel(openAiApi, - AzureOpenAiChatOptions.builder().deploymentName(DEFAULT_DEPLOYMENT_NAME).build()); + .credential(new AzureKeyCredential("xxx")); + private final AzureOpenAiChatModel chatModel = AzureOpenAiChatModel.builder() + .openAIClientBuilder(openAiApi) + .defaultOptions(AzureOpenAiChatOptions.builder() + .deploymentName(DEFAULT_DEPLOYMENT_NAME) + .build()) + .build(); @Test @Disabled diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java index d1cc381fb..06b0b2565 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; @@ -35,7 +34,7 @@ public class BaiChuanChatModelTests { .build()) .build(); - private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel); + private final BaiChuanChatModel chatModel = new BaiChuanChatModel(openAiChatModel); @Test @Disabled diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java index d20a1761f..7b51df166 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; @@ -8,9 +7,9 @@ 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 org.springframework.ai.deepseek.DeepSeekChatModel; +import org.springframework.ai.deepseek.DeepSeekChatOptions; +import org.springframework.ai.deepseek.api.DeepSeekApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -23,19 +22,16 @@ import java.util.List; */ public class DeepSeekChatModelTests { - private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() - .baseUrl(DeepSeekChatModel.BASE_URL) - .apiKey("sk-e52047409b144d97b791a6a46a2d") // apiKey + private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder() + .deepSeekApi(DeepSeekApi.builder() + .apiKey("sk-eaf4172a057344dd9bc64b1f806b6axx") // apiKey .build()) - .defaultOptions(OpenAiChatOptions.builder() + .defaultOptions(DeepSeekChatOptions.builder() .model("deepseek-chat") // 模型 .temperature(0.7) .build()) .build(); - private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel); - @Test @Disabled public void testCall() { diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java index 153342d44..69e2c1daa 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java @@ -1,20 +1,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; -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.ollama.OllamaChatModel; -import org.springframework.ai.ollama.api.OllamaApi; -import org.springframework.ai.ollama.api.OllamaModel; -import org.springframework.ai.ollama.api.OllamaOptions; -import reactor.core.publisher.Flux; - -import java.util.ArrayList; -import java.util.List; /** * {@link OllamaChatModel} 集成测试 @@ -23,43 +9,43 @@ import java.util.List; */ public class LlamaChatModelTests { - private final OllamaChatModel chatModel = OllamaChatModel.builder() - .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址 - .defaultOptions(OllamaOptions.builder() - .model(OllamaModel.LLAMA3.getName()) // 模型 - .build()) - .build(); - - @Test - @Disabled - public void testCall() { - // 准备参数 - List messages = new ArrayList<>(); - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); - messages.add(new UserMessage("1 + 1 = ?")); - - // 调用 - ChatResponse response = chatModel.call(new Prompt(messages)); - // 打印结果 - System.out.println(response); - System.out.println(response.getResult().getOutput()); - } - - @Test - @Disabled - public void testStream() { - // 准备参数 - List messages = new ArrayList<>(); - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); - messages.add(new UserMessage("1 + 1 = ?")); - - // 调用 - Flux flux = chatModel.stream(new Prompt(messages)); - // 打印结果 - flux.doOnNext(response -> { -// System.out.println(response); - System.out.println(response.getResult().getOutput()); - }).then().block(); - } +// private final OllamaChatModel chatModel = OllamaChatModel.builder() +// .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址 +// .defaultOptions(OllamaOptions.builder() +// .model(OllamaModel.LLAMA3.getName()) // 模型 +// .build()) +// .build(); +// +// @Test +// @Disabled +// public void testCall() { +// // 准备参数 +// List messages = new ArrayList<>(); +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); +// messages.add(new UserMessage("1 + 1 = ?")); +// +// // 调用 +// ChatResponse response = chatModel.call(new Prompt(messages)); +// // 打印结果 +// System.out.println(response); +// System.out.println(response.getResult().getOutput()); +// } +// +// @Test +// @Disabled +// public void testStream() { +// // 准备参数 +// List messages = new ArrayList<>(); +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); +// messages.add(new UserMessage("1 + 1 = ?")); +// +// // 调用 +// Flux flux = chatModel.stream(new Prompt(messages)); +// // 打印结果 +// flux.doOnNext(response -> { +//// System.out.println(response); +// System.out.println(response.getResult().getOutput()); +// }).then().block(); +// } } diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java index 7de7fd709..992334b4d 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java @@ -2,14 +2,14 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springaicommunity.moonshot.MoonshotChatModel; +import org.springaicommunity.moonshot.MoonshotChatOptions; +import org.springaicommunity.moonshot.api.MoonshotApi; 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.moonshot.MoonshotChatModel; -import org.springframework.ai.moonshot.MoonshotChatOptions; -import org.springframework.ai.moonshot.api.MoonshotApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -22,11 +22,15 @@ import java.util.List; */ public class MoonshotChatModelTests { - private final MoonshotChatModel chatModel = new MoonshotChatModel( - new MoonshotApi("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA"), // 密钥 - MoonshotChatOptions.builder() - .model("moonshot-v1-8k") // 模型 - .build()); + private final MoonshotChatModel chatModel = MoonshotChatModel.builder() + .moonshotApi(MoonshotApi.builder() + .apiKey("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA") // 密钥 + .build()) + .defaultOptions(MoonshotChatOptions.builder() + .model("kimi-k2-0711-preview") // 模型 + .build()) + .build(); + @Test @Disabled public void testCall() { diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java index f86e67a66..d2bf68812 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java @@ -23,7 +23,9 @@ import java.util.List; public class OllamaChatModelTests { private final OllamaChatModel chatModel = OllamaChatModel.builder() - .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址 + .ollamaApi(OllamaApi.builder() + .baseUrl("http://127.0.0.1:11434") // Ollama 服务地址 + .build()) .defaultOptions(OllamaOptions.builder() // .model("qwen") // 模型(https://ollama.com/library/qwen) .model("deepseek-r1") // 模型(https://ollama.com/library/deepseek-r1) diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java index ff866fe40..c650fd042 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java @@ -25,10 +25,10 @@ public class OpenAIChatModelTests { private final OpenAiChatModel chatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl("https://api.holdai.top") - .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") // apiKey + .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") // apiKey .build()) .defaultOptions(OpenAiChatOptions.builder() - .model(OpenAiApi.ChatModel.GPT_4_O) // 模型 + .model(OpenAiApi.ChatModel.GPT_4_1_NANO) // 模型 .temperature(0.7) .build()) .build(); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java index 4f0efdb20..4f2e27edd 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java @@ -22,14 +22,17 @@ import java.util.List; */ public class TongYiChatModelTests { - private final DashScopeChatModel chatModel = new DashScopeChatModel( - new DashScopeApi("sk-7d903764249848cfa912733146da12d1"), - DashScopeChatOptions.builder() + private final DashScopeChatModel chatModel = DashScopeChatModel.builder() + .dashScopeApi(DashScopeApi.builder() + .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0") + .build()) + .defaultOptions( DashScopeChatOptions.builder() .withModel("qwen1.5-72b-chat") // 模型 // .withModel("deepseek-r1") // 模型(deepseek-r1) // .withModel("deepseek-v3") // 模型(deepseek-v3) // .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) - .build()); + .build()) + .build(); @Test @Disabled diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java index ab6f64243..cb7be2a29 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springaicommunity.qianfan.QianFanChatModel; +import org.springaicommunity.qianfan.QianFanChatOptions; +import org.springaicommunity.qianfan.api.QianFanApi; import org.springframework.ai.chat.messages.Message; 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.qianfan.QianFanChatModel; -import org.springframework.ai.qianfan.QianFanChatOptions; -import org.springframework.ai.qianfan.api.QianFanApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -23,9 +23,9 @@ import java.util.List; public class YiYanChatModelTests { private final QianFanChatModel chatModel = new QianFanChatModel( - new QianFanApi("qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e"), // 密钥 + new QianFanApi("DGnyzREuaY7av7c38bOM9Ji2", "9aR8myflEOPDrEeLhoXv0FdqANOAyIZW"), // 密钥 QianFanChatOptions.builder() - .model(QianFanApi.ChatModel.ERNIE_4_0_8K_Preview.getValue()) + .model("ERNIE-4.5-8K-Preview") .build() ); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java index 49015b9b9..1b124529d 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java @@ -18,7 +18,7 @@ public class OpenAiImageModelTests { private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder() .baseUrl("https://api.holdai.top") // apiKey - .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") + .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") .build()); @Test @@ -26,8 +26,8 @@ public class OpenAiImageModelTests { public void testCall() { // 准备参数 ImageOptions options = OpenAiImageOptions.builder() - .withModel(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜 - .withHeight(256).withWidth(256) + .model(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜 + .height(256).width(256) .build(); ImagePrompt prompt = new ImagePrompt("中国长城!", options); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java index 8f44ab9ad..156360f25 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.image; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springaicommunity.qianfan.QianFanImageModel; +import org.springaicommunity.qianfan.QianFanImageOptions; +import org.springaicommunity.qianfan.api.QianFanImageApi; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; -import org.springframework.ai.qianfan.QianFanImageModel; -import org.springframework.ai.qianfan.QianFanImageOptions; -import org.springframework.ai.qianfan.api.QianFanImageApi; import static cn.iocoder.yudao.module.ai.framework.ai.core.model.image.StabilityAiImageModelTests.viewImage; diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java index b58e6df00..8cf556d9f 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java @@ -31,8 +31,8 @@ public class StabilityAiImageModelTests { public void testCall() { // 准备参数 ImageOptions options = OpenAiImageOptions.builder() - .withModel("stable-diffusion-v1-6") - .withHeight(320).withWidth(320) + .model("stable-diffusion-v1-6") + .height(320).width(320) .build(); ImagePrompt prompt = new ImagePrompt("great wall", options); diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java index e7ed65c0b..e93138f8c 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.ppt.wdd; -import cn.iocoder.yudao.module.ai.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; diff --git a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java index 13e009fbd..267892bed 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java +++ b/yudao-module-ai/yudao-module-ai-server/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.ppt.xunfei; import cn.hutool.core.io.FileUtil; -import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.api.XunFeiPptApi; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.api.XunFeiPptApi; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockMultipartFile; diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index be7b08008..afbfce273 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -6,8 +6,8 @@ server: spring: autoconfigure: exclude: - - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 43f8f584b..a85360a02 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -10,8 +10,8 @@ spring: - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 - de.codecentric.boot.admin.server.cloud.config.AdminServerDiscoveryAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 - - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 037753872..4cd067caf 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -174,7 +174,7 @@ spring: vectorstore: # 向量存储 redis: initialize-schema: true - index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行 + index-name: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行 prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构 qdrant: initialize-schema: true @@ -212,13 +212,14 @@ spring: api-key: xxxx moonshot: # 月之暗灭(KIMI) api-key: sk-abc + deepseek: # DeepSeek + api-key: sk-e94db327cc7d457d99a8de8810fc6b12 + chat: + options: + model: deepseek-chat yudao: ai: - deep-seek: # DeepSeek - enable: true - api-key: sk-e94db327cc7d457d99a8de8810fc6b12 - model: deepseek-chat doubao: # 字节豆包 enable: true api-key: 5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272