package com.iailab.framework.ai.config; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import com.iailab.framework.ai.core.factory.AiModelFactory; import com.iailab.framework.ai.core.factory.AiModelFactoryImpl; import com.iailab.framework.ai.core.model.baichuan.BaiChuanChatModel; import com.iailab.framework.ai.core.model.deepseek.DeepSeekChatModel; import com.iailab.framework.ai.core.model.doubao.DouBaoChatModel; import com.iailab.framework.ai.core.model.hunyuan.HunYuanChatModel; import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi; import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowChatModel; import com.iailab.framework.ai.core.model.suno.api.SunoApi; import com.iailab.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; import org.springframework.ai.openai.OpenAiChatModel; 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.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; /** * Iailab AI 自动配置 * * @author fansili */ @AutoConfiguration @EnableConfigurationProperties({ IailabAiProperties.class, QdrantVectorStoreProperties.class, // 解析 Qdrant 配置 RedisVectorStoreProperties.class, // 解析 Redis 配置 MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置 }) @Slf4j public class IailabAiAutoConfiguration { @Bean public AiModelFactory aiModelFactory() { return new AiModelFactoryImpl(); } // ========== 各种 AI Client 创建 ========== @Bean @ConditionalOnProperty(value = "iailab.ai.deepseek.enable", havingValue = "true") public DeepSeekChatModel deepSeekChatModel(IailabAiProperties IailabAiProperties) { IailabAiProperties.DeepSeekProperties properties = IailabAiProperties.getDeepseek(); return buildDeepSeekChatModel(properties); } public DeepSeekChatModel buildDeepSeekChatModel(IailabAiProperties.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 = "iailab.ai.doubao.enable", havingValue = "true") public DouBaoChatModel douBaoChatClient(IailabAiProperties IailabAiProperties) { IailabAiProperties.DouBaoProperties properties = IailabAiProperties.getDoubao(); return buildDouBaoChatClient(properties); } public DouBaoChatModel buildDouBaoChatClient(IailabAiProperties.DouBaoProperties properties) { if (StrUtil.isEmpty(properties.getModel())) { properties.setModel(DouBaoChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl(DouBaoChatModel.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 DouBaoChatModel(openAiChatModel); } @Bean @ConditionalOnProperty(value = "iailab.ai.siliconflow.enable", havingValue = "true") public SiliconFlowChatModel siliconFlowChatClient(IailabAiProperties IailabAiProperties) { IailabAiProperties.SiliconFlowProperties properties = IailabAiProperties.getSiliconflow(); return buildSiliconFlowChatClient(properties); } public SiliconFlowChatModel buildSiliconFlowChatClient(IailabAiProperties.SiliconFlowProperties properties) { if (StrUtil.isEmpty(properties.getModel())) { properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl(SiliconFlowApiConstants.DEFAULT_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 SiliconFlowChatModel(openAiChatModel); } @Bean @ConditionalOnProperty(value = "iailab.ai.hunyuan.enable", havingValue = "true") public HunYuanChatModel hunYuanChatClient(IailabAiProperties IailabAiProperties) { IailabAiProperties.HunYuanProperties properties = IailabAiProperties.getHunyuan(); return buildHunYuanChatClient(properties); } public HunYuanChatModel buildHunYuanChatClient(IailabAiProperties.HunYuanProperties properties) { 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); } // 创建 OpenAiChatModel、HunYuanChatModel 对象 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl(properties.getBaseUrl()) .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 HunYuanChatModel(openAiChatModel); } @Bean @ConditionalOnProperty(value = "iailab.ai.xinghuo.enable", havingValue = "true") public XingHuoChatModel xingHuoChatClient(IailabAiProperties IailabAiProperties) { IailabAiProperties.XingHuoProperties properties = IailabAiProperties.getXinghuo(); return buildXingHuoChatClient(properties); } public XingHuoChatModel buildXingHuoChatClient(IailabAiProperties.XingHuoProperties properties) { if (StrUtil.isEmpty(properties.getModel())) { properties.setModel(XingHuoChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl(XingHuoChatModel.BASE_URL) .apiKey(properties.getAppKey() + ":" + properties.getSecretKey()) .build()) .defaultOptions(OpenAiChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) .toolCallingManager(getToolCallingManager()) .build(); return new XingHuoChatModel(openAiChatModel); } @Bean @ConditionalOnProperty(value = "iailab.ai.baichuan.enable", havingValue = "true") public BaiChuanChatModel baiChuanChatClient(IailabAiProperties IailabAiProperties) { IailabAiProperties.BaiChuanProperties properties = IailabAiProperties.getBaichuan(); return buildBaiChuanChatClient(properties); } public BaiChuanChatModel buildBaiChuanChatClient(IailabAiProperties.BaiChuanProperties properties) { if (StrUtil.isEmpty(properties.getModel())) { properties.setModel(BaiChuanChatModel.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl(BaiChuanChatModel.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 BaiChuanChatModel(openAiChatModel); } @Bean @ConditionalOnProperty(value = "iailab.ai.midjourney.enable", havingValue = "true") public MidjourneyApi midjourneyApi(IailabAiProperties IailabAiProperties) { IailabAiProperties.MidjourneyProperties config = IailabAiProperties.getMidjourney(); return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl()); } @Bean @ConditionalOnProperty(value = "iailab.ai.suno.enable", havingValue = "true") public SunoApi sunoApi(IailabAiProperties IailabAiProperties) { return new SunoApi(IailabAiProperties.getSuno().getBaseUrl()); } // ========== RAG 相关 ========== @Bean public TokenCountEstimator tokenCountEstimator() { return new JTokkitTokenCountEstimator(); } @Bean public BatchingStrategy batchingStrategy() { return new TokenCountBatchingStrategy(); } private static ToolCallingManager getToolCallingManager() { return SpringUtil.getBean(ToolCallingManager.class); } }