Commit 8e012b10bb28d16cdde68c715babbcddb88e17b7

Authored by xiaoxin
1 parent 059f64ac

【优化】AI 知识库: VectorStore 获取 抽到 AiModelFactory

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.service.model; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.service.model;
2 2
3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; 4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
5 -import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;  
6 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; 5 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
7 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; 6 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -39,8 +38,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @@ -39,8 +38,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
39 38
40 @Resource 39 @Resource
41 private AiModelFactory modelFactory; 40 private AiModelFactory modelFactory;
42 - @Resource  
43 - private AiVectorStoreFactory vectorFactory;  
44 41
45 @Override 42 @Override
46 public Long createApiKey(AiApiKeySaveReqVO createReqVO) { 43 public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
@@ -149,7 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @@ -149,7 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
149 public VectorStore getOrCreateVectorStore(Long id) { 146 public VectorStore getOrCreateVectorStore(Long id) {
150 AiApiKeyDO apiKey = validateApiKey(id); 147 AiApiKeyDO apiKey = validateApiKey(id);
151 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); 148 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
152 - return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); 149 + return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
153 } 150 }
154 151
155 } 152 }
156 \ No newline at end of file 153 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
@@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.ai.config; @@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.ai.config;
2 2
3 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; 3 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; 4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
5 -import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;  
6 -import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactoryImpl;  
7 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; 5 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
8 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; 6 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions;
9 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; 7 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
@@ -38,11 +36,6 @@ public class YudaoAiAutoConfiguration { @@ -38,11 +36,6 @@ public class YudaoAiAutoConfiguration {
38 return new AiModelFactoryImpl(); 36 return new AiModelFactoryImpl();
39 } 37 }
40 38
41 - @Bean  
42 - public AiVectorStoreFactory aiVectorFactory() {  
43 - return new AiVectorStoreFactoryImpl();  
44 - }  
45 -  
46 39
47 // ========== 各种 AI Client 创建 ========== 40 // ========== 各种 AI Client 创建 ==========
48 41
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
6 import org.springframework.ai.chat.model.ChatModel; 6 import org.springframework.ai.chat.model.ChatModel;
7 import org.springframework.ai.embedding.EmbeddingModel; 7 import org.springframework.ai.embedding.EmbeddingModel;
8 import org.springframework.ai.image.ImageModel; 8 import org.springframework.ai.image.ImageModel;
  9 +import org.springframework.ai.vectorstore.VectorStore;
9 10
10 /** 11 /**
11 * AI Model 模型工厂的接口类 12 * AI Model 模型工厂的接口类
@@ -92,4 +93,17 @@ public interface AiModelFactory { @@ -92,4 +93,17 @@ public interface AiModelFactory {
92 */ 93 */
93 EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url); 94 EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url);
94 95
  96 + /**
  97 + * 基于指定配置,获得 VectorStore 对象
  98 + * <p>
  99 + * 如果不存在,则进行创建
  100 + *
  101 + * @param embeddingModel 嵌入模型
  102 + * @param platform 平台
  103 + * @param apiKey API KEY
  104 + * @param url API URL
  105 + * @return VectorStore 对象
  106 + */
  107 + VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url);
  108 +
95 } 109 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
13 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; 13 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
14 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; 14 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
15 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; 15 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
  16 +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
16 import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; 17 import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration;
17 import com.alibaba.cloud.ai.tongyi.TongYiConnectionProperties; 18 import com.alibaba.cloud.ai.tongyi.TongYiConnectionProperties;
18 import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel; 19 import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel;
@@ -54,13 +55,17 @@ import org.springframework.ai.qianfan.api.QianFanApi; @@ -54,13 +55,17 @@ import org.springframework.ai.qianfan.api.QianFanApi;
54 import org.springframework.ai.qianfan.api.QianFanImageApi; 55 import org.springframework.ai.qianfan.api.QianFanImageApi;
55 import org.springframework.ai.stabilityai.StabilityAiImageModel; 56 import org.springframework.ai.stabilityai.StabilityAiImageModel;
56 import org.springframework.ai.stabilityai.api.StabilityAiApi; 57 import org.springframework.ai.stabilityai.api.StabilityAiApi;
  58 +import org.springframework.ai.vectorstore.RedisVectorStore;
  59 +import org.springframework.ai.vectorstore.VectorStore;
57 import org.springframework.ai.zhipuai.ZhiPuAiChatModel; 60 import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
58 import org.springframework.ai.zhipuai.ZhiPuAiImageModel; 61 import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
59 import org.springframework.ai.zhipuai.api.ZhiPuAiApi; 62 import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
60 import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; 63 import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
  64 +import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
61 import org.springframework.retry.support.RetryTemplate; 65 import org.springframework.retry.support.RetryTemplate;
62 import org.springframework.web.client.ResponseErrorHandler; 66 import org.springframework.web.client.ResponseErrorHandler;
63 import org.springframework.web.client.RestClient; 67 import org.springframework.web.client.RestClient;
  68 +import redis.clients.jedis.JedisPooled;
64 69
65 import java.util.List; 70 import java.util.List;
66 71
@@ -191,6 +196,28 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -191,6 +196,28 @@ public class AiModelFactoryImpl implements AiModelFactory {
191 }); 196 });
192 } 197 }
193 198
  199 + @Override
  200 + public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
  201 + String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
  202 + return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
  203 + // TODO 芋艿 @xin 这两个配置取哪好呢
  204 + // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
  205 + // TODO 回复:好的哈
  206 + String index = "default-index";
  207 + String prefix = "default:";
  208 + var config = RedisVectorStore.RedisVectorStoreConfig.builder()
  209 + .withIndexName(index)
  210 + .withPrefix(prefix)
  211 + .build();
  212 + RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
  213 + RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
  214 + new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
  215 + true);
  216 + redisVectorStore.afterPropertiesSet();
  217 + return redisVectorStore;
  218 + });
  219 + }
  220 +
194 private static String buildClientCacheKey(Class<?> clazz, Object... params) { 221 private static String buildClientCacheKey(Class<?> clazz, Object... params) {
195 if (ArrayUtil.isEmpty(params)) { 222 if (ArrayUtil.isEmpty(params)) {
196 return clazz.getName(); 223 return clazz.getName();
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java deleted
1 -package cn.iocoder.yudao.framework.ai.core.factory;  
2 -  
3 -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;  
4 -import org.springframework.ai.embedding.EmbeddingModel;  
5 -import org.springframework.ai.vectorstore.VectorStore;  
6 -  
7 -// TODO @xin:也放到 AiModelFactory 里面好了,后续改成 AiFactory  
8 -/**  
9 - * AI Vector 模型工厂的接口类  
10 - *  
11 - * @author xiaoxin  
12 - */  
13 -public interface AiVectorStoreFactory {  
14 -  
15 - /**  
16 - * 基于指定配置,获得 VectorStore 对象  
17 - * <p>  
18 - * 如果不存在,则进行创建  
19 - *  
20 - * @param embeddingModel 嵌入模型  
21 - * @param platform 平台  
22 - * @param apiKey API KEY  
23 - * @param url API URL  
24 - * @return VectorStore 对象  
25 - */  
26 - VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url);  
27 -  
28 -}  
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java deleted
1 -package cn.iocoder.yudao.framework.ai.core.factory;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -import cn.hutool.core.lang.func.Func0;  
5 -import cn.hutool.core.util.ArrayUtil;  
6 -import cn.hutool.core.util.StrUtil;  
7 -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;  
8 -import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;  
9 -import org.springframework.ai.embedding.EmbeddingModel;  
10 -import org.springframework.ai.vectorstore.RedisVectorStore;  
11 -import org.springframework.ai.vectorstore.VectorStore;  
12 -import org.springframework.boot.autoconfigure.data.redis.RedisProperties;  
13 -import redis.clients.jedis.JedisPooled;  
14 -  
15 -/**  
16 - * AI Vector 模型工厂的实现类  
17 - * 使用 redisVectorStore 实现 VectorStore  
18 - *  
19 - * @author xiaoxin  
20 - */  
21 -public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory {  
22 -  
23 - @Override  
24 - public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {  
25 - String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);  
26 - return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {  
27 - // TODO 芋艿 @xin 这两个配置取哪好呢  
28 - // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上  
29 - // TODO 回复:好的哈  
30 - String index = "default-index";  
31 - String prefix = "default:";  
32 - var config = RedisVectorStore.RedisVectorStoreConfig.builder()  
33 - .withIndexName(index)  
34 - .withPrefix(prefix)  
35 - .build();  
36 - RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);  
37 - RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,  
38 - new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),  
39 - true);  
40 - redisVectorStore.afterPropertiesSet();  
41 - return redisVectorStore;  
42 - });  
43 - }  
44 -  
45 - private static String buildClientCacheKey(Class<?> clazz, Object... params) {  
46 - if (ArrayUtil.isEmpty(params)) {  
47 - return clazz.getName();  
48 - }  
49 - return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));  
50 - }  
51 -  
52 -}