mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-01 02:38:43 +08:00 
			
		
		
		
	Merge branch 'master-jdk21-ai' of https://gitee.com/cherishsince/ruoyi-vue-pro into master-jdk17
This commit is contained in:
		| @@ -14,58 +14,57 @@ | ||||
|     <name>${project.artifactId}</name> | ||||
|     <description>AI 大模型拓展,接入国内外大模型</description> | ||||
|     <properties> | ||||
|         <spring-ai.version>1.0.0-M1</spring-ai.version> | ||||
|         <spring-ai.groupId>group.springframework.ai</spring-ai.groupId> | ||||
|         <spring-ai.version>1.1.0</spring-ai.version> | ||||
|     </properties> | ||||
|  | ||||
|     <dependencies> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-openai-spring-boot-starter</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 向量化,基于 Redis 存储,Tika 解析内容 --> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-transformers-spring-boot-starter</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-tika-document-reader</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-redis-store</artifactId> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- TODO @xin:引入我们项目的 starter --> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.data</groupId> | ||||
|             <artifactId>spring-data-redis</artifactId> | ||||
|             <optional>true</optional> | ||||
|             <groupId>cn.iocoder.boot</groupId> | ||||
|             <artifactId>yudao-spring-boot-starter-redis</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
| @@ -73,11 +72,10 @@ | ||||
|             <artifactId>yudao-common</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- TODO 芋艿:等 spring-ai 官方发布后,需要把 groupId 改下 --> | ||||
|         <dependency> | ||||
|             <groupId>group.springframework.ai</groupId> | ||||
|             <groupId>${spring-ai.groupId}</groupId> | ||||
|             <artifactId>spring-ai-qianfan-spring-boot-starter</artifactId> | ||||
|             <version>1.1.0</version> | ||||
|             <version>${spring-ai.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 阿里云 通义千问 --> | ||||
|   | ||||
| @@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.ai.config; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; | ||||
| import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; | ||||
| import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory; | ||||
| import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactoryImpl; | ||||
| import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; | ||||
| import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; | ||||
| import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||||
| @@ -10,20 +12,15 @@ import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; | ||||
| import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions; | ||||
| import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; | ||||
| import org.springframework.ai.document.MetadataMode; | ||||
| import org.springframework.ai.embedding.EmbeddingModel; | ||||
| import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; | ||||
| import org.springframework.ai.tokenizer.TokenCountEstimator; | ||||
| import org.springframework.ai.transformer.splitter.TokenTextSplitter; | ||||
| import org.springframework.ai.transformers.TransformersEmbeddingModel; | ||||
| import org.springframework.ai.vectorstore.RedisVectorStore; | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||
| import org.springframework.boot.autoconfigure.data.redis.RedisProperties; | ||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Import; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import redis.clients.jedis.JedisPooled; | ||||
|  | ||||
| /** | ||||
|  * 芋道 AI 自动配置 | ||||
| @@ -41,6 +38,12 @@ public class YudaoAiAutoConfiguration { | ||||
|         return new AiModelFactoryImpl(); | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public AiVectorStoreFactory aiVectorFactory() { | ||||
|         return new AiVectorStoreFactoryImpl(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // ========== 各种 AI Client 创建 ========== | ||||
|  | ||||
|     @Bean | ||||
| @@ -83,30 +86,31 @@ public class YudaoAiAutoConfiguration { | ||||
|     } | ||||
|  | ||||
|     // ========== rag 相关 ========== | ||||
|     @Bean | ||||
|     @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||||
|     public EmbeddingModel transformersEmbeddingClient() { | ||||
|         return new TransformersEmbeddingModel(MetadataMode.EMBED); | ||||
|     } | ||||
|     // TODO @xin 免费版本 | ||||
| //    @Bean | ||||
| //    @Lazy // TODO 芋艿:临时注释,避免无法启动」 | ||||
| //    public EmbeddingModel transformersEmbeddingClient() { | ||||
| //        return new TransformersEmbeddingModel(MetadataMode.EMBED); | ||||
| //    } | ||||
|  | ||||
|     /** | ||||
|      * 我们启动有加载很多 Embedding 模型,不晓得取哪个好,先 new 个 TransformersEmbeddingModel 跑 | ||||
|      * TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel | ||||
|      */ | ||||
|     @Bean | ||||
|     @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||||
|     public RedisVectorStore vectorStore(TransformersEmbeddingModel transformersEmbeddingModel, RedisVectorStoreProperties properties, | ||||
|                                         RedisProperties redisProperties) { | ||||
|         var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||||
|                 .withIndexName(properties.getIndex()) | ||||
|                 .withPrefix(properties.getPrefix()) | ||||
|                 .build(); | ||||
|  | ||||
|         RedisVectorStore redisVectorStore = new RedisVectorStore(config, transformersEmbeddingModel, | ||||
|                 new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||||
|                 properties.isInitializeSchema()); | ||||
|         redisVectorStore.afterPropertiesSet(); | ||||
|         return redisVectorStore; | ||||
|     } | ||||
| //    @Bean | ||||
| //    @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||||
| //    public RedisVectorStore vectorStore(TongYiTextEmbeddingModel tongYiTextEmbeddingModel, RedisVectorStoreProperties properties, | ||||
| //                                        RedisProperties redisProperties) { | ||||
| //        var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||||
| //                .withIndexName(properties.getIndex()) | ||||
| //                .withPrefix(properties.getPrefix()) | ||||
| //                .build(); | ||||
| // | ||||
| //        RedisVectorStore redisVectorStore = new RedisVectorStore(config, tongYiTextEmbeddingModel, | ||||
| //                new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||||
| //                properties.isInitializeSchema()); | ||||
| //        redisVectorStore.afterPropertiesSet(); | ||||
| //        return redisVectorStore; | ||||
| //    } | ||||
|  | ||||
|     @Bean | ||||
|     @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||||
| @@ -114,4 +118,10 @@ public class YudaoAiAutoConfiguration { | ||||
|         return new TokenTextSplitter(500, 100, 5, 10000, true); | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||||
|     public TokenCountEstimator tokenCountEstimator() { | ||||
|         return new JTokkitTokenCountEstimator(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||||
| import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||||
| import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||||
| import org.springframework.ai.chat.model.ChatModel; | ||||
| import org.springframework.ai.embedding.EmbeddingModel; | ||||
| import org.springframework.ai.image.ImageModel; | ||||
|  | ||||
| /** | ||||
| @@ -79,4 +80,16 @@ public interface AiModelFactory { | ||||
|      */ | ||||
|     SunoApi getOrCreateSunoApi(String apiKey, String url); | ||||
|  | ||||
|     /** | ||||
|      * 基于指定配置,获得 EmbeddingModel 对象 | ||||
|      * | ||||
|      * 如果不存在,则进行创建 | ||||
|      * | ||||
|      * @param platform 平台 | ||||
|      * @param apiKey   API KEY | ||||
|      * @param url      API URL | ||||
|      * @return ChatModel 对象 | ||||
|      */ | ||||
|     EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; | ||||
| import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; | ||||
| import com.alibaba.dashscope.aigc.generation.Generation; | ||||
| import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; | ||||
| import com.alibaba.dashscope.embeddings.TextEmbedding; | ||||
| import com.azure.ai.openai.OpenAIClient; | ||||
| import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; | ||||
| import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; | ||||
| @@ -37,6 +38,7 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; | ||||
| import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties; | ||||
| import org.springframework.ai.azure.openai.AzureOpenAiChatModel; | ||||
| import org.springframework.ai.chat.model.ChatModel; | ||||
| import org.springframework.ai.embedding.EmbeddingModel; | ||||
| import org.springframework.ai.image.ImageModel; | ||||
| import org.springframework.ai.model.function.FunctionCallbackContext; | ||||
| import org.springframework.ai.ollama.OllamaChatModel; | ||||
| @@ -175,6 +177,20 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||||
|         return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) { | ||||
|         String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url); | ||||
|         return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> { | ||||
|             // TODO @xin 先测试一个 | ||||
|             switch (platform) { | ||||
|                 case TONG_YI: | ||||
|                     return buildTongYiEmbeddingModel(apiKey); | ||||
|                 default: | ||||
|                     throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private static String buildClientCacheKey(Class<?> clazz, Object... params) { | ||||
|         if (ArrayUtil.isEmpty(params)) { | ||||
|             return clazz.getName(); | ||||
| @@ -238,8 +254,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel( | ||||
|      * ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} | ||||
|      * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} | ||||
|      */ | ||||
|     private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { | ||||
|         url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); | ||||
| @@ -248,8 +263,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel( | ||||
|      * ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} | ||||
|      * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} | ||||
|      */ | ||||
|     private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { | ||||
|         url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); | ||||
| @@ -315,4 +329,15 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||||
|         return new StabilityAiImageModel(stabilityAiApi); | ||||
|     } | ||||
|  | ||||
|     // ========== 各种创建 EmbeddingModel 的方法 ========== | ||||
|  | ||||
|     /** | ||||
|      * 可参考 {@link TongYiAutoConfiguration#tongYiTextEmbeddingClient(TextEmbedding, TongYiConnectionProperties)} | ||||
|      */ | ||||
|     private EmbeddingModel buildTongYiEmbeddingModel(String apiKey) { | ||||
|         TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties(); | ||||
|         connectionProperties.setApiKey(apiKey); | ||||
|         return new TongYiAutoConfiguration().tongYiTextEmbeddingClient(SpringUtil.getBean(TextEmbedding.class), connectionProperties); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,28 @@ | ||||
| package cn.iocoder.yudao.framework.ai.core.factory; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||||
| import org.springframework.ai.embedding.EmbeddingModel; | ||||
| import org.springframework.ai.vectorstore.VectorStore; | ||||
|  | ||||
| // TODO @xin:也放到 AiModelFactory 里面好了,后续改成 AiFactory | ||||
| /** | ||||
|  * AI Vector 模型工厂的接口类 | ||||
|  * | ||||
|  * @author xiaoxin | ||||
|  */ | ||||
| public interface AiVectorStoreFactory { | ||||
|  | ||||
|     /** | ||||
|      * 基于指定配置,获得 VectorStore 对象 | ||||
|      * <p> | ||||
|      * 如果不存在,则进行创建 | ||||
|      * | ||||
|      * @param embeddingModel 嵌入模型 | ||||
|      * @param platform       平台 | ||||
|      * @param apiKey         API KEY | ||||
|      * @param url            API URL | ||||
|      * @return VectorStore 对象 | ||||
|      */ | ||||
|     VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url); | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| package cn.iocoder.yudao.framework.ai.core.factory; | ||||
|  | ||||
| import cn.hutool.core.lang.Singleton; | ||||
| import cn.hutool.core.lang.func.Func0; | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||||
| import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; | ||||
| import org.springframework.ai.embedding.EmbeddingModel; | ||||
| import org.springframework.ai.vectorstore.RedisVectorStore; | ||||
| import org.springframework.ai.vectorstore.VectorStore; | ||||
| import org.springframework.boot.autoconfigure.data.redis.RedisProperties; | ||||
| import redis.clients.jedis.JedisPooled; | ||||
|  | ||||
| /** | ||||
|  * AI Vector 模型工厂的实现类 | ||||
|  * 使用 redisVectorStore 实现 VectorStore | ||||
|  * | ||||
|  * @author xiaoxin | ||||
|  */ | ||||
| public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory { | ||||
|  | ||||
|     @Override | ||||
|     public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) { | ||||
|         String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url); | ||||
|         return Singleton.get(cacheKey, (Func0<VectorStore>) () -> { | ||||
|             // TODO 芋艿 @xin 这两个配置取哪好呢 | ||||
|             // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上 | ||||
|             // TODO 回复:好的哈 | ||||
|             String index = "default-index"; | ||||
|             String prefix = "default:"; | ||||
|             var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||||
|                     .withIndexName(index) | ||||
|                     .withPrefix(prefix) | ||||
|                     .build(); | ||||
|             RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); | ||||
|             RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, | ||||
|                     new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||||
|                     true); | ||||
|             redisVectorStore.afterPropertiesSet(); | ||||
|             return redisVectorStore; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private static String buildClientCacheKey(Class<?> clazz, Object... params) { | ||||
|         if (ArrayUtil.isEmpty(params)) { | ||||
|             return clazz.getName(); | ||||
|         } | ||||
|         return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_")); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -19,6 +19,7 @@ import org.springframework.ai.embedding.EmbeddingModel; | ||||
| import org.springframework.ai.vectorstore.RedisVectorStore; | ||||
| import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig; | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||
| import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; | ||||
| @@ -31,13 +32,14 @@ import redis.clients.jedis.JedisPooled; | ||||
|  * TODO @xin 先拿 spring-ai 最新代码覆盖,1.0.0-M1 跟 redis 自动配置会冲突 | ||||
|  * | ||||
|  * TODO 这个官方,有说啥时候 fix 哇? | ||||
|  * TODO 看着是列在1.0.0-M2版本 | ||||
|  * | ||||
|  * @author Christian Tzolov | ||||
|  * @author Eddú Meléndez | ||||
|  */ | ||||
| @AutoConfiguration(after = RedisAutoConfiguration.class) | ||||
| @ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class}) | ||||
| //@ConditionalOnBean(JedisConnectionFactory.class) | ||||
| @ConditionalOnBean(JedisConnectionFactory.class) | ||||
| @EnableConfigurationProperties(RedisVectorStoreProperties.class) | ||||
| public class RedisVectorStoreAutoConfiguration { | ||||
|  | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV