Commit f97fb0a8fe9e7987233e0f81940b3521544f2031
1 parent
024109da
【新增】AI 知识库: AiVectorFactory 负责管理不同 EmbeddingModel 对应的 VectorStore
Showing
13 changed files
with
239 additions
and
52 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
| @@ -45,7 +45,7 @@ public class AiKnowledgeDO extends BaseDO { | @@ -45,7 +45,7 @@ public class AiKnowledgeDO extends BaseDO { | ||
| 45 | @TableField(typeHandler = JacksonTypeHandler.class) | 45 | @TableField(typeHandler = JacksonTypeHandler.class) |
| 46 | private List<Long> visibilityPermissions; | 46 | private List<Long> visibilityPermissions; |
| 47 | /** | 47 | /** |
| 48 | - * 嵌入模型编号,高质量模式时维护 | 48 | + * 嵌入模型编号 |
| 49 | */ | 49 | */ |
| 50 | private Long modelId; | 50 | private Long modelId; |
| 51 | /** | 51 | /** |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
| @@ -24,10 +24,14 @@ public class AiKnowledgeSegmentDO extends BaseDO { | @@ -24,10 +24,14 @@ public class AiKnowledgeSegmentDO extends BaseDO { | ||
| 24 | * 向量库的编号 | 24 | * 向量库的编号 |
| 25 | */ | 25 | */ |
| 26 | private String vectorId; | 26 | private String vectorId; |
| 27 | - // TODO @新:knowledgeId 加个,会方便点 | 27 | + /** |
| 28 | + * 知识库编号 | ||
| 29 | + * 关联 {@link AiKnowledgeDO#getId()} | ||
| 30 | + */ | ||
| 31 | + private Long knowledgeId; | ||
| 28 | /** | 32 | /** |
| 29 | * 文档编号 | 33 | * 文档编号 |
| 30 | - * | 34 | + * <p> |
| 31 | * 关联 {@link AiKnowledgeDocumentDO#getId()} | 35 | * 关联 {@link AiKnowledgeDocumentDO#getId()} |
| 32 | */ | 36 | */ |
| 33 | private Long documentId; | 37 | private Long documentId; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
| @@ -6,24 +6,27 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | @@ -6,24 +6,27 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; |
| 9 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | ||
| 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | 11 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 12 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 11 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; | 13 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; |
| 12 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; | 14 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; |
| 13 | import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; | 15 | import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; |
| 16 | +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 17 | +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | ||
| 14 | import jakarta.annotation.Resource; | 18 | import jakarta.annotation.Resource; |
| 15 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
| 16 | import org.springframework.ai.document.Document; | 20 | import org.springframework.ai.document.Document; |
| 17 | import org.springframework.ai.reader.tika.TikaDocumentReader; | 21 | import org.springframework.ai.reader.tika.TikaDocumentReader; |
| 18 | import org.springframework.ai.tokenizer.TokenCountEstimator; | 22 | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| 19 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; | 23 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; |
| 20 | -import org.springframework.ai.vectorstore.RedisVectorStore; | 24 | +import org.springframework.ai.vectorstore.VectorStore; |
| 21 | import org.springframework.core.io.ByteArrayResource; | 25 | import org.springframework.core.io.ByteArrayResource; |
| 22 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
| 23 | import org.springframework.transaction.annotation.Transactional; | 27 | import org.springframework.transaction.annotation.Transactional; |
| 24 | 28 | ||
| 25 | import java.util.List; | 29 | import java.util.List; |
| 26 | -import java.util.Objects; | ||
| 27 | 30 | ||
| 28 | /** | 31 | /** |
| 29 | * AI 知识库-文档 Service 实现类 | 32 | * AI 知识库-文档 Service 实现类 |
| @@ -42,9 +45,14 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | @@ -42,9 +45,14 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | ||
| 42 | @Resource | 45 | @Resource |
| 43 | private TokenTextSplitter tokenTextSplitter; | 46 | private TokenTextSplitter tokenTextSplitter; |
| 44 | @Resource | 47 | @Resource |
| 45 | - private TokenCountEstimator TOKEN_COUNT_ESTIMATOR; | 48 | + private TokenCountEstimator tokenCountEstimator; |
| 49 | + | ||
| 50 | + @Resource | ||
| 51 | + private AiApiKeyService apiKeyService; | ||
| 52 | + @Resource | ||
| 53 | + private AiKnowledgeService knowledgeService; | ||
| 46 | @Resource | 54 | @Resource |
| 47 | - private RedisVectorStore vectorStore; | 55 | + private AiChatModelService chatModelService; |
| 48 | 56 | ||
| 49 | 57 | ||
| 50 | // TODO 芋艿:需要 review 下,代码格式; | 58 | // TODO 芋艿:需要 review 下,代码格式; |
| @@ -53,18 +61,18 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | @@ -53,18 +61,18 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | ||
| 53 | public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { | 61 | public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { |
| 54 | // 1.1 下载文档 | 62 | // 1.1 下载文档 |
| 55 | String url = createReqVO.getUrl(); | 63 | String url = createReqVO.getUrl(); |
| 56 | - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url)); | ||
| 57 | // 1.2 加载文档 | 64 | // 1.2 加载文档 |
| 65 | + TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url)); | ||
| 58 | List<Document> documents = loader.get(); | 66 | List<Document> documents = loader.get(); |
| 59 | Document document = CollUtil.getFirst(documents); | 67 | Document document = CollUtil.getFirst(documents); |
| 60 | - // TODO @xin:是不是不存在,就抛出异常呀;厚泽 return 呀; | ||
| 61 | - Integer tokens = Objects.nonNull(document) ? TOKEN_COUNT_ESTIMATOR.estimate(document.getContent()) : 0; | ||
| 62 | - Integer wordCount = Objects.nonNull(document) ? document.getContent().length() : 0; | 68 | + String content = document.getContent(); |
| 69 | + Integer tokens = tokenCountEstimator.estimate(content); | ||
| 70 | + Integer wordCount = content.length(); | ||
| 63 | 71 | ||
| 72 | + // 1.3 文档记录入库 | ||
| 64 | AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) | 73 | AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) |
| 65 | .setTokens(tokens).setWordCount(wordCount) | 74 | .setTokens(tokens).setWordCount(wordCount) |
| 66 | .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); | 75 | .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); |
| 67 | - // 1.2 文档记录入库 | ||
| 68 | documentMapper.insert(documentDO); | 76 | documentMapper.insert(documentDO); |
| 69 | Long documentId = documentDO.getId(); | 77 | Long documentId = documentDO.getId(); |
| 70 | if (CollUtil.isEmpty(documents)) { | 78 | if (CollUtil.isEmpty(documents)) { |
| @@ -75,11 +83,16 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | @@ -75,11 +83,16 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | ||
| 75 | List<Document> segments = tokenTextSplitter.apply(documents); | 83 | List<Document> segments = tokenTextSplitter.apply(documents); |
| 76 | // 2.2 分段内容入库 | 84 | // 2.2 分段内容入库 |
| 77 | List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments, | 85 | List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments, |
| 78 | - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) | ||
| 79 | - .setTokens(TOKEN_COUNT_ESTIMATOR.estimate(segment.getContent())).setWordCount(segment.getContent().length()) | 86 | + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()) |
| 87 | + .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) | ||
| 80 | .setStatus(CommonStatusEnum.ENABLE.getStatus())); | 88 | .setStatus(CommonStatusEnum.ENABLE.getStatus())); |
| 81 | segmentMapper.insertBatch(segmentDOList); | 89 | segmentMapper.insertBatch(segmentDOList); |
| 82 | - // 3 向量化并存储 | 90 | + |
| 91 | + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); | ||
| 92 | + AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); | ||
| 93 | + // 3.1 获取向量存储实例 | ||
| 94 | + VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); | ||
| 95 | + // 3.2 向量化并存储 | ||
| 83 | vectorStore.add(segments); | 96 | vectorStore.add(segments); |
| 84 | return documentId; | 97 | return documentId; |
| 85 | } | 98 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
| 1 | package cn.iocoder.yudao.module.ai.service.knowledge; | 1 | package cn.iocoder.yudao.module.ai.service.knowledge; |
| 2 | + | ||
| 2 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; | 3 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; |
| 3 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; | 4 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; |
| 5 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | ||
| 4 | 6 | ||
| 5 | /** | 7 | /** |
| 6 | * AI 知识库-基础信息 Service 接口 | 8 | * AI 知识库-基础信息 Service 接口 |
| @@ -13,7 +15,7 @@ public interface AiKnowledgeService { | @@ -13,7 +15,7 @@ public interface AiKnowledgeService { | ||
| 13 | * 创建【我的】知识库 | 15 | * 创建【我的】知识库 |
| 14 | * | 16 | * |
| 15 | * @param createReqVO 创建信息 | 17 | * @param createReqVO 创建信息 |
| 16 | - * @param userId 用户编号 | 18 | + * @param userId 用户编号 |
| 17 | * @return 编号 | 19 | * @return 编号 |
| 18 | */ | 20 | */ |
| 19 | Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId); | 21 | Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId); |
| @@ -23,8 +25,16 @@ public interface AiKnowledgeService { | @@ -23,8 +25,16 @@ public interface AiKnowledgeService { | ||
| 23 | * 创建【我的】知识库 | 25 | * 创建【我的】知识库 |
| 24 | * | 26 | * |
| 25 | * @param updateReqVO 更新信息 | 27 | * @param updateReqVO 更新信息 |
| 26 | - * @param userId 用户编号 | 28 | + * @param userId 用户编号 |
| 27 | */ | 29 | */ |
| 28 | void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId); | 30 | void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId); |
| 29 | 31 | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 校验知识库是否存在 | ||
| 35 | + * | ||
| 36 | + * @param id 记录编号 | ||
| 37 | + */ | ||
| 38 | + AiKnowledgeDO validateKnowledgeExists(Long id); | ||
| 39 | + | ||
| 30 | } | 40 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
| @@ -29,7 +29,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | @@ -29,7 +29,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | ||
| 29 | private AiChatModelService chatModalService; | 29 | private AiChatModelService chatModalService; |
| 30 | 30 | ||
| 31 | @Resource | 31 | @Resource |
| 32 | - private AiKnowledgeMapper knowledgeBaseMapper; | 32 | + private AiKnowledgeMapper knowledgeMapper; |
| 33 | 33 | ||
| 34 | @Override | 34 | @Override |
| 35 | public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { | 35 | public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { |
| @@ -39,7 +39,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | @@ -39,7 +39,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | ||
| 39 | // 2. 插入知识库 | 39 | // 2. 插入知识库 |
| 40 | AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) | 40 | AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) |
| 41 | .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); | 41 | .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); |
| 42 | - knowledgeBaseMapper.insert(knowledgeBase); | 42 | + knowledgeMapper.insert(knowledgeBase); |
| 43 | return knowledgeBase.getId(); | 43 | return knowledgeBase.getId(); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| @@ -56,11 +56,12 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | @@ -56,11 +56,12 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | ||
| 56 | // 2. 更新知识库 | 56 | // 2. 更新知识库 |
| 57 | AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); | 57 | AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); |
| 58 | updateDO.setModel(model.getModel()); | 58 | updateDO.setModel(model.getModel()); |
| 59 | - knowledgeBaseMapper.updateById(updateDO); | 59 | + knowledgeMapper.updateById(updateDO); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | + @Override | ||
| 62 | public AiKnowledgeDO validateKnowledgeExists(Long id) { | 63 | public AiKnowledgeDO validateKnowledgeExists(Long id) { |
| 63 | - AiKnowledgeDO knowledgeBase = knowledgeBaseMapper.selectById(id); | 64 | + AiKnowledgeDO knowledgeBase = knowledgeMapper.selectById(id); |
| 64 | if (knowledgeBase == null) { | 65 | if (knowledgeBase == null) { |
| 65 | throw exception(KNOWLEDGE_NOT_EXISTS); | 66 | throw exception(KNOWLEDGE_NOT_EXISTS); |
| 66 | } | 67 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
| @@ -9,7 +9,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveR | @@ -9,7 +9,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveR | ||
| 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; |
| 10 | import jakarta.validation.Valid; | 10 | import jakarta.validation.Valid; |
| 11 | import org.springframework.ai.chat.model.ChatModel; | 11 | import org.springframework.ai.chat.model.ChatModel; |
| 12 | +import org.springframework.ai.embedding.EmbeddingModel; | ||
| 12 | import org.springframework.ai.image.ImageModel; | 13 | import org.springframework.ai.image.ImageModel; |
| 14 | +import org.springframework.ai.vectorstore.VectorStore; | ||
| 13 | 15 | ||
| 14 | import java.util.List; | 16 | import java.util.List; |
| 15 | 17 | ||
| @@ -84,6 +86,14 @@ public interface AiApiKeyService { | @@ -84,6 +86,14 @@ public interface AiApiKeyService { | ||
| 84 | ChatModel getChatModel(Long id); | 86 | ChatModel getChatModel(Long id); |
| 85 | 87 | ||
| 86 | /** | 88 | /** |
| 89 | + * 获得 EmbeddingModel 对象 | ||
| 90 | + * | ||
| 91 | + * @param id 编号 | ||
| 92 | + * @return EmbeddingModel 对象 | ||
| 93 | + */ | ||
| 94 | + EmbeddingModel getEmbeddingModel(Long id); | ||
| 95 | + | ||
| 96 | + /** | ||
| 87 | * 获得 ImageModel 对象 | 97 | * 获得 ImageModel 对象 |
| 88 | * | 98 | * |
| 89 | * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择 | 99 | * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择 |
| @@ -111,4 +121,12 @@ public interface AiApiKeyService { | @@ -111,4 +121,12 @@ public interface AiApiKeyService { | ||
| 111 | */ | 121 | */ |
| 112 | SunoApi getSunoApi(); | 122 | SunoApi getSunoApi(); |
| 113 | 123 | ||
| 124 | + /** | ||
| 125 | + * 获得 vector 对象 | ||
| 126 | + * | ||
| 127 | + * @param id 编号 | ||
| 128 | + * @return VectorStore 对象 | ||
| 129 | + */ | ||
| 130 | + VectorStore getOrCreateVectorStore(Long id); | ||
| 131 | + | ||
| 114 | } | 132 | } |
| 115 | \ No newline at end of file | 133 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.model; | @@ -2,6 +2,7 @@ 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.AiVectorFactory; | ||
| 5 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 6 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 6 | import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | 7 | import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; |
| 7 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 8 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| @@ -13,7 +14,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | @@ -13,7 +14,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | ||
| 13 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; | 14 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; |
| 14 | import jakarta.annotation.Resource; | 15 | import jakarta.annotation.Resource; |
| 15 | import org.springframework.ai.chat.model.ChatModel; | 16 | import org.springframework.ai.chat.model.ChatModel; |
| 17 | +import org.springframework.ai.embedding.EmbeddingModel; | ||
| 16 | import org.springframework.ai.image.ImageModel; | 18 | import org.springframework.ai.image.ImageModel; |
| 19 | +import org.springframework.ai.vectorstore.VectorStore; | ||
| 17 | import org.springframework.stereotype.Service; | 20 | import org.springframework.stereotype.Service; |
| 18 | import org.springframework.validation.annotation.Validated; | 21 | import org.springframework.validation.annotation.Validated; |
| 19 | 22 | ||
| @@ -36,6 +39,8 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | @@ -36,6 +39,8 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | ||
| 36 | 39 | ||
| 37 | @Resource | 40 | @Resource |
| 38 | private AiModelFactory modelFactory; | 41 | private AiModelFactory modelFactory; |
| 42 | + @Resource | ||
| 43 | + private AiVectorFactory vectorFactory; | ||
| 39 | 44 | ||
| 40 | @Override | 45 | @Override |
| 41 | public Long createApiKey(AiApiKeySaveReqVO createReqVO) { | 46 | public Long createApiKey(AiApiKeySaveReqVO createReqVO) { |
| @@ -105,6 +110,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | @@ -105,6 +110,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | ||
| 105 | } | 110 | } |
| 106 | 111 | ||
| 107 | @Override | 112 | @Override |
| 113 | + public EmbeddingModel getEmbeddingModel(Long id) { | ||
| 114 | + AiApiKeyDO apiKey = validateApiKey(id); | ||
| 115 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 116 | + return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + @Override | ||
| 108 | public ImageModel getImageModel(AiPlatformEnum platform) { | 120 | public ImageModel getImageModel(AiPlatformEnum platform) { |
| 109 | AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); | 121 | AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); |
| 110 | if (apiKey == null) { | 122 | if (apiKey == null) { |
| @@ -132,4 +144,11 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | @@ -132,4 +144,11 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | ||
| 132 | } | 144 | } |
| 133 | return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); | 145 | return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); |
| 134 | } | 146 | } |
| 147 | + | ||
| 148 | + @Override | ||
| 149 | + public VectorStore getOrCreateVectorStore(Long id) { | ||
| 150 | + AiApiKeyDO apiKey = validateApiKey(id); | ||
| 151 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 152 | + return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 153 | + } | ||
| 135 | } | 154 | } |
| 136 | \ No newline at end of file | 155 | \ 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,6 +2,8 @@ package cn.iocoder.yudao.framework.ai.config; | @@ -2,6 +2,8 @@ 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.AiVectorFactory; | ||
| 6 | +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactoryImpl; | ||
| 5 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; | 7 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; |
| 6 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; | 8 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; |
| 7 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 9 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| @@ -10,22 +12,15 @@ import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; | @@ -10,22 +12,15 @@ import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; | ||
| 10 | import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions; | 12 | import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions; |
| 11 | import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; | 13 | import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; |
| 12 | import lombok.extern.slf4j.Slf4j; | 14 | import lombok.extern.slf4j.Slf4j; |
| 13 | -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; | ||
| 14 | -import org.springframework.ai.document.MetadataMode; | ||
| 15 | -import org.springframework.ai.embedding.EmbeddingModel; | ||
| 16 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; | 15 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; |
| 17 | import org.springframework.ai.tokenizer.TokenCountEstimator; | 16 | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| 18 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; | 17 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; |
| 19 | -import org.springframework.ai.transformers.TransformersEmbeddingModel; | ||
| 20 | -import org.springframework.ai.vectorstore.RedisVectorStore; | ||
| 21 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 18 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 23 | -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; | ||
| 24 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 20 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 25 | import org.springframework.context.annotation.Bean; | 21 | import org.springframework.context.annotation.Bean; |
| 26 | import org.springframework.context.annotation.Import; | 22 | import org.springframework.context.annotation.Import; |
| 27 | import org.springframework.context.annotation.Lazy; | 23 | import org.springframework.context.annotation.Lazy; |
| 28 | -import redis.clients.jedis.JedisPooled; | ||
| 29 | 24 | ||
| 30 | /** | 25 | /** |
| 31 | * 芋道 AI 自动配置 | 26 | * 芋道 AI 自动配置 |
| @@ -43,6 +38,12 @@ public class YudaoAiAutoConfiguration { | @@ -43,6 +38,12 @@ public class YudaoAiAutoConfiguration { | ||
| 43 | return new AiModelFactoryImpl(); | 38 | return new AiModelFactoryImpl(); |
| 44 | } | 39 | } |
| 45 | 40 | ||
| 41 | + @Bean | ||
| 42 | + public AiVectorFactory aiVectorFactory() { | ||
| 43 | + return new AiVectorFactoryImpl(); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + | ||
| 46 | // ========== 各种 AI Client 创建 ========== | 47 | // ========== 各种 AI Client 创建 ========== |
| 47 | 48 | ||
| 48 | @Bean | 49 | @Bean |
| @@ -85,30 +86,31 @@ public class YudaoAiAutoConfiguration { | @@ -85,30 +86,31 @@ public class YudaoAiAutoConfiguration { | ||
| 85 | } | 86 | } |
| 86 | 87 | ||
| 87 | // ========== rag 相关 ========== | 88 | // ========== rag 相关 ========== |
| 88 | - @Bean | ||
| 89 | - @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||
| 90 | - public EmbeddingModel transformersEmbeddingClient() { | ||
| 91 | - return new TransformersEmbeddingModel(MetadataMode.EMBED); | ||
| 92 | - } | 89 | + // TODO @xin 免费版本 |
| 90 | +// @Bean | ||
| 91 | +// @Lazy // TODO 芋艿:临时注释,避免无法启动」 | ||
| 92 | +// public EmbeddingModel transformersEmbeddingClient() { | ||
| 93 | +// return new TransformersEmbeddingModel(MetadataMode.EMBED); | ||
| 94 | +// } | ||
| 93 | 95 | ||
| 94 | /** | 96 | /** |
| 95 | - * TODO @xin 抽离出去,根据具体模型走 | 97 | + * TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel |
| 96 | */ | 98 | */ |
| 97 | - @Bean | ||
| 98 | - @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||
| 99 | - public RedisVectorStore vectorStore(TransformersEmbeddingModel transformersEmbeddingModel, RedisVectorStoreProperties properties, | ||
| 100 | - RedisProperties redisProperties) { | ||
| 101 | - var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||
| 102 | - .withIndexName(properties.getIndex()) | ||
| 103 | - .withPrefix(properties.getPrefix()) | ||
| 104 | - .build(); | ||
| 105 | - | ||
| 106 | - RedisVectorStore redisVectorStore = new RedisVectorStore(config, transformersEmbeddingModel, | ||
| 107 | - new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||
| 108 | - properties.isInitializeSchema()); | ||
| 109 | - redisVectorStore.afterPropertiesSet(); | ||
| 110 | - return redisVectorStore; | ||
| 111 | - } | 99 | +// @Bean |
| 100 | +// @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||
| 101 | +// public RedisVectorStore vectorStore(TongYiTextEmbeddingModel tongYiTextEmbeddingModel, RedisVectorStoreProperties properties, | ||
| 102 | +// RedisProperties redisProperties) { | ||
| 103 | +// var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||
| 104 | +// .withIndexName(properties.getIndex()) | ||
| 105 | +// .withPrefix(properties.getPrefix()) | ||
| 106 | +// .build(); | ||
| 107 | +// | ||
| 108 | +// RedisVectorStore redisVectorStore = new RedisVectorStore(config, tongYiTextEmbeddingModel, | ||
| 109 | +// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||
| 110 | +// properties.isInitializeSchema()); | ||
| 111 | +// redisVectorStore.afterPropertiesSet(); | ||
| 112 | +// return redisVectorStore; | ||
| 113 | +// } | ||
| 112 | 114 | ||
| 113 | @Bean | 115 | @Bean |
| 114 | @Lazy // TODO 芋艿:临时注释,避免无法启动 | 116 | @Lazy // TODO 芋艿:临时注释,避免无法启动 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
| @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 4 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 4 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 5 | import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | 5 | 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.image.ImageModel; | 8 | import org.springframework.ai.image.ImageModel; |
| 8 | 9 | ||
| 9 | /** | 10 | /** |
| @@ -26,6 +27,18 @@ public interface AiModelFactory { | @@ -26,6 +27,18 @@ public interface AiModelFactory { | ||
| 26 | ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url); | 27 | ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url); |
| 27 | 28 | ||
| 28 | /** | 29 | /** |
| 30 | + * 基于指定配置,获得 EmbeddingModel 对象 | ||
| 31 | + * <p> | ||
| 32 | + * 如果不存在,则进行创建 | ||
| 33 | + * | ||
| 34 | + * @param platform 平台 | ||
| 35 | + * @param apiKey API KEY | ||
| 36 | + * @param url API URL | ||
| 37 | + * @return ChatModel 对象 | ||
| 38 | + */ | ||
| 39 | + EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url); | ||
| 40 | + | ||
| 41 | + /** | ||
| 29 | * 基于默认配置,获得 ChatModel 对象 | 42 | * 基于默认配置,获得 ChatModel 对象 |
| 30 | * | 43 | * |
| 31 | * 默认配置,指的是在 application.yaml 配置文件中的 spring.ai 相关的配置 | 44 | * 默认配置,指的是在 application.yaml 配置文件中的 spring.ai 相关的配置 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
| @@ -21,6 +21,7 @@ import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; | @@ -21,6 +21,7 @@ import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; | ||
| 21 | import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; | 21 | import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; |
| 22 | import com.alibaba.dashscope.aigc.generation.Generation; | 22 | import com.alibaba.dashscope.aigc.generation.Generation; |
| 23 | import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; | 23 | import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; |
| 24 | +import com.alibaba.dashscope.embeddings.TextEmbedding; | ||
| 24 | import com.azure.ai.openai.OpenAIClient; | 25 | import com.azure.ai.openai.OpenAIClient; |
| 25 | import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; | 26 | import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; |
| 26 | import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; | 27 | import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; |
| @@ -37,6 +38,7 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; | @@ -37,6 +38,7 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; | ||
| 37 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties; | 38 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties; |
| 38 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; | 39 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; |
| 39 | import org.springframework.ai.chat.model.ChatModel; | 40 | import org.springframework.ai.chat.model.ChatModel; |
| 41 | +import org.springframework.ai.embedding.EmbeddingModel; | ||
| 40 | import org.springframework.ai.image.ImageModel; | 42 | import org.springframework.ai.image.ImageModel; |
| 41 | import org.springframework.ai.model.function.FunctionCallbackContext; | 43 | import org.springframework.ai.model.function.FunctionCallbackContext; |
| 42 | import org.springframework.ai.ollama.OllamaChatModel; | 44 | import org.springframework.ai.ollama.OllamaChatModel; |
| @@ -98,6 +100,21 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -98,6 +100,21 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 98 | } | 100 | } |
| 99 | 101 | ||
| 100 | @Override | 102 | @Override |
| 103 | + public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) { | ||
| 104 | + String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url); | ||
| 105 | + return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> { | ||
| 106 | + // TODO @xin 先测试一个 | ||
| 107 | + switch (platform) { | ||
| 108 | + case TONG_YI: | ||
| 109 | + return buildTongYiEmbeddingModel(apiKey); | ||
| 110 | + default: | ||
| 111 | + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); | ||
| 112 | + } | ||
| 113 | + }); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + | ||
| 117 | + @Override | ||
| 101 | public ChatModel getDefaultChatModel(AiPlatformEnum platform) { | 118 | public ChatModel getDefaultChatModel(AiPlatformEnum platform) { |
| 102 | //noinspection EnhancedSwitchMigration | 119 | //noinspection EnhancedSwitchMigration |
| 103 | switch (platform) { | 120 | switch (platform) { |
| @@ -239,7 +256,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -239,7 +256,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 239 | 256 | ||
| 240 | /** | 257 | /** |
| 241 | * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel( | 258 | * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel( |
| 242 | - * ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} | 259 | + *ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} |
| 243 | */ | 260 | */ |
| 244 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { | 261 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { |
| 245 | url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); | 262 | url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); |
| @@ -249,7 +266,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -249,7 +266,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 249 | 266 | ||
| 250 | /** | 267 | /** |
| 251 | * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel( | 268 | * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel( |
| 252 | - * ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} | 269 | + *ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} |
| 253 | */ | 270 | */ |
| 254 | private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { | 271 | private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { |
| 255 | url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); | 272 | url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); |
| @@ -315,4 +332,15 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -315,4 +332,15 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 315 | return new StabilityAiImageModel(stabilityAiApi); | 332 | return new StabilityAiImageModel(stabilityAiApi); |
| 316 | } | 333 | } |
| 317 | 334 | ||
| 335 | + // ========== 各种创建 EmbeddingModel 的方法 ========== | ||
| 336 | + | ||
| 337 | + /** | ||
| 338 | + * 可参考 {@link TongYiAutoConfiguration#tongYiTextEmbeddingClient(TextEmbedding, TongYiConnectionProperties)} | ||
| 339 | + */ | ||
| 340 | + private EmbeddingModel buildTongYiEmbeddingModel(String apiKey) { | ||
| 341 | + TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties(); | ||
| 342 | + connectionProperties.setApiKey(apiKey); | ||
| 343 | + return new TongYiAutoConfiguration().tongYiTextEmbeddingClient(SpringUtil.getBean(TextEmbedding.class), connectionProperties); | ||
| 344 | + } | ||
| 345 | + | ||
| 318 | } | 346 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java
0 → 100644
| 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 | +/** | ||
| 8 | + * AI Vector 模型工厂的接口类 | ||
| 9 | + * @author xiaoxin | ||
| 10 | + */ | ||
| 11 | +public interface AiVectorFactory { | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 基于指定配置,获得 VectorStore 对象 | ||
| 16 | + * <p> | ||
| 17 | + * 如果不存在,则进行创建 | ||
| 18 | + * | ||
| 19 | + * @param embeddingModel 嵌入模型 | ||
| 20 | + * @param platform 平台 | ||
| 21 | + * @param apiKey API KEY | ||
| 22 | + * @param url API URL | ||
| 23 | + * @return VectorStore 对象 | ||
| 24 | + */ | ||
| 25 | + VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url); | ||
| 26 | + | ||
| 27 | +} |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java
0 → 100644
| 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 AiVectorFactoryImpl implements AiVectorFactory { | ||
| 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 | + String index = "default-index"; | ||
| 30 | + String prefix = "default:"; | ||
| 31 | + var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||
| 32 | + .withIndexName(index) | ||
| 33 | + .withPrefix(prefix) | ||
| 34 | + .build(); | ||
| 35 | + RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); | ||
| 36 | + RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, | ||
| 37 | + new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||
| 38 | + true); | ||
| 39 | + redisVectorStore.afterPropertiesSet(); | ||
| 40 | + return redisVectorStore; | ||
| 41 | + }); | ||
| 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 | +} |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java
| @@ -19,6 +19,7 @@ import org.springframework.ai.embedding.EmbeddingModel; | @@ -19,6 +19,7 @@ import org.springframework.ai.embedding.EmbeddingModel; | ||
| 19 | import org.springframework.ai.vectorstore.RedisVectorStore; | 19 | import org.springframework.ai.vectorstore.RedisVectorStore; |
| 20 | import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig; | 20 | import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig; |
| 21 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 21 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 22 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||
| 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| 24 | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; | 25 | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; |
| @@ -38,7 +39,7 @@ import redis.clients.jedis.JedisPooled; | @@ -38,7 +39,7 @@ import redis.clients.jedis.JedisPooled; | ||
| 38 | */ | 39 | */ |
| 39 | @AutoConfiguration(after = RedisAutoConfiguration.class) | 40 | @AutoConfiguration(after = RedisAutoConfiguration.class) |
| 40 | @ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class}) | 41 | @ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class}) |
| 41 | -//@ConditionalOnBean(JedisConnectionFactory.class) | 42 | +@ConditionalOnBean(JedisConnectionFactory.class) |
| 42 | @EnableConfigurationProperties(RedisVectorStoreProperties.class) | 43 | @EnableConfigurationProperties(RedisVectorStoreProperties.class) |
| 43 | public class RedisVectorStoreAutoConfiguration { | 44 | public class RedisVectorStoreAutoConfiguration { |
| 44 | 45 |