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 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
5   -import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;
6 5 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
7 6 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
8 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
... ... @@ -39,8 +38,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
39 38  
40 39 @Resource
41 40 private AiModelFactory modelFactory;
42   - @Resource
43   - private AiVectorStoreFactory vectorFactory;
44 41  
45 42 @Override
46 43 public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
... ... @@ -149,7 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
149 146 public VectorStore getOrCreateVectorStore(Long id) {
150 147 AiApiKeyDO apiKey = validateApiKey(id);
151 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 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 2  
3 3 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
4 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 5 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
8 6 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions;
9 7 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
... ... @@ -38,11 +36,6 @@ public class YudaoAiAutoConfiguration {
38 36 return new AiModelFactoryImpl();
39 37 }
40 38  
41   - @Bean
42   - public AiVectorStoreFactory aiVectorFactory() {
43   - return new AiVectorStoreFactoryImpl();
44   - }
45   -
46 39  
47 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 import org.springframework.ai.chat.model.ChatModel;
7 7 import org.springframework.ai.embedding.EmbeddingModel;
8 8 import org.springframework.ai.image.ImageModel;
  9 +import org.springframework.ai.vectorstore.VectorStore;
9 10  
10 11 /**
11 12 * AI Model 模型工厂的接口类
... ... @@ -92,4 +93,17 @@ public interface AiModelFactory {
92 93 */
93 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 13 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
14 14 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
15 15 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
  16 +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
16 17 import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration;
17 18 import com.alibaba.cloud.ai.tongyi.TongYiConnectionProperties;
18 19 import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel;
... ... @@ -54,13 +55,17 @@ import org.springframework.ai.qianfan.api.QianFanApi;
54 55 import org.springframework.ai.qianfan.api.QianFanImageApi;
55 56 import org.springframework.ai.stabilityai.StabilityAiImageModel;
56 57 import org.springframework.ai.stabilityai.api.StabilityAiApi;
  58 +import org.springframework.ai.vectorstore.RedisVectorStore;
  59 +import org.springframework.ai.vectorstore.VectorStore;
57 60 import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
58 61 import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
59 62 import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
60 63 import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
  64 +import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
61 65 import org.springframework.retry.support.RetryTemplate;
62 66 import org.springframework.web.client.ResponseErrorHandler;
63 67 import org.springframework.web.client.RestClient;
  68 +import redis.clients.jedis.JedisPooled;
64 69  
65 70 import java.util.List;
66 71  
... ... @@ -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 221 private static String buildClientCacheKey(Class<?> clazz, Object... params) {
195 222 if (ArrayUtil.isEmpty(params)) {
196 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   -}