| | |
| | | package cn.iocoder.yudao.module.ai.framework.ai.core.model; |
| | | |
| | | import cn.hutool.core.io.FileUtil; |
| | | import cn.hutool.core.lang.Assert; |
| | | import cn.hutool.core.lang.Singleton; |
| | | import cn.hutool.core.lang.func.Func0; |
| | | 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.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel; |
| | | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi; |
| | | 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 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; |
| | | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; |
| | | import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel; |
| | | import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions; |
| | | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; |
| | | import com.anthropic.client.okhttp.AnthropicOkHttpClient; |
| | | import com.azure.ai.openai.OpenAIClientBuilder; |
| | | import com.azure.core.credential.KeyCredential; |
| | | import com.openai.client.OpenAIClient; |
| | | import com.openai.client.okhttp.OpenAIOkHttpClient; |
| | | 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.springaicommunity.moonshot.MoonshotChatModel; |
| | | import org.springaicommunity.moonshot.MoonshotChatOptions; |
| | | import org.springaicommunity.moonshot.api.MoonshotApi; |
| | | 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.springframework.ai.azure.openai.AzureOpenAiChatModel; |
| | | import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.ai.chat.model.ChatModel; |
| | | import org.springframework.ai.deepseek.DeepSeekChatModel; |
| | | import org.springframework.ai.deepseek.DeepSeekChatOptions; |
| | | import org.springframework.ai.deepseek.api.DeepSeekApi; |
| | | import org.springframework.ai.document.MetadataMode; |
| | | import org.springframework.ai.embedding.BatchingStrategy; |
| | | import org.springframework.ai.embedding.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.anthropic.autoconfigure.AnthropicChatAutoConfiguration; |
| | | 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.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; |
| | | import org.springframework.ai.ollama.api.OllamaEmbeddingOptions; |
| | | import org.springframework.ai.embedding.TokenCountBatchingStrategy; |
| | | import org.springframework.ai.openai.OpenAiChatModel; |
| | | import org.springframework.ai.openai.OpenAiEmbeddingModel; |
| | | import org.springframework.ai.openai.OpenAiEmbeddingOptions; |
| | | import org.springframework.ai.openai.OpenAiImageModel; |
| | | import org.springframework.ai.anthropic.AnthropicChatModel; |
| | | 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; |
| | | import org.springframework.beans.BeansException; |
| | | import org.springframework.beans.factory.ObjectProvider; |
| | | import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties; |
| | | import org.springframework.web.client.RestClient; |
| | | import redis.clients.jedis.JedisPooled; |
| | | |
| | | import java.io.File; |
| | | import java.time.Duration; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Timer; |
| | | import java.util.TimerTask; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | | |
| | | /** |
| | | * AI Model 模型工厂的实现类 |
| | | * AI Model 模型工厂实现类 |
| | | * |
| | | * @author 芋道源码 |
| | | * 使用 OpenAI 兼容接口对接通义千问 + Milvus 向量存储 |
| | | */ |
| | | @Slf4j |
| | | public class AiModelFactoryImpl implements AiModelFactory { |
| | | |
| | | @Override |
| | | public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) { |
| | | String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url); |
| | | public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url, String model) { |
| | | String cacheKey = buildCacheKey(ChatModel.class, platform, apiKey, url, model); |
| | | return Singleton.get(cacheKey, (Func0<ChatModel>) () -> { |
| | | // noinspection EnhancedSwitchMigration |
| | | switch (platform) { |
| | | case TONG_YI: |
| | | return buildTongYiChatModel(apiKey); |
| | | case YI_YAN: |
| | | return buildYiYanChatModel(apiKey); |
| | | case DEEP_SEEK: |
| | | return buildDeepSeekChatModel(apiKey); |
| | | case DOU_BAO: |
| | | return buildDouBaoChatModel(apiKey); |
| | | case HUN_YUAN: |
| | | return buildHunYuanChatModel(apiKey, url); |
| | | case SILICON_FLOW: |
| | | return buildSiliconFlowChatModel(apiKey); |
| | | case ZHI_PU: |
| | | return buildZhiPuChatModel(apiKey, url); |
| | | case MINI_MAX: |
| | | return buildMiniMaxChatModel(apiKey, url); |
| | | case MOONSHOT: |
| | | return buildMoonshotChatModel(apiKey, url); |
| | | case XING_HUO: |
| | | return buildXingHuoChatModel(apiKey); |
| | | case BAI_CHUAN: |
| | | return buildBaiChuanChatModel(apiKey); |
| | | case OPENAI: |
| | | return buildOpenAiChatModel(apiKey, url); |
| | | case AZURE_OPENAI: |
| | | return buildAzureOpenAiChatModel(apiKey, url); |
| | | case ANTHROPIC: |
| | | return buildAnthropicChatModel(apiKey, url); |
| | | case GEMINI: |
| | | return buildGeminiChatModel(apiKey); |
| | | case OLLAMA: |
| | | return buildOllamaChatModel(url); |
| | | case GROK: |
| | | return buildGrokChatModel(apiKey,url); |
| | | default: |
| | | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| | | if (platform == AiPlatformEnum.TONG_YI) { |
| | | return AiAutoConfiguration.buildTongYiChatModel(apiKey, model); |
| | | } |
| | | throw new IllegalArgumentException(StrUtil.format("不支持的平台({})", platform)); |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public ChatModel getDefaultChatModel(AiPlatformEnum platform) { |
| | | // noinspection EnhancedSwitchMigration |
| | | switch (platform) { |
| | | case TONG_YI: |
| | | return SpringUtil.getBean(DashScopeChatModel.class); |
| | | case YI_YAN: |
| | | return SpringUtil.getBean(QianFanChatModel.class); |
| | | case DEEP_SEEK: |
| | | return SpringUtil.getBean(DeepSeekChatModel.class); |
| | | case DOU_BAO: |
| | | return SpringUtil.getBean(DouBaoChatModel.class); |
| | | case HUN_YUAN: |
| | | return SpringUtil.getBean(HunYuanChatModel.class); |
| | | case SILICON_FLOW: |
| | | return SpringUtil.getBean(SiliconFlowChatModel.class); |
| | | case ZHI_PU: |
| | | return SpringUtil.getBean(ZhiPuAiChatModel.class); |
| | | case MINI_MAX: |
| | | return SpringUtil.getBean(MiniMaxChatModel.class); |
| | | case MOONSHOT: |
| | | return SpringUtil.getBean(MoonshotChatModel.class); |
| | | case XING_HUO: |
| | | return SpringUtil.getBean(XingHuoChatModel.class); |
| | | case BAI_CHUAN: |
| | | return SpringUtil.getBean(BaiChuanChatModel.class); |
| | | case OPENAI: |
| | | return SpringUtil.getBean(OpenAiChatModel.class); |
| | | case AZURE_OPENAI: |
| | | return SpringUtil.getBean(AzureOpenAiChatModel.class); |
| | | case ANTHROPIC: |
| | | return SpringUtil.getBean(AnthropicChatModel.class); |
| | | case GEMINI: |
| | | return SpringUtil.getBean(GeminiChatModel.class); |
| | | case OLLAMA: |
| | | return SpringUtil.getBean(OllamaChatModel.class); |
| | | default: |
| | | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| | | if (platform == AiPlatformEnum.TONG_YI) { |
| | | return SpringUtil.getBean(OpenAiChatModel.class); |
| | | } |
| | | throw new IllegalArgumentException(StrUtil.format("不支持的平台({})", platform)); |
| | | } |
| | | |
| | | @Override |
| | | public ImageModel getDefaultImageModel(AiPlatformEnum platform) { |
| | | // noinspection EnhancedSwitchMigration |
| | | switch (platform) { |
| | | case TONG_YI: |
| | | return SpringUtil.getBean(DashScopeImageModel.class); |
| | | case YI_YAN: |
| | | return SpringUtil.getBean(QianFanImageModel.class); |
| | | case ZHI_PU: |
| | | return SpringUtil.getBean(ZhiPuAiImageModel.class); |
| | | case SILICON_FLOW: |
| | | return SpringUtil.getBean(SiliconFlowImageModel.class); |
| | | case OPENAI: |
| | | return SpringUtil.getBean(OpenAiImageModel.class); |
| | | case STABLE_DIFFUSION: |
| | | return SpringUtil.getBean(StabilityAiImageModel.class); |
| | | default: |
| | | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) { |
| | | // noinspection EnhancedSwitchMigration |
| | | switch (platform) { |
| | | case TONG_YI: |
| | | return buildTongYiImagesModel(apiKey); |
| | | case YI_YAN: |
| | | return buildQianFanImageModel(apiKey); |
| | | case ZHI_PU: |
| | | return buildZhiPuAiImageModel(apiKey, url); |
| | | case OPENAI: |
| | | return buildOpenAiImageModel(apiKey, url); |
| | | case SILICON_FLOW: |
| | | return buildSiliconFlowImageModel(apiKey,url); |
| | | case STABLE_DIFFUSION: |
| | | return buildStabilityAiImageModel(apiKey, url); |
| | | default: |
| | | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url) { |
| | | String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey, |
| | | url); |
| | | return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> { |
| | | YudaoAiProperties.Midjourney properties = SpringUtil.getBean(YudaoAiProperties.class) |
| | | .getMidjourney(); |
| | | return new MidjourneyApi(url, apiKey, properties.getNotifyUrl()); |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public SunoApi getOrCreateSunoApi(String apiKey, String url) { |
| | | String cacheKey = buildClientCacheKey(SunoApi.class, AiPlatformEnum.SUNO.getPlatform(), apiKey, url); |
| | | return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url)); |
| | | } |
| | | |
| | | @Override |
| | | @SuppressWarnings("EnhancedSwitchMigration") |
| | | public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) { |
| | | String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model); |
| | | String cacheKey = buildCacheKey(EmbeddingModel.class, platform, apiKey, url, model); |
| | | return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> { |
| | | switch (platform) { |
| | | case TONG_YI: |
| | | return buildTongYiEmbeddingModel(apiKey, model); |
| | | case YI_YAN: |
| | | return buildYiYanEmbeddingModel(apiKey, model); |
| | | case ZHI_PU: |
| | | return buildZhiPuEmbeddingModel(apiKey, url, model); |
| | | case MINI_MAX: |
| | | return buildMiniMaxEmbeddingModel(apiKey, url, model); |
| | | case OPENAI: |
| | | return buildOpenAiEmbeddingModel(apiKey, url, model); |
| | | case AZURE_OPENAI: |
| | | return buildAzureOpenAiEmbeddingModel(apiKey, url, model); |
| | | case OLLAMA: |
| | | return buildOllamaEmbeddingModel(url, model); |
| | | default: |
| | | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| | | if (platform == AiPlatformEnum.TONG_YI) { |
| | | return AiAutoConfiguration.buildTongYiEmbeddingModel(apiKey, model); |
| | | } |
| | | throw new IllegalArgumentException(StrUtil.format("不支持的平台({})", platform)); |
| | | }); |
| | | } |
| | | |
| | |
| | | public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type, |
| | | EmbeddingModel embeddingModel, |
| | | Map<String, Class<?>> metadataFields) { |
| | | String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); |
| | | // metadataFields 参与缓存 key,确保不同知识库使用不同配置时不会复用 |
| | | String cacheKey = buildCacheKey(VectorStore.class, embeddingModel, type, metadataFields.hashCode()); |
| | | return Singleton.get(cacheKey, (Func0<VectorStore>) () -> { |
| | | if (type == SimpleVectorStore.class) { |
| | | return buildSimpleVectorStore(embeddingModel); |
| | | } |
| | | if (type == QdrantVectorStore.class) { |
| | | return buildQdrantVectorStore(embeddingModel); |
| | | } |
| | | if (type == RedisVectorStore.class) { |
| | | return buildRedisVectorStore(embeddingModel, metadataFields); |
| | | } |
| | | if (type == MilvusVectorStore.class) { |
| | | return buildMilvusVectorStore(embeddingModel); |
| | | } |
| | | throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); |
| | | throw new IllegalArgumentException(StrUtil.format("不支持的向量存储类型({})", type)); |
| | | }); |
| | | } |
| | | |
| | | private static String buildClientCacheKey(Class<?> clazz, Object... params) { |
| | | if (ArrayUtil.isEmpty(params)) { |
| | | return clazz.getName(); |
| | | } |
| | | return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_")); |
| | | } |
| | | |
| | | // ========== 各种创建 spring-ai 客户端的方法 ========== |
| | | |
| | | /** |
| | | * 可参考 {@link DashScopeChatAutoConfiguration} 的 dashscopeChatModel 方法 |
| | | */ |
| | | private static DashScopeChatModel buildTongYiChatModel(String key) { |
| | | DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(key).build(); |
| | | DashScopeChatOptions options = DashScopeChatOptions |
| | | .builder() |
| | | .model(DashScopeApi.DEFAULT_CHAT_MODEL) |
| | | .temperature(0.7) |
| | | .build(); |
| | | return DashScopeChatModel |
| | | .builder() |
| | | .dashScopeApi(dashScopeApi) |
| | | .defaultOptions(options) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法 |
| | | */ |
| | | private static DashScopeImageModel buildTongYiImagesModel(String key) { |
| | | DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(key).build(); |
| | | return DashScopeImageModel.builder() |
| | | .dashScopeApi(dashScopeImageApi) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 QianFanChatAutoConfiguration 的 qianFanChatModel 方法 |
| | | */ |
| | | private static QianFanChatModel buildYiYanChatModel(String key) { |
| | | // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 |
| | | List<String> keys = StrUtil.split(key, '|'); |
| | | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| | | String appKey = keys.get(0); |
| | | String secretKey = keys.get(1); |
| | | QianFanApi qianFanApi = new QianFanApi(appKey, secretKey); |
| | | return new QianFanChatModel(qianFanApi); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 QianFanEmbeddingAutoConfiguration 的 qianFanImageModel 方法 |
| | | */ |
| | | private QianFanImageModel buildQianFanImageModel(String key) { |
| | | // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 |
| | | List<String> keys = StrUtil.split(key, '|'); |
| | | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| | | String appKey = keys.get(0); |
| | | String secretKey = keys.get(1); |
| | | QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey); |
| | | return new QianFanImageModel(qianFanApi); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link DeepSeekChatAutoConfiguration} 的 deepSeekChatModel 方法 |
| | | */ |
| | | private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) { |
| | | 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(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AiAutoConfiguration#douBaoChatClient(YudaoAiProperties)} |
| | | */ |
| | | private ChatModel buildDouBaoChatModel(String apiKey) { |
| | | YudaoAiProperties.DouBao properties = new YudaoAiProperties.DouBao() |
| | | .setApiKey(apiKey); |
| | | return new AiAutoConfiguration().buildDouBaoChatClient(properties); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(YudaoAiProperties)} |
| | | */ |
| | | private ChatModel buildHunYuanChatModel(String apiKey, String url) { |
| | | YudaoAiProperties.HunYuan properties = new YudaoAiProperties.HunYuan() |
| | | .setBaseUrl(url).setApiKey(apiKey); |
| | | return new AiAutoConfiguration().buildHunYuanChatClient(properties); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)} |
| | | */ |
| | | private ChatModel buildSiliconFlowChatModel(String apiKey) { |
| | | YudaoAiProperties.SiliconFlow properties = new YudaoAiProperties.SiliconFlow() |
| | | .setApiKey(apiKey); |
| | | return new AiAutoConfiguration().buildSiliconFlowChatClient(properties); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法 |
| | | */ |
| | | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { |
| | | ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey); |
| | | if (StrUtil.isNotEmpty(url)) { |
| | | zhiPuAiApiBuilder.baseUrl(url); |
| | | } |
| | | ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); |
| | | return new ZhiPuAiChatModel(zhiPuAiApiBuilder.build(), options, getToolCallingManager(), new org.springframework.core.retry.RetryTemplate(), |
| | | getObservationRegistry().getIfAvailable()); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link ZhiPuAiImageAutoConfiguration} 的 zhiPuAiImageModel 方法 |
| | | */ |
| | | private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { |
| | | ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey) |
| | | : new ZhiPuAiImageApi(url, apiKey, RestClient.builder()); |
| | | return new ZhiPuAiImageModel(zhiPuAiApi); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@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, getToolCallingManager(), new org.springframework.core.retry.RetryTemplate()); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 MoonshotChatAutoConfiguration 的 moonshotChatModel 方法 |
| | | */ |
| | | private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { |
| | | 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 MoonshotChatModel.builder() |
| | | .moonshotApi(moonshotApiBuilder.build()) |
| | | .defaultOptions(options) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)} |
| | | */ |
| | | private static XingHuoChatModel buildXingHuoChatModel(String key) { |
| | | List<String> keys = StrUtil.split(key, '|'); |
| | | Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| | | YudaoAiProperties.XingHuo properties = new YudaoAiProperties.XingHuo() |
| | | .setAppKey(keys.get(0)).setSecretKey(keys.get(1)); |
| | | return new AiAutoConfiguration().buildXingHuoChatClient(properties); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)} |
| | | */ |
| | | private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) { |
| | | YudaoAiProperties.BaiChuan properties = new YudaoAiProperties.BaiChuan() |
| | | .setApiKey(apiKey); |
| | | return new AiAutoConfiguration().buildBaiChuanChatClient(properties); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link OpenAiChatAutoConfiguration} 的 openAiChatModel 方法 |
| | | */ |
| | | private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { |
| | | return OpenAiChatModel.builder() |
| | | .openAiClient(buildOpenAiClient(openAiToken, url)) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AzureOpenAiChatAutoConfiguration} |
| | | */ |
| | | private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) { |
| | | // TODO @芋艿:使用前,请测试,暂时没密钥!!! |
| | | OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() |
| | | .endpoint(url).credential(new KeyCredential(apiKey)); |
| | | return AzureOpenAiChatModel.builder() |
| | | .openAIClientBuilder(openAIClientBuilder) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AnthropicChatAutoConfiguration} 的 anthropicApi 方法 |
| | | */ |
| | | private static AnthropicChatModel buildAnthropicChatModel(String apiKey, String url) { |
| | | AnthropicOkHttpClient.Builder builder = AnthropicOkHttpClient.builder().apiKey(apiKey); |
| | | if (StrUtil.isNotEmpty(url)) { |
| | | builder.baseUrl(url); |
| | | } |
| | | return AnthropicChatModel.builder() |
| | | .anthropicClient(builder.build()) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AiAutoConfiguration#buildGeminiChatClient(YudaoAiProperties.Gemini)} |
| | | */ |
| | | private static GeminiChatModel buildGeminiChatModel(String apiKey) { |
| | | YudaoAiProperties.Gemini properties = SpringUtil.getBean(YudaoAiProperties.class) |
| | | .getGemini().setApiKey(apiKey); |
| | | return new AiAutoConfiguration().buildGeminiChatClient(properties); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法 |
| | | */ |
| | | private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { |
| | | return new OpenAiImageModel(buildOpenAiClient(openAiToken, url)); |
| | | } |
| | | |
| | | /** |
| | | * 创建 SiliconFlowImageModel 对象 |
| | | */ |
| | | private SiliconFlowImageModel buildSiliconFlowImageModel(String apiToken, String url) { |
| | | url = StrUtil.blankToDefault(url, SiliconFlowApiConstants.DEFAULT_BASE_URL); |
| | | SiliconFlowImageApi openAiApi = new SiliconFlowImageApi(url, apiToken); |
| | | return new SiliconFlowImageModel(openAiApi); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link OllamaChatAutoConfiguration} 的 ollamaChatModel 方法 |
| | | */ |
| | | private static OllamaChatModel buildOllamaChatModel(String url) { |
| | | OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); |
| | | return OllamaChatModel.builder() |
| | | .ollamaApi(ollamaApi) |
| | | .toolCallingManager(getToolCallingManager()) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link StabilityAiImageAutoConfiguration} 的 stabilityAiImageModel 方法 |
| | | */ |
| | | private StabilityAiImageModel buildStabilityAiImageModel(String apiKey, String url) { |
| | | url = StrUtil.blankToDefault(url, StabilityAiApi.DEFAULT_BASE_URL); |
| | | StabilityAiApi stabilityAiApi = new StabilityAiApi(apiKey, StabilityAiApi.DEFAULT_IMAGE_MODEL, url); |
| | | return new StabilityAiImageModel(stabilityAiApi); |
| | | } |
| | | |
| | | private ChatModel buildGrokChatModel(String apiKey,String url) { |
| | | YudaoAiProperties.Grok properties = new YudaoAiProperties.Grok() |
| | | .setBaseUrl(url) |
| | | .setApiKey(apiKey); |
| | | return new AiAutoConfiguration().buildGrokChatClient(properties); |
| | | } |
| | | |
| | | // ========== 各种创建 EmbeddingModel 的方法 ========== |
| | | |
| | | /** |
| | | * 可参考 {@link DashScopeEmbeddingAutoConfiguration} 的 DashScopeEmbeddingModel 方法 |
| | | */ |
| | | private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { |
| | | DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); |
| | | DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().model(model).build(); |
| | | return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 ZhiPuAiEmbeddingModel 方法 |
| | | */ |
| | | private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { |
| | | ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey); |
| | | if (StrUtil.isNotEmpty(url)) { |
| | | zhiPuAiApiBuilder.baseUrl(url); |
| | | } |
| | | ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build(); |
| | | return new ZhiPuAiEmbeddingModel(zhiPuAiApiBuilder.build(), MetadataMode.EMBED, zhiPuAiEmbeddingOptions); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link MiniMaxEmbeddingAutoConfiguration} 的 miniMaxEmbeddingModel 方法 |
| | | */ |
| | | private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) { |
| | | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey) |
| | | : new MiniMaxApi(url, apiKey); |
| | | MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build(); |
| | | return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link QianFanEmbeddingModel} 的 qianFanEmbeddingModel 方法 |
| | | */ |
| | | private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) { |
| | | List<String> keys = StrUtil.split(key, '|'); |
| | | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| | | String appKey = keys.get(0); |
| | | String secretKey = keys.get(1); |
| | | QianFanApi qianFanApi = new QianFanApi(appKey, secretKey); |
| | | QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build(); |
| | | return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions); |
| | | } |
| | | |
| | | private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { |
| | | OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); |
| | | OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder().model(model).build(); |
| | | return OllamaEmbeddingModel.builder() |
| | | .ollamaApi(ollamaApi) |
| | | .defaultOptions(ollamaOptions) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link OpenAiEmbeddingAutoConfiguration} 的 openAiEmbeddingModel 方法 |
| | | */ |
| | | private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) { |
| | | OpenAiEmbeddingOptions openAiEmbeddingProperties = OpenAiEmbeddingOptions.builder().model(model).build(); |
| | | return new OpenAiEmbeddingModel(buildOpenAiClient(openAiToken, url), MetadataMode.EMBED, openAiEmbeddingProperties); |
| | | } |
| | | |
| | | private static OpenAIClient buildOpenAiClient(String apiKey, String url) { |
| | | OpenAIOkHttpClient.Builder builder = OpenAIOkHttpClient.builder().apiKey(apiKey); |
| | | if (StrUtil.isNotEmpty(url)) { |
| | | builder.baseUrl(url); |
| | | } |
| | | return builder.build(); |
| | | } |
| | | |
| | | /** |
| | | * 可参考 {@link AzureOpenAiEmbeddingAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 |
| | | */ |
| | | private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) { |
| | | // 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(openAIClientBuilder, embeddingProperties, |
| | | getObservationRegistry(), getEmbeddingModelObservationConvention()); |
| | | } |
| | | |
| | | // ========== 各种创建 VectorStore 的方法 ========== |
| | | |
| | | /** |
| | | * 注意:仅适合本地测试使用,生产建议还是使用 Qdrant、Milvus 等 |
| | | */ |
| | | @SneakyThrows |
| | | @SuppressWarnings("ResultOfMethodCallIgnored") |
| | | private SimpleVectorStore buildSimpleVectorStore(EmbeddingModel embeddingModel) { |
| | | SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build(); |
| | | // 启动加载 |
| | | File file = new File(StrUtil.format("{}/vector_store/simple_{}.json", |
| | | FileUtil.getUserHomePath(), embeddingModel.getClass().getSimpleName())); |
| | | if (!file.exists()) { |
| | | FileUtil.mkParentDirs(file); |
| | | file.createNewFile(); |
| | | } else if (file.length() > 0) { |
| | | vectorStore.load(file); |
| | | } |
| | | // 定时持久化,每分钟一次 |
| | | Timer timer = new Timer("SimpleVectorStoreTimer-" + file.getAbsolutePath()); |
| | | timer.scheduleAtFixedRate(new TimerTask() { |
| | | |
| | | @Override |
| | | public void run() { |
| | | vectorStore.save(file); |
| | | } |
| | | |
| | | }, Duration.ofMinutes(1).toMillis(), Duration.ofMinutes(1).toMillis()); |
| | | // 关闭时,进行持久化 |
| | | RuntimeUtil.addShutdownHook(() -> vectorStore.save(file)); |
| | | return vectorStore; |
| | | } |
| | | |
| | | /** |
| | | * 参考 {@link QdrantVectorStoreAutoConfiguration} 的 vectorStore 方法 |
| | | */ |
| | | @SneakyThrows |
| | | private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) { |
| | | QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration(); |
| | | QdrantVectorStoreProperties properties = SpringUtil.getBean(QdrantVectorStoreProperties.class); |
| | | // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象 |
| | | QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder( |
| | | properties.getHost(), properties.getPort(), properties.isUseTls()); |
| | | if (StrUtil.isNotEmpty(properties.getApiKey())) { |
| | | grpcClientBuilder.withApiKey(properties.getApiKey()); |
| | | } |
| | | QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build()); |
| | | // 创建 QdrantVectorStore 对象 |
| | | QdrantVectorStore vectorStore = configuration.vectorStore(embeddingModel, properties, qdrantClient, |
| | | getObservationRegistry(), getCustomObservationConvention(), getBatchingStrategy()); |
| | | // 初始化索引 |
| | | vectorStore.afterPropertiesSet(); |
| | | return vectorStore; |
| | | } |
| | | |
| | | /** |
| | | * 参考 {@link RedisVectorStoreAutoConfiguration} 的 vectorStore 方法 |
| | | */ |
| | | private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel, |
| | | Map<String, Class<?>> metadataFields) { |
| | | // 创建 JedisPooled 对象 |
| | | DataRedisProperties redisProperties = SpringUtils.getBean(DataRedisProperties.class); |
| | | JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort(), |
| | | redisProperties.getUsername(), redisProperties.getPassword()); |
| | | // 创建 RedisVectorStoreProperties 对象 |
| | | RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class); |
| | | RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel) |
| | | .indexName(properties.getIndexName()).prefix(properties.getPrefix()) |
| | | .initializeSchema(properties.isInitializeSchema()) |
| | | .metadataFields(convertList(metadataFields.entrySet(), entry -> { |
| | | String fieldName = entry.getKey(); |
| | | Class<?> fieldType = entry.getValue(); |
| | | if (Number.class.isAssignableFrom(fieldType)) { |
| | | return RedisVectorStore.MetadataField.numeric(fieldName); |
| | | } |
| | | if (Boolean.class.isAssignableFrom(fieldType)) { |
| | | return RedisVectorStore.MetadataField.tag(fieldName); |
| | | } |
| | | return RedisVectorStore.MetadataField.text(fieldName); |
| | | })) |
| | | .observationRegistry(getObservationRegistry().getObject()) |
| | | .customObservationConvention(getCustomObservationConvention().getObject()) |
| | | .batchingStrategy(getBatchingStrategy()) |
| | | .build(); |
| | | // 初始化索引 |
| | | redisVectorStore.afterPropertiesSet(); |
| | | return redisVectorStore; |
| | | } |
| | | |
| | | /** |
| | | * 参考 {@link MilvusVectorStoreAutoConfiguration} 的 vectorStore 方法 |
| | | */ |
| | | @SneakyThrows |
| | | private MilvusVectorStore buildMilvusVectorStore(EmbeddingModel embeddingModel) { |
| | | MilvusVectorStoreAutoConfiguration configuration = new MilvusVectorStoreAutoConfiguration(); |
| | | // 获取配置属性 |
| | | MilvusVectorStoreProperties serverProperties = SpringUtil.getBean(MilvusVectorStoreProperties.class); |
| | | MilvusServiceClientProperties clientProperties = SpringUtil.getBean(MilvusServiceClientProperties.class); |
| | | |
| | | // 创建 MilvusServiceClient 对象 |
| | | MilvusServiceClient milvusClient = configuration.milvusClient(serverProperties, clientProperties, |
| | | new MilvusServiceClientConnectionDetails() { |
| | | var connectParam = io.milvus.param.ConnectParam.newBuilder() |
| | | .withHost(clientProperties.getHost()) |
| | | .withPort(clientProperties.getPort()) |
| | | .withDatabaseName(serverProperties.getDatabaseName()) |
| | | .build(); |
| | | var milvusClient = new io.milvus.client.MilvusServiceClient(connectParam); |
| | | |
| | | @Override |
| | | public String getHost() { |
| | | return clientProperties.getHost(); |
| | | } |
| | | |
| | | @Override |
| | | public int getPort() { |
| | | return clientProperties.getPort(); |
| | | } |
| | | |
| | | } |
| | | ); |
| | | // 创建 MilvusVectorStore 对象 |
| | | MilvusVectorStore vectorStore = configuration.vectorStore(milvusClient, embeddingModel, serverProperties, |
| | | getBatchingStrategy(), getObservationRegistry(), getCustomObservationConvention()); |
| | | |
| | | // 初始化索引 |
| | | vectorStore.afterPropertiesSet(); |
| | | MilvusVectorStore vectorStore = MilvusVectorStore.builder(milvusClient, embeddingModel) |
| | | .databaseName(serverProperties.getDatabaseName()) |
| | | .collectionName(serverProperties.getCollectionName()) |
| | | .initializeSchema(serverProperties.isInitializeSchema()) |
| | | .batchingStrategy(new TokenCountBatchingStrategy()) |
| | | .build(); |
| | | try { |
| | | vectorStore.afterPropertiesSet(); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("Milvus 向量存储初始化失败: " + e.getMessage(), e); |
| | | } |
| | | return vectorStore; |
| | | } |
| | | |
| | | private static ObjectProvider<ObservationRegistry> getObservationRegistry() { |
| | | return new ObjectProvider<>() { |
| | | |
| | | @Override |
| | | public ObservationRegistry getObject() throws BeansException { |
| | | return SpringUtil.getBean(ObservationRegistry.class); |
| | | } |
| | | |
| | | }; |
| | | } |
| | | |
| | | private static ObjectProvider<VectorStoreObservationConvention> getCustomObservationConvention() { |
| | | return new ObjectProvider<>() { |
| | | |
| | | @Override |
| | | public VectorStoreObservationConvention getObject() throws BeansException { |
| | | return new DefaultVectorStoreObservationConvention(); |
| | | } |
| | | |
| | | }; |
| | | } |
| | | |
| | | private static BatchingStrategy getBatchingStrategy() { |
| | | return SpringUtil.getBean(BatchingStrategy.class); |
| | | } |
| | | |
| | | private static ToolCallingManager getToolCallingManager() { |
| | | return SpringUtil.getBean(ToolCallingManager.class); |
| | | } |
| | | |
| | | private static ObjectProvider<EmbeddingModelObservationConvention> getEmbeddingModelObservationConvention() { |
| | | return new ObjectProvider<>() { |
| | | |
| | | @Override |
| | | public EmbeddingModelObservationConvention getObject() throws BeansException { |
| | | return SpringUtil.getBean(EmbeddingModelObservationConvention.class); |
| | | } |
| | | |
| | | }; |
| | | private static String buildCacheKey(Class<?> clazz, Object... params) { |
| | | if (ArrayUtil.isEmpty(params)) return clazz.getName(); |
| | | return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_")); |
| | | } |
| | | |
| | | } |