From b4af042c640cc0e710f7c4121b6375c50210d54e Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 15 Aug 2024 11:28:02 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 4 ++ .../knowledge/AiKnowledgeController.java | 39 ++++++++++ .../vo/AiKnowledgeCreateMyReqVO.java | 31 ++++++++ .../vo/AiKnowledgeUpdateMyReqVO.java | 36 ++++++++++ .../knowledge/AiKnowledgeBaseDO.java | 56 +++++++++++++++ .../knowledge/AiKnowledgeDocumentDO.java | 55 ++++++++++++++ .../knowledge/AiKnowledgeSegmentDO.java | 48 +++++++++++++ .../knowledge/AiKnowledgeBaseMapper.java | 12 ++++ .../knowledge/AiKnowledgeDocumentMapper.java | 12 ++++ .../knowledge/AiKnowledgeSegmentMapper.java | 12 ++++ .../service/knowledge/AiEmbeddingService.java | 26 +++++++ ...eImpl.java => AiEmbeddingServiceImpl.java} | 12 ++-- .../knowledge/AiKnowledgeBaseService.java | 30 ++++++++ .../knowledge/AiKnowledgeBaseServiceImpl.java | 72 +++++++++++++++++++ .../knowledge/AiKnowledgeDocumentService.java | 10 +++ .../AiKnowledgeDocumentServiceImpl.java | 16 +++++ .../knowledge/AiKnowledgeSegmentService.java | 11 +++ .../AiKnowledgeSegmentServiceImpl.java | 16 +++++ .../ai/service/knowledge/DocService.java | 15 ---- .../yudao-spring-boot-starter-ai/pom.xml | 6 +- .../RedisVectorStoreAutoConfiguration.java | 1 + 21 files changed, 497 insertions(+), 23 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/{DocServiceImpl.java => AiEmbeddingServiceImpl.java} (76%) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocService.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index ddfb489f3..b68591796 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -50,4 +50,8 @@ public interface ErrorCodeConstants { ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!"); ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!"); + + // ========== API 知识库 1-022-008-000 ========== + ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!"); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java new file mode 100644 index 000000000..9d9c99a9a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeBaseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - AI 知识库") +@RestController +@RequestMapping("/ai/knowledge") +public class AiKnowledgeController { + + @Resource + private AiKnowledgeBaseService knowledgeBaseService; + + @PostMapping("/create-my") + @Operation(summary = "创建【我的】知识库") + public CommonResult createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) { + return success(knowledgeBaseService.createKnowledgeMy(createReqVO, getLoginUserId())); + } + + + @PutMapping("/update-my") + @Operation(summary = "更新【我的】知识库") + public CommonResult updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) { + knowledgeBaseService.updateKnowledgeMy(updateReqVO, getLoginUserId()); + return success(true); + } + + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java new file mode 100644 index 000000000..817fcaabb --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * @author xiaoxin + */ +@Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO") +@Data +public class AiKnowledgeCreateMyReqVO { + + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "") + @NotBlank(message = "知识库名称不能为空") + private String name; + + @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") + private String description; + + @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") + private List visibilityPermissions; + + @Schema(description = "嵌入模型 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "嵌入模型不能为空") + private Long modelId; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java new file mode 100644 index 000000000..2bc39d5db --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * @author xiaoxin + */ +@Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO") +@Data +public class AiKnowledgeUpdateMyReqVO { + + + @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") + @NotNull(message = "知识库编号不能为空") + private Long id; + + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "") + @NotBlank(message = "知识库名称不能为空") + private String name; + + @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") + private String description; + + @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") + private List visibilityPermissions; + + @Schema(description = "嵌入模型 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "嵌入模型不能为空") + private Long modelId; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java new file mode 100644 index 000000000..3bf45d348 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; + + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.List; + +/** + * AI 知识库 DO + * + * @author xiaoxin + */ +@TableName(value = "ai_knowledge_base") +@Data +public class AiKnowledgeBaseDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 用户编号 + *

+ * 关联 AdminUserDO 的 userId 字段 + */ + private Long userId; + /** + * 知识库名称 + */ + private String name; + /** + * 知识库描述 + */ + private String description; + /** + * 可见权限,只能选择哪些人可见 + */ + private List visibilityPermissions; + /** + * 嵌入模型编号,高质量模式时维护 + */ + private Long modelId; + /** + * 模型标识 + */ + private String model; + /** + * 是否启用 + */ + private Boolean status; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java new file mode 100644 index 000000000..8de3697a7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * AI 知识库-文档 DO + * + * @author xiaoxin + */ +@TableName(value = "ai_knowledge_document") +@Data +public class AiKnowledgeDocumentDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 知识库编号 + */ + private Long knowledgeId; + /** + * 文件名称 + */ + private String name; + /** + * 内容 + */ + private String content; + /** + * 文件 URL + */ + private String url; + /** + * token数量 + */ + private Integer tokens; + /** + * 字符数 + */ + private Integer wordCount; + /** + * 切片状态 + */ + private Integer sliceStatus; + /** + * 是否启用 + */ + private Boolean status; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java new file mode 100644 index 000000000..4ce3bb4ee --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * AI 知识库-文档分段 DO + * + * @author xiaoxin + */ +@TableName(value = "ai_knowledge_segment") +@Data +public class AiKnowledgeSegmentDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 向量库的id + */ + private String vectorId; + /** + * 文档编号 + */ + private Long documentId; + /** + * 切片内容 + */ + private String content; + /** + * 字符数 + */ + private Integer wordCount; + /** + * token数量 + */ + private Integer tokens; + /** + * 是否启用 + */ + private Boolean status; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java new file mode 100644 index 000000000..3a23aa55d --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO; + +/** + * AI 知识库基础信息 Mapper + * + * @author xiaoxin + */ +public interface AiKnowledgeBaseMapper extends BaseMapperX { +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java new file mode 100644 index 000000000..35532956f --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; + +/** + * AI 知识库-文档 Mapper + * + * @author xiaoxin + */ +public interface AiKnowledgeDocumentMapper extends BaseMapperX { +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java new file mode 100644 index 000000000..c3cbca2c1 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; + +/** + * AI 知识库-分片 Mapper + * + * @author xiaoxin + */ +public interface AiKnowledgeSegmentMapper extends BaseMapperX { +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java new file mode 100644 index 000000000..38ff0d022 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; + +import org.springframework.ai.document.Document; + +import java.util.List; + +/** + * AI 嵌入 Service 接口 + * + * @author xiaoxin + */ +public interface AiEmbeddingService { + + /** + * 向量化文档 + */ + void embeddingDoc(); + + + /** + * 相似查询 + * + * @param content 查询内容 + */ + List similaritySearch(String content); +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java similarity index 76% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocServiceImpl.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java index b0f4afaf8..37c414428 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java @@ -1,24 +1,23 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.RedisVectorStore; +import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.List; /** - * AI 知识库 Service 实现类 + * AI 嵌入 Service 实现类 * * @author xiaoxin */ @Service -@Slf4j -public class DocServiceImpl implements DocService { +public class AiEmbeddingServiceImpl implements AiEmbeddingService { @Resource private RedisVectorStore vectorStore; @@ -40,4 +39,9 @@ public class DocServiceImpl implements DocService { vectorStore.add(segments); } + @Override + public List similaritySearch(String content) { + SearchRequest request = SearchRequest.query(content); + return vectorStore.similaritySearch(request); + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java new file mode 100644 index 000000000..7657ab748 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; + +/** + * AI 知识库-基础信息 Service 接口 + * + * @author xiaoxin + */ +public interface AiKnowledgeBaseService { + + + /** + * 创建【我的】知识库 + * + * @param createReqVO 创建信息 + * @param userId 用户编号 + * @return 编号 + */ + Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId); + + + /** + * 创建【我的】知识库 + * + * @param updateReqVO 更新信息 + * @param userId 用户编号 + */ + void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId); +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java new file mode 100644 index 000000000..4f28726bd --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeBaseMapper; +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS; + +/** + * AI 知识库-基础信息 Service 实现类 + * + * @author xiaoxin + */ +@Service +@Slf4j +public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService { + + @Resource + private AiKnowledgeBaseMapper knowledgeBaseMapper; + @Resource + private AiChatModelService chatModalService; + + @Override + public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { + AiChatModelDO model = validateChatModel(createReqVO.getModelId()); + + AiKnowledgeBaseDO knowledgeBaseDO = BeanUtils.toBean(createReqVO, AiKnowledgeBaseDO.class); + knowledgeBaseDO.setModel(model.getModel()).setUserId(userId); + + knowledgeBaseMapper.insert(knowledgeBaseDO); + return knowledgeBaseDO.getId(); + } + + @Override + public void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId) { + + AiKnowledgeBaseDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); + if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) { + throw exception(KNOWLEDGE_NOT_EXISTS); + } + AiChatModelDO model = validateChatModel(updateReqVO.getModelId()); + AiKnowledgeBaseDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeBaseDO.class); + updateDO.setModel(model.getModel()); + + knowledgeBaseMapper.updateById(updateDO); + } + + + private AiChatModelDO validateChatModel(Long id) { + AiChatModelDO model = chatModalService.validateChatModel(id); + Assert.notNull(model, "未找到对应嵌入模型"); + return model; + } + + public AiKnowledgeBaseDO validateKnowledgeExists(Long id) { + AiKnowledgeBaseDO knowledgeBase = knowledgeBaseMapper.selectById(id); + if (knowledgeBase == null) { + throw exception(KNOWLEDGE_NOT_EXISTS); + } + return knowledgeBase; + } +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java new file mode 100644 index 000000000..5af45e5e8 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -0,0 +1,10 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; + +/** + * AI 知识库-文档 Service 接口 + * + * @author xiaoxin + */ +public interface AiKnowledgeDocumentService { + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java new file mode 100644 index 000000000..84ebb617e --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * AI 知识库-文档 Service 实现类 + * + * @author xiaoxin + */ +@Service +@Slf4j +public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService { + + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java new file mode 100644 index 000000000..003ce5c96 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -0,0 +1,11 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; + +/** + * AI 知识库-分片 Service 接口 + * + * @author xiaoxin + */ +public interface AiKnowledgeSegmentService { + + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java new file mode 100644 index 000000000..aa5facc36 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.ai.service.knowledge; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * AI 知识库-基础信息 Service 实现类 + * + * @author xiaoxin + */ +@Service +@Slf4j +public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { + + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocService.java deleted file mode 100644 index 47905d4b1..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/DocService.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -/** - * AI 知识库 Service 接口 - * - * @author xiaoxin - */ -public interface DocService { - - /** - * 向量化文档 - */ - void embeddingDoc(); - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index ae1f37948..4585311c7 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -57,11 +57,9 @@ ${spring-ai.version} - - org.springframework.data - spring-data-redis - true + cn.iocoder.boot + yudao-spring-boot-starter-redis diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java index 61c38dd1d..615b05f78 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java @@ -31,6 +31,7 @@ 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 From 8e54eef8af762e77084c4d7742307afa2cdf212a Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 15 Aug 2024 15:57:03 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=EF=BC=9A=E6=96=87=E6=A1=A3=E5=88=87?= =?UTF-8?q?=E7=89=87=E5=90=91=E9=87=8F=E5=8C=96=E5=85=A5=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeDocumentStatusEnum.java | 39 +++++++++++ .../vo/AiKnowledgeCreateMyReqVO.java | 4 +- .../vo/AiKnowledgeDocumentCreateReqVO.java | 27 ++++++++ .../knowledge/AiKnowledgeBaseDO.java | 12 +++- .../knowledge/AiKnowledgeDocumentDO.java | 11 ++- .../knowledge/AiKnowledgeSegmentDO.java | 7 +- .../knowledge/AiKnowledgeBaseMapper.java | 2 + .../knowledge/AiKnowledgeDocumentMapper.java | 2 + .../knowledge/AiKnowledgeSegmentMapper.java | 2 + .../service/knowledge/AiEmbeddingService.java | 7 +- .../knowledge/AiEmbeddingServiceImpl.java | 23 ++----- .../knowledge/AiKnowledgeBaseServiceImpl.java | 9 ++- .../knowledge/AiKnowledgeDocumentService.java | 11 +++ .../AiKnowledgeDocumentServiceImpl.java | 68 +++++++++++++++++++ 14 files changed, 190 insertions(+), 34 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java new file mode 100644 index 000000000..a37fa8643 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.ai.enums.knowledge; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * AI 知识库-文档状态的枚举 + * + * @author xiaoxin + */ +@AllArgsConstructor +@Getter +public enum AiKnowledgeDocumentStatusEnum implements IntArrayValuable { + + IN_PROGRESS(10, "索引中"), + SUCCESS(20, "可用"), + FAIL(30, "失败"); + + /** + * 状态 + */ + private final Integer status; + + /** + * 状态名 + */ + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiKnowledgeDocumentStatusEnum::getStatus).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java index 817fcaabb..fa9161eba 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java @@ -14,11 +14,11 @@ import java.util.List; @Data public class AiKnowledgeCreateMyReqVO { - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "") + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") @NotBlank(message = "知识库名称不能为空") private String name; - @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") + @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档") private String description; @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java new file mode 100644 index 000000000..fa24eef72 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * @author xiaoxin + */ +@Schema(description = "管理后台 - AI 知识库【创建文档】 Request VO") +@Data +public class AiKnowledgeDocumentCreateReqVO { + + + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") + @NotNull(message = "知识库编号不能为空") + private Long knowledgeId; + + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆") + @NotBlank(message = "文档名称不能为空") + private String name; + + @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") + private String url; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java index 3bf45d348..81cbf3ac9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java @@ -1,10 +1,13 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; import java.util.List; @@ -40,7 +43,8 @@ public class AiKnowledgeBaseDO extends BaseDO { /** * 可见权限,只能选择哪些人可见 */ - private List visibilityPermissions; + @TableField(typeHandler = JacksonTypeHandler.class) + private List visibilityPermissions; /** * 嵌入模型编号,高质量模式时维护 */ @@ -50,7 +54,9 @@ public class AiKnowledgeBaseDO extends BaseDO { */ private String model; /** - * 是否启用 + * 状态 + *

+ * 枚举 {@link CommonStatusEnum} */ - private Boolean status; + private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java index 8de3697a7..75d927cf0 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -46,10 +48,15 @@ public class AiKnowledgeDocumentDO extends BaseDO { private Integer wordCount; /** * 切片状态 + *

+ * 枚举 {@link AiKnowledgeDocumentStatusEnum} */ private Integer sliceStatus; + /** - * 是否启用 + * 状态 + *

+ * 枚举 {@link CommonStatusEnum} */ - private Boolean status; + private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index 4ce3bb4ee..657a3739b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; @@ -41,8 +42,10 @@ public class AiKnowledgeSegmentDO extends BaseDO { */ private Integer tokens; /** - * 是否启用 + * 状态 + *

+ * 枚举 {@link CommonStatusEnum} */ - private Boolean status; + private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java index 3a23aa55d..cad90fcfe 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java @@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO; +import org.apache.ibatis.annotations.Mapper; /** * AI 知识库基础信息 Mapper * * @author xiaoxin */ +@Mapper public interface AiKnowledgeBaseMapper extends BaseMapperX { } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java index 35532956f..af55f545a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java @@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import org.apache.ibatis.annotations.Mapper; /** * AI 知识库-文档 Mapper * * @author xiaoxin */ +@Mapper public interface AiKnowledgeDocumentMapper extends BaseMapperX { } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index c3cbca2c1..5043ee0ca 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import org.apache.ibatis.annotations.Mapper; /** * AI 知识库-分片 Mapper * * @author xiaoxin */ +@Mapper public interface AiKnowledgeSegmentMapper extends BaseMapperX { } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java index 38ff0d022..9055cdc18 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.SearchRequest; import java.util.List; @@ -12,9 +13,9 @@ import java.util.List; public interface AiEmbeddingService { /** - * 向量化文档 + * 向量化文档并存储 */ - void embeddingDoc(); + void add(List documents); /** @@ -22,5 +23,5 @@ public interface AiEmbeddingService { * * @param content 查询内容 */ - List similaritySearch(String content); + List similaritySearch(SearchRequest request); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java index 37c414428..a2c3e819d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java @@ -2,11 +2,9 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import jakarta.annotation.Resource; import org.springframework.ai.document.Document; -import org.springframework.ai.reader.tika.TikaDocumentReader; -import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.RedisVectorStore; import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.List; @@ -21,27 +19,14 @@ public class AiEmbeddingServiceImpl implements AiEmbeddingService { @Resource private RedisVectorStore vectorStore; - @Resource - private TokenTextSplitter tokenTextSplitter; - - // TODO @xin 临时测试用,后续删 - @Value("classpath:/webapp/test/Fel.pdf") - private org.springframework.core.io.Resource data; @Override - public void embeddingDoc() { - // 读取文件 - TikaDocumentReader loader = new TikaDocumentReader(data); - List documents = loader.get(); - // 文档分段 - List segments = tokenTextSplitter.apply(documents); - // 向量化并存储 - vectorStore.add(segments); + public void add(List documents) { + vectorStore.add(documents); } @Override - public List similaritySearch(String content) { - SearchRequest request = SearchRequest.query(content); + public List similaritySearch(SearchRequest request) { return vectorStore.similaritySearch(request); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java index 4f28726bd..63f4f53db 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; @@ -25,17 +26,19 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_ @Slf4j public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService { - @Resource - private AiKnowledgeBaseMapper knowledgeBaseMapper; @Resource private AiChatModelService chatModalService; + @Resource + private AiKnowledgeBaseMapper knowledgeBaseMapper; + + @Override public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { AiChatModelDO model = validateChatModel(createReqVO.getModelId()); AiKnowledgeBaseDO knowledgeBaseDO = BeanUtils.toBean(createReqVO, AiKnowledgeBaseDO.class); - knowledgeBaseDO.setModel(model.getModel()).setUserId(userId); + knowledgeBaseDO.setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); knowledgeBaseMapper.insert(knowledgeBaseDO); return knowledgeBaseDO.getId(); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 5af45e5e8..52c62abf7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; + /** * AI 知识库-文档 Service 接口 * @@ -7,4 +9,13 @@ package cn.iocoder.yudao.module.ai.service.knowledge; */ public interface AiKnowledgeDocumentService { + + /** + * 创建文档 + * + * @param createReqVO 文档创建 Request VO + * @return 文档编号 + */ + Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 84ebb617e..caef4b802 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -1,7 +1,25 @@ package cn.iocoder.yudao.module.ai.service.knowledge; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; +import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.document.Document; +import org.springframework.ai.reader.tika.TikaDocumentReader; +import org.springframework.ai.transformer.splitter.TokenTextSplitter; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; /** * AI 知识库-文档 Service 实现类 @@ -12,5 +30,55 @@ import org.springframework.stereotype.Service; @Slf4j public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService { + @Resource + private AiKnowledgeDocumentMapper documentMapper; + @Resource + private AiKnowledgeSegmentMapper segmentMapper; + @Resource + private TokenTextSplitter tokenTextSplitter; + + @Resource + private AiEmbeddingService embeddingService; + + // TODO @xin 临时测试用,后续删 + @Value("classpath:/webapp/test/Fel.pdf") + private org.springframework.core.io.Resource data; + + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { + AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class); + documentDO + //todo + .setTokens(0).setWordCount(0) + .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); + documentMapper.insert(documentDO); + + TikaDocumentReader loader = new TikaDocumentReader(data); + List documents = loader.get(); + Long documentId = documentDO.getId(); + if (CollUtil.isEmpty(documents)) { + log.info("文档内容为空"); + return documentId; + } + + // 文档分段 + List segments = tokenTextSplitter.apply(documents); + + List segmentDOList = CollectionUtils.convertList(segments, + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) + //todo + .setTokens(0).setWordCount(0) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + + // 分段内容入库 + segmentMapper.insertBatch(segmentDOList); + + //向量化并存储 + embeddingService.add(segments); + + return documentId; + } } From 238f603f690ef7e747b177729237af1a008b3056 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 15 Aug 2024 16:50:16 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=EF=BC=9A=E6=96=87=E6=A1=A3=20token?= =?UTF-8?q?=E3=80=81=E5=AD=97=E7=AC=A6=E6=95=B0=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/knowledge/AiEmbeddingService.java | 2 +- .../knowledge/AiEmbeddingServiceImpl.java | 3 +- .../AiKnowledgeDocumentServiceImpl.java | 29 ++++++++++++------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java index 9055cdc18..eee2f8044 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java @@ -21,7 +21,7 @@ public interface AiEmbeddingService { /** * 相似查询 * - * @param content 查询内容 + * @param request 查询实体 */ List similaritySearch(SearchRequest request); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java index a2c3e819d..2a6e75722 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java @@ -4,7 +4,6 @@ import jakarta.annotation.Resource; import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.RedisVectorStore; import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.List; @@ -21,6 +20,8 @@ public class AiEmbeddingServiceImpl implements AiEmbeddingService { private RedisVectorStore vectorStore; @Override +// @Async + // TODO xiaoxin 报错先注释 public void add(List documents) { vectorStore.add(documents); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index caef4b802..9ee5c4eed 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -14,12 +14,14 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.reader.tika.TikaDocumentReader; +import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Objects; /** * AI 知识库-文档 Service 实现类 @@ -41,7 +43,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Resource private AiEmbeddingService embeddingService; - // TODO @xin 临时测试用,后续删 + private static final JTokkitTokenCountEstimator TOKEN_COUNT_ESTIMATOR = new JTokkitTokenCountEstimator(); + + // TODO xiaoxin 临时测试用,后续删 @Value("classpath:/webapp/test/Fel.pdf") private org.springframework.core.io.Resource data; @@ -49,18 +53,23 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Override @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { - AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class); - documentDO - //todo - .setTokens(0).setWordCount(0) - .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); - documentMapper.insert(documentDO); + // TODO xiaoxin 后续从 url 加载 TikaDocumentReader loader = new TikaDocumentReader(data); + // 加载文档 List documents = loader.get(); + Document document = CollUtil.getFirst(documents); + // TODO 芋艿 文档层面有没有可能会比较大,这两个字段是否可以从分段表计算得出? + Integer tokens = Objects.nonNull(document) ? TOKEN_COUNT_ESTIMATOR.estimate(document.getContent()) : 0; + Integer wordCount = Objects.nonNull(document) ? document.getContent().length() : 0; + + AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class); + documentDO.setTokens(tokens).setWordCount(wordCount) + .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); + // 文档记录入库 + documentMapper.insert(documentDO); Long documentId = documentDO.getId(); if (CollUtil.isEmpty(documents)) { - log.info("文档内容为空"); return documentId; } @@ -69,10 +78,8 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic List segmentDOList = CollectionUtils.convertList(segments, segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) - //todo - .setTokens(0).setWordCount(0) + .setTokens(TOKEN_COUNT_ESTIMATOR.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); - // 分段内容入库 segmentMapper.insertBatch(segmentDOList); From 6cceab5ba4ed23c5d29ee9992449e8539156fc38 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 19 Aug 2024 19:24:36 +0800 Subject: [PATCH 04/14] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91AI=20=E5=A4=A7=E6=A8=A1=E5=9E=8B=EF=BC=9A?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/AiKnowledgeCreateMyReqVO.java | 7 ++----- .../vo/AiKnowledgeDocumentCreateReqVO.java | 5 +++-- .../vo/AiKnowledgeUpdateMyReqVO.java | 8 ++------ .../ai/dal/dataobject/image/AiImageDO.java | 2 +- .../knowledge/AiKnowledgeBaseDO.java | 8 ++++---- .../knowledge/AiKnowledgeDocumentDO.java | 8 +++++--- .../knowledge/AiKnowledgeSegmentDO.java | 10 ++++++---- .../dal/dataobject/mindmap/AiMindMapDO.java | 2 +- .../ai/dal/dataobject/music/AiMusicDO.java | 2 +- .../ai/dal/dataobject/write/AiWriteDO.java | 2 +- .../service/knowledge/AiEmbeddingService.java | 2 +- .../knowledge/AiEmbeddingServiceImpl.java | 2 ++ .../knowledge/AiKnowledgeBaseService.java | 2 +- .../knowledge/AiKnowledgeBaseServiceImpl.java | 18 ++++++++++------- .../knowledge/AiKnowledgeDocumentService.java | 1 - .../AiKnowledgeDocumentServiceImpl.java | 20 +++++++++---------- .../knowledge/AiKnowledgeSegmentService.java | 3 +-- .../AiKnowledgeSegmentServiceImpl.java | 3 +-- 18 files changed, 53 insertions(+), 52 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java index fa9161eba..ac94a4c15 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java @@ -7,9 +7,6 @@ import lombok.Data; import java.util.List; -/** - * @author xiaoxin - */ @Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO") @Data public class AiKnowledgeCreateMyReqVO { @@ -21,10 +18,10 @@ public class AiKnowledgeCreateMyReqVO { @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档") private String description; - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") + @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") private List visibilityPermissions; - @Schema(description = "嵌入模型 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "嵌入模型不能为空") private Long modelId; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java index fa24eef72..10ad036b2 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; +import org.hibernate.validator.constraints.URL; /** * @author xiaoxin @@ -12,7 +13,6 @@ import lombok.Data; @Data public class AiKnowledgeDocumentCreateReqVO { - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") @NotNull(message = "知识库编号不能为空") private Long knowledgeId; @@ -21,7 +21,8 @@ public class AiKnowledgeDocumentCreateReqVO { @NotBlank(message = "文档名称不能为空") private String name; - @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") + @URL(message = "文档 URL 格式不正确") private String url; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java index 2bc39d5db..e1f6a31af 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java @@ -7,14 +7,10 @@ import lombok.Data; import java.util.List; -/** - * @author xiaoxin - */ @Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO") @Data public class AiKnowledgeUpdateMyReqVO { - @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") @NotNull(message = "知识库编号不能为空") private Long id; @@ -26,10 +22,10 @@ public class AiKnowledgeUpdateMyReqVO { @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") private String description; - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") + @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]") private List visibilityPermissions; - @Schema(description = "嵌入模型 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "嵌入模型不能为空") private Long modelId; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java index 6768d904b..8584c5e14 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java @@ -32,7 +32,7 @@ public class AiImageDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java index 81cbf3ac9..d33114f2d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; - import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -12,19 +10,20 @@ import lombok.Data; import java.util.List; +// TODO @xin:要不把 AiKnowledgeBaseDO 改成 AiKnowledgeDO。感觉 base 后缀,感觉有点奇怪(让人以为是基类)。然后,我们很多地方的外键编号,都是 knowledgeId /** * AI 知识库 DO * * @author xiaoxin */ -@TableName(value = "ai_knowledge_base") +@TableName(value = "ai_knowledge_base", autoResultMap = true) @Data public class AiKnowledgeBaseDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** * 用户编号 @@ -40,6 +39,7 @@ public class AiKnowledgeBaseDO extends BaseDO { * 知识库描述 */ private String description; + // TODO @新:如果全部可见,需要怎么设置? /** * 可见权限,只能选择哪些人可见 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java index 75d927cf0..486602509 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; -import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -20,10 +19,12 @@ public class AiKnowledgeDocumentDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** * 知识库编号 + * + * 关联 {@link AiKnowledgeBaseDO#getId()} */ private Long knowledgeId; /** @@ -39,7 +40,7 @@ public class AiKnowledgeDocumentDO extends BaseDO { */ private String url; /** - * token数量 + * token 数量 */ private Integer tokens; /** @@ -59,4 +60,5 @@ public class AiKnowledgeDocumentDO extends BaseDO { * 枚举 {@link CommonStatusEnum} */ private Integer status; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index 657a3739b..2032bfd5e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -19,14 +18,17 @@ public class AiKnowledgeSegmentDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** - * 向量库的id + * 向量库的编号 */ private String vectorId; + // TODO @新:knowledgeId 加个,会方便点 /** * 文档编号 + * + * 关联 {@link AiKnowledgeDocumentDO#getId()} */ private Long documentId; /** @@ -38,7 +40,7 @@ public class AiKnowledgeSegmentDO extends BaseDO { */ private Integer wordCount; /** - * token数量 + * token 数量 */ private Integer tokens; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java index 0442a52d7..824881bf3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java @@ -19,7 +19,7 @@ public class AiMindMapDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java index 8a6cbe828..97491ec4f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java @@ -25,7 +25,7 @@ public class AiMusicDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java index 752876f2a..5d2f6dcf1 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java @@ -20,7 +20,7 @@ public class AiWriteDO extends BaseDO { /** * 编号 */ - @TableId(type = IdType.AUTO) + @TableId private Long id; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java index eee2f8044..ee4b3d03c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java @@ -17,11 +17,11 @@ public interface AiEmbeddingService { */ void add(List documents); - /** * 相似查询 * * @param request 查询实体 */ List similaritySearch(SearchRequest request); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java index 2a6e75722..689ccea03 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service; import java.util.List; +// TODO @xin:是不是不用 AiEmbeddingServiceImpl,直接 vectorStore 注入到需要的地方就好啦。通过 KnowledgeDocumentService 返回就好。 /** * AI 嵌入 Service 实现类 * @@ -30,4 +31,5 @@ public class AiEmbeddingServiceImpl implements AiEmbeddingService { public List similaritySearch(SearchRequest request) { return vectorStore.similaritySearch(request); } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java index 7657ab748..be96b0918 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdat */ public interface AiKnowledgeBaseService { - /** * 创建【我的】知识库 * @@ -27,4 +26,5 @@ public interface AiKnowledgeBaseService { * @param userId 用户编号 */ void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java index 63f4f53db..c208c92ba 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java @@ -32,33 +32,36 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService { @Resource private AiKnowledgeBaseMapper knowledgeBaseMapper; - @Override public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { + // TODO @xin:貌似直接调用 chatModalService.validateChatModel(id) 完事,不用搞个方法 + // 1. 校验模型配置 AiChatModelDO model = validateChatModel(createReqVO.getModelId()); - AiKnowledgeBaseDO knowledgeBaseDO = BeanUtils.toBean(createReqVO, AiKnowledgeBaseDO.class); - knowledgeBaseDO.setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); - + // 2. 插入知识库 + // TODO @xin:不用 DO 结尾 + AiKnowledgeBaseDO knowledgeBaseDO = BeanUtils.toBean(createReqVO, AiKnowledgeBaseDO.class) + .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); knowledgeBaseMapper.insert(knowledgeBaseDO); return knowledgeBaseDO.getId(); } @Override public void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId) { - + // 1.1 校验知识库存在 AiKnowledgeBaseDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) { throw exception(KNOWLEDGE_NOT_EXISTS); } + // 1.2 校验模型配置 AiChatModelDO model = validateChatModel(updateReqVO.getModelId()); + + // 2. 更新知识库 AiKnowledgeBaseDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeBaseDO.class); updateDO.setModel(model.getModel()); - knowledgeBaseMapper.updateById(updateDO); } - private AiChatModelDO validateChatModel(Long id) { AiChatModelDO model = chatModalService.validateChatModel(id); Assert.notNull(model, "未找到对应嵌入模型"); @@ -72,4 +75,5 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService { } return knowledgeBase; } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 52c62abf7..82c4f7b91 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocum */ public interface AiKnowledgeDocumentService { - /** * 创建文档 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 9ee5c4eed..537033015 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -43,28 +43,30 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Resource private AiEmbeddingService embeddingService; + // TODO @xin:@Resource 注入 private static final JTokkitTokenCountEstimator TOKEN_COUNT_ESTIMATOR = new JTokkitTokenCountEstimator(); // TODO xiaoxin 临时测试用,后续删 @Value("classpath:/webapp/test/Fel.pdf") private org.springframework.core.io.Resource data; - + // TODO 芋艿:需要 review 下,代码格式; + // TODO @xin:最好有 1、/2、/3 这种,让代码更有层次感 @Override @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { - // TODO xiaoxin 后续从 url 加载 TikaDocumentReader loader = new TikaDocumentReader(data); // 加载文档 List documents = loader.get(); Document document = CollUtil.getFirst(documents); - // TODO 芋艿 文档层面有没有可能会比较大,这两个字段是否可以从分段表计算得出? + // TODO @xin:是不是不存在,就抛出异常呀;厚泽 return 呀; + // TODO 芋艿 文档层面有没有可能会比较大,这两个字段是否可以从分段表计算得出?回复:先直接算; Integer tokens = Objects.nonNull(document) ? TOKEN_COUNT_ESTIMATOR.estimate(document.getContent()) : 0; Integer wordCount = Objects.nonNull(document) ? document.getContent().length() : 0; - AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class); - documentDO.setTokens(tokens).setWordCount(wordCount) + AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) + .setTokens(tokens).setWordCount(wordCount) .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); // 文档记录入库 documentMapper.insert(documentDO); @@ -75,17 +77,15 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic // 文档分段 List segments = tokenTextSplitter.apply(documents); - + // 分段内容入库 List segmentDOList = CollectionUtils.convertList(segments, segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) .setTokens(TOKEN_COUNT_ESTIMATOR.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); - // 分段内容入库 segmentMapper.insertBatch(segmentDOList); - - //向量化并存储 + // 向量化并存储 embeddingService.add(segments); - return documentId; } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 003ce5c96..7caea9ff4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -1,11 +1,10 @@ package cn.iocoder.yudao.module.ai.service.knowledge; /** - * AI 知识库-分片 Service 接口 + * AI 知识库分片 Service 接口 * * @author xiaoxin */ public interface AiKnowledgeSegmentService { - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index aa5facc36..226c5f8fb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -4,7 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** - * AI 知识库-基础信息 Service 实现类 + * AI 知识库分片 Service 实现类 * * @author xiaoxin */ @@ -12,5 +12,4 @@ import org.springframework.stereotype.Service; @Slf4j public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { - } From ed2296e4c772724004d92b0b0dc3b209cd70a78b Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Wed, 28 Aug 2024 16:01:10 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo=E3=80=91A?= =?UTF-8?q?I=20=E7=9F=A5=E8=AF=86=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledge/AiKnowledgeController.java | 4 +-- ...nowledgeBaseDO.java => AiKnowledgeDO.java} | 5 ++- .../knowledge/AiKnowledgeDocumentDO.java | 2 +- .../knowledge/AiKnowledgeBaseMapper.java | 4 +-- .../service/knowledge/AiEmbeddingService.java | 27 -------------- .../knowledge/AiEmbeddingServiceImpl.java | 35 ------------------- .../AiKnowledgeDocumentServiceImpl.java | 24 ++++++------- ...seService.java => AiKnowledgeService.java} | 2 +- ...eImpl.java => AiKnowledgeServiceImpl.java} | 31 ++++++---------- .../ai/config/YudaoAiAutoConfiguration.java | 10 +++++- 10 files changed, 39 insertions(+), 105 deletions(-) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/{AiKnowledgeBaseDO.java => AiKnowledgeDO.java} (81%) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/{AiKnowledgeBaseService.java => AiKnowledgeService.java} (94%) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/{AiKnowledgeBaseServiceImpl.java => AiKnowledgeServiceImpl.java} (66%) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index 9d9c99a9a..9eae6b70c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; -import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeBaseService; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -19,7 +19,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti public class AiKnowledgeController { @Resource - private AiKnowledgeBaseService knowledgeBaseService; + private AiKnowledgeService knowledgeBaseService; @PostMapping("/create-my") @Operation(summary = "创建【我的】知识库") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java similarity index 81% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java index d33114f2d..89e7486dc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java @@ -10,15 +10,14 @@ import lombok.Data; import java.util.List; -// TODO @xin:要不把 AiKnowledgeBaseDO 改成 AiKnowledgeDO。感觉 base 后缀,感觉有点奇怪(让人以为是基类)。然后,我们很多地方的外键编号,都是 knowledgeId /** * AI 知识库 DO * * @author xiaoxin */ -@TableName(value = "ai_knowledge_base", autoResultMap = true) +@TableName(value = "ai_knowledge", autoResultMap = true) @Data -public class AiKnowledgeBaseDO extends BaseDO { +public class AiKnowledgeDO extends BaseDO { /** * 编号 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java index 486602509..c5e526cce 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java @@ -24,7 +24,7 @@ public class AiKnowledgeDocumentDO extends BaseDO { /** * 知识库编号 * - * 关联 {@link AiKnowledgeBaseDO#getId()} + * 关联 {@link AiKnowledgeDO#getId()} */ private Long knowledgeId; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java index cad90fcfe..710b65429 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import org.apache.ibatis.annotations.Mapper; /** @@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper; * @author xiaoxin */ @Mapper -public interface AiKnowledgeBaseMapper extends BaseMapperX { +public interface AiKnowledgeBaseMapper extends BaseMapperX { } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java deleted file mode 100644 index ee4b3d03c..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import org.springframework.ai.document.Document; -import org.springframework.ai.vectorstore.SearchRequest; - -import java.util.List; - -/** - * AI 嵌入 Service 接口 - * - * @author xiaoxin - */ -public interface AiEmbeddingService { - - /** - * 向量化文档并存储 - */ - void add(List documents); - - /** - * 相似查询 - * - * @param request 查询实体 - */ - List similaritySearch(SearchRequest request); - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java deleted file mode 100644 index 689ccea03..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import jakarta.annotation.Resource; -import org.springframework.ai.document.Document; -import org.springframework.ai.vectorstore.RedisVectorStore; -import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.stereotype.Service; - -import java.util.List; - -// TODO @xin:是不是不用 AiEmbeddingServiceImpl,直接 vectorStore 注入到需要的地方就好啦。通过 KnowledgeDocumentService 返回就好。 -/** - * AI 嵌入 Service 实现类 - * - * @author xiaoxin - */ -@Service -public class AiEmbeddingServiceImpl implements AiEmbeddingService { - - @Resource - private RedisVectorStore vectorStore; - - @Override -// @Async - // TODO xiaoxin 报错先注释 - public void add(List documents) { - vectorStore.add(documents); - } - - @Override - public List similaritySearch(SearchRequest request) { - return vectorStore.similaritySearch(request); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 537033015..69a73d6f7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -14,8 +14,9 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.reader.tika.TikaDocumentReader; -import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; +import org.springframework.ai.tokenizer.TokenCountEstimator; import org.springframework.ai.transformer.splitter.TokenTextSplitter; +import org.springframework.ai.vectorstore.RedisVectorStore; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -39,52 +40,49 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Resource private TokenTextSplitter tokenTextSplitter; - @Resource - private AiEmbeddingService embeddingService; + private TokenCountEstimator TOKEN_COUNT_ESTIMATOR; + @Resource + private RedisVectorStore vectorStore; - // TODO @xin:@Resource 注入 - private static final JTokkitTokenCountEstimator TOKEN_COUNT_ESTIMATOR = new JTokkitTokenCountEstimator(); // TODO xiaoxin 临时测试用,后续删 @Value("classpath:/webapp/test/Fel.pdf") private org.springframework.core.io.Resource data; // TODO 芋艿:需要 review 下,代码格式; - // TODO @xin:最好有 1、/2、/3 这种,让代码更有层次感 @Override @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { // TODO xiaoxin 后续从 url 加载 TikaDocumentReader loader = new TikaDocumentReader(data); - // 加载文档 + // 1.1 加载文档 List documents = loader.get(); Document document = CollUtil.getFirst(documents); // TODO @xin:是不是不存在,就抛出异常呀;厚泽 return 呀; - // TODO 芋艿 文档层面有没有可能会比较大,这两个字段是否可以从分段表计算得出?回复:先直接算; Integer tokens = Objects.nonNull(document) ? TOKEN_COUNT_ESTIMATOR.estimate(document.getContent()) : 0; Integer wordCount = Objects.nonNull(document) ? document.getContent().length() : 0; AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) .setTokens(tokens).setWordCount(wordCount) .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); - // 文档记录入库 + // 1.2 文档记录入库 documentMapper.insert(documentDO); Long documentId = documentDO.getId(); if (CollUtil.isEmpty(documents)) { return documentId; } - // 文档分段 + // 2.1 文档分段 List segments = tokenTextSplitter.apply(documents); - // 分段内容入库 + // 2.2 分段内容入库 List segmentDOList = CollectionUtils.convertList(segments, segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) .setTokens(TOKEN_COUNT_ESTIMATOR.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); segmentMapper.insertBatch(segmentDOList); - // 向量化并存储 - embeddingService.add(segments); + // 3 向量化并存储 + vectorStore.add(segments); return documentId; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java similarity index 94% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java index be96b0918..91b0c9b3e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdat * * @author xiaoxin */ -public interface AiKnowledgeBaseService { +public interface AiKnowledgeService { /** * 创建【我的】知识库 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java similarity index 66% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index c208c92ba..a981b877f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -1,12 +1,11 @@ package cn.iocoder.yudao.module.ai.service.knowledge; -import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeBaseMapper; import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; @@ -24,7 +23,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_ */ @Service @Slf4j -public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService { +public class AiKnowledgeServiceImpl implements AiKnowledgeService { @Resource private AiChatModelService chatModalService; @@ -34,42 +33,34 @@ public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService { @Override public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { - // TODO @xin:貌似直接调用 chatModalService.validateChatModel(id) 完事,不用搞个方法 // 1. 校验模型配置 - AiChatModelDO model = validateChatModel(createReqVO.getModelId()); + AiChatModelDO model = chatModalService.validateChatModel(createReqVO.getModelId()); // 2. 插入知识库 - // TODO @xin:不用 DO 结尾 - AiKnowledgeBaseDO knowledgeBaseDO = BeanUtils.toBean(createReqVO, AiKnowledgeBaseDO.class) + AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); - knowledgeBaseMapper.insert(knowledgeBaseDO); - return knowledgeBaseDO.getId(); + knowledgeBaseMapper.insert(knowledgeBase); + return knowledgeBase.getId(); } @Override public void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId) { // 1.1 校验知识库存在 - AiKnowledgeBaseDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); + AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) { throw exception(KNOWLEDGE_NOT_EXISTS); } // 1.2 校验模型配置 - AiChatModelDO model = validateChatModel(updateReqVO.getModelId()); + AiChatModelDO model = chatModalService.validateChatModel(updateReqVO.getModelId()); // 2. 更新知识库 - AiKnowledgeBaseDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeBaseDO.class); + AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); updateDO.setModel(model.getModel()); knowledgeBaseMapper.updateById(updateDO); } - private AiChatModelDO validateChatModel(Long id) { - AiChatModelDO model = chatModalService.validateChatModel(id); - Assert.notNull(model, "未找到对应嵌入模型"); - return model; - } - - public AiKnowledgeBaseDO validateKnowledgeExists(Long id) { - AiKnowledgeBaseDO knowledgeBase = knowledgeBaseMapper.selectById(id); + public AiKnowledgeDO validateKnowledgeExists(Long id) { + AiKnowledgeDO knowledgeBase = knowledgeBaseMapper.selectById(id); if (knowledgeBase == null) { throw exception(KNOWLEDGE_NOT_EXISTS); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 543444fdd..8566a0941 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -13,6 +13,8 @@ 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; @@ -90,7 +92,7 @@ public class YudaoAiAutoConfiguration { } /** - * 我们启动有加载很多 Embedding 模型,不晓得取哪个好,先 new 个 TransformersEmbeddingModel 跑 + * TODO @xin 抽离出去,根据具体模型走 */ @Bean @Lazy // TODO 芋艿:临时注释,避免无法启动 @@ -114,4 +116,10 @@ public class YudaoAiAutoConfiguration { return new TokenTextSplitter(500, 100, 5, 10000, true); } + @Bean + @Lazy // TODO 芋艿:临时注释,避免无法启动 + public TokenCountEstimator tokenCountEstimator() { + return new JTokkitTokenCountEstimator(); + } + } \ No newline at end of file From 024109dac9237d06766daf8f1aa0e5347b5bf361 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 29 Aug 2024 10:12:47 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E4=BB=8Eurl=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...BaseMapper.java => AiKnowledgeMapper.java} | 2 +- .../AiKnowledgeDocumentServiceImpl.java | 24 ++++++++++++------ .../knowledge/AiKnowledgeServiceImpl.java | 4 +-- .../src/main/resources/webapp/test/Fel.pdf | Bin 352908 -> 0 bytes 4 files changed, 19 insertions(+), 11 deletions(-) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/{AiKnowledgeBaseMapper.java => AiKnowledgeMapper.java} (80%) delete mode 100755 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/webapp/test/Fel.pdf diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java similarity index 80% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java index 710b65429..41e71ccad 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java @@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper; * @author xiaoxin */ @Mapper -public interface AiKnowledgeBaseMapper extends BaseMapperX { +public interface AiKnowledgeMapper extends BaseMapperX { } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 69a73d6f7..2af8b9d90 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.collection.CollUtil; +import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -17,7 +18,7 @@ import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.tokenizer.TokenCountEstimator; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.RedisVectorStore; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -46,17 +47,14 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic private RedisVectorStore vectorStore; - // TODO xiaoxin 临时测试用,后续删 - @Value("classpath:/webapp/test/Fel.pdf") - private org.springframework.core.io.Resource data; - // TODO 芋艿:需要 review 下,代码格式; @Override @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { - // TODO xiaoxin 后续从 url 加载 - TikaDocumentReader loader = new TikaDocumentReader(data); - // 1.1 加载文档 + // 1.1 下载文档 + String url = createReqVO.getUrl(); + TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url)); + // 1.2 加载文档 List documents = loader.get(); Document document = CollUtil.getFirst(documents); // TODO @xin:是不是不存在,就抛出异常呀;厚泽 return 呀; @@ -86,4 +84,14 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return documentId; } + private org.springframework.core.io.Resource downloadFile(String url) { + try { + byte[] bytes = HttpUtil.downloadBytes(url); + return new ByteArrayResource(bytes); + } catch (Exception e) { + log.error("[downloadFile][url({}) 下载失败]", url, e); + throw new RuntimeException(e); + } + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index a981b877f..5889bcef7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreat import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeBaseMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -29,7 +29,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { private AiChatModelService chatModalService; @Resource - private AiKnowledgeBaseMapper knowledgeBaseMapper; + private AiKnowledgeMapper knowledgeBaseMapper; @Override public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/webapp/test/Fel.pdf b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/webapp/test/Fel.pdf deleted file mode 100755 index 405b67fedada1d989b18a7d81df1149fc7ebec01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352908 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

Fijw^)$D%QILN=)zF{%qagD!ovQOM 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

Ql{C5jDPsKZ(&1FAJne`d?X^bb zZHJ~(D@?RPs}VI!E38bEM3n?sOw-=jcx@Un15rYgf2sf`-OYWUE0A>*7>Xr5v{vm{ z;6U~^o0^==iOQq+O-6>GQgb6TEKIy(%rn2RFnZpYc}+(81%Dw$*xdZcbrn=xTACNs zd5@Hh&8da{*QK?=)_%tr`gcXe{$or|1A{G;Di)&R;^J+mlt>peGyy2*!r0VQZk_rL z6g0FL-;BqF;-ukS|0`4fLxN17imqD!l=cSh%(eqBvpIz~X~x!hQGtpsrSEhT3yql}nzf6cCzDY26vL$29%q1Y(HnYp^zC0N@CQyK|;*vO4zo7FpADF)(KU1wZUBx1aJ^2_SqDd zkLd77iclg`ZTfL2r?UXPYerWB6mlpr>kMe3a<}Fs(Q>)4*&G=H~VE3^!> zx>`tdzI1kw2@ak}uwLgv)k4x#vU$NKb_X-h^aol%V8;a-AUJx#O>$(p7S(1SM`L~& zv)KP_UO@voY+iDjQ*Usg6Iq8ksK`w6cGBCycnsqBi9v}i!7mL5F?qiiTmXl zLL+%C@u?=TfS|jE{e!3G>%LO-q`L-QEB%384>9^ac2ZpwgFtFXrmTjlBVe# zu}w_stVB5~*76jtsk54r0QvRZ&E)##Quh_=&}>eNdTQ;*D?LjcB+~{Ny_u*3eRAmp zrwf1xo@GZqVcQWSC(D&Y2^D0`^tyjYlu92`zf94X4LaW8DWY@FFS_u>(8ALWNL#9K|K-Ah}CDvn>WAT%(ESSoJ2UK zaKMCJow5Nc!Tb|L?i0bTHasQo`Z6x%*E(CZhxlwzM;luj379;AS}l^7IP3dQjzc}? z-$dXc`0uu2-UktsbO17Q-k-e24o8~1osToikBbo)t1pqyHEJW}mZ*9aq1thWTD%WU z5<9k*@r5T0^iP340!~o_49gU5H>B6a7w7|M*ex!cEa7_Yn=)|&1E23g)6)Cl-o1f( z@%|j3qw?^s@P+>I?R91t7N{#F-p>J)nIHQ0>h+ycM+K;Z;jW8M0w0yOfLfR5vm6=y z2%s7bsA&HV^CmPfW(~VY38>eJ+0r%M~dzR1D)}iPd-Tfvuh1&AuC_O8bNkmpb$TI>$UcL1i)}<=ctn z67CH5k{^})fuFfl-h^y#p-gU&d-(s`5cO&Q2-9^jIPg=(=0+{_>d)&0dWOUJKZlf; zVp0tE6k!bZ6xA)Rl>DC{+>XFkSoHqqP)RKjOaBM}wFkJ)xt(J0TezzkmRI{wG_#ZSm+ELq``iGVo|acM21UPUx_ zN20y87*$s$;P95>aX?<7N(PD?*%pwHyTG&JV&#wPw-iHVXJ$%R{+T_?(|lcCfQjjv z>I(gKf%~7?D?F+FzUIqH{&RVnJ2Q=pwC6w=fc)lwKXTE6;#X?Z+z7y+_9|Yhr^oG5 ziDb>{5=U+@9<`nKgV0_eq1CElylgq*_nR= z_S&W~PpFCgt*QJs&S1M5rmYP9Ze}dbBl~J#`hTbKK1ylPG?W3_Ztt(pP^}L-Nb~C_ ztMe{7x60!J{FBjhqC8LKbgJ5xNJLo?RT+zxca6SbZhWGM&&))RFjY|alSh;-qc0vBKMT3zZUJrsn7Kcjl?Xu6V-kN$aVUF)mdBd7Vh!+j^O z$)B8vf(eeQyAYv{y5ZMMYbH>-JpAhLPnVr12Ii{|EQQ!C<&GItj{Q<~t2U1NnBQNf znp^BTd0G-@TQo?S#BT2b7Ev z*GXo`ATK>&6@#(Rzt6Ia7b6c2Z&~%YE!7!MQT>~IzqMtVA4V_$IlLSsLcGI{b*Woq zGjm9B4&*YyD{&XXcD?tvB*+aK z5svh%g+^TC=U29^j&2sSV2?ui7=(B!scw{!Dgt(hDO; zTLj)BWV5)<+1HB5l6{6tM{9O$`i5)dZNu0q2yg#|f5ekoOfz#S^_vqK{g|PNuGcV} zR}(C-p1nB%@~KS#rXUE`GvpmkkxjdH#T-_@!8MhpwcF7Su?$fe-rW!5ZY#rup>mlt z0hjSdFHX|T)QP-j+hIkNrM?k)23!a23XMdep=*7A=Y}Zt%zecve?H!%fS}DbtE2ow zT?gA!FOQ#q9)vn?4mko3*CTXrl|;bOFus;2mcSo2Q4AYI)9V-(c9^GP5PjNFAf<3} z%9Dq#;R-H#cr49Zwx4guNA>bfE$`ZOq}PDc4=L6>YFVh@wbMKmK4P8Ca?(0&Fk4yN zQiX6NE?Qb2lft|5lI@zh8H_3}K^OFv7KWbXIrVgY%co znM^7(raW#LMoTB!?*n*Z~w_MO(z?f=DzQXKJ%Xg(mC9t*Y($A z(!4@ghP0N+Z>Md@kdyUs3`cUcpxmu5lGbKI&pxYAY`jampg=;aC(7cw^ydZoXujINc@aD?8eR0@#-yW>-vl|MEfJ!^~4UprRe6)6L`HZ7B74 z=^=VFrh|X3tJ3E{Zd@efZ0UXtV^2}U0mJn&VE-jG0%+=BW-ZOKG(J1sZ(c9xtgJ@& zn)P4(O!Ro1^zhGjoo`EN4Z<`~8Byr;fLdLs3#X{U2pXF|0U) zX1=G$q~U2^`7=EEfrH{N6Egx2zqI+0#y@JY#dYyt%J>g285U537Gqnv9qEx^T+(bu zx|$2eoOQ1}U&1o{o##kig@uYKJQ=uuAL_gBB%?4swQ2H0i9XCwi6ulQKg43l$sA02 zHS89H|H1DG1KLDP8gAjK8czRO|DzIU#J7z2e-v!=t&vJK|!I=PJ3iS=jrL+=L- zt5@i3PI*A>F50qeFtRq;Luq~lv_j1?x#uRsuozmI|Iz!?0M{Ta;N`8roGkRA#tv%o z`Vk99?*Cn9)L(zsuu)4|@&gypJerxAxom=nPEK|OKN%7K@ONpGZ?E?BjQeF!{dI=X z+4l7Gkm{N8|MhphgOSyFZT~;Lg@0Xie+!Cnas9XRpX=YMs{PCP&-KxZ=l{QhZn>(q z6uJVojxh!f=(HO12&epjEcfNAwyU4*uZ0P`+kq{D~IMD0AT}DIH})?{ccfZ(WgmzQ)_IA3mr&g zT{@Oyi==gy#Vq#_yamjTt1ysW4d}Sil3!$HEIPpkWx&JqM^@s8HQ%lKt&>kt7!*4i z?zK(sso-V?o^aQJLPi>mX_F1nYbV$6Ei*vxjvx8&sh152^3&NGc% zKqGEPEp7Ke{3pU`98I1Kg}k>$cpO6%p=5*6{oD6}#LAtlep_zcINKZBE5R;dPdlx zR{kpFsF02@-0$E$=JW~SvK+U}U%+35OriSHXlh^4z(B(6`Z=X=aI-KN3t`GO?c`)f z(9}9h>u0&Qd&Z7BJA?yShs1e{N?x0ObXNI>bn&Rfn{nPXBDGILNgU?Aka1Fh*KqWC zvqx1TV$rudHo}-x4szo?E373ka?+1{<8(t#mxPw;#=B(n;yU@JwuThF&Br)xB+X^T z)G1qNCT=q8Ox20oOofLfKptv+9Y~?)L5)RA+e%CCm>*IHF{#7Ln_FDzmmRwTCHD< z=e(?sckXqn|Hox!3E#!j%q-_&No6<-5qX=v8p(m_a8WVR-5;Wq)D>j0>fQXu;Si&e zOJHg1HNzzgg`D0My#d9yW@JD%Jm^XVMV>q39EmROBk|@;^V&K=^-Pq{cBJXUe-H!z z)oP22h5f%k1FnB7aQGiV16D2|Q1GwzTkJsm;C}}*_@{>A|G*5i8RCJMLBasV2Sl(l zltA+rD){KX^$%u1hT3>F=$*6@^EfIcocacqnBf6zuq5%z+1=Ehg|2`S9x|X)j}iO7 zej!D*kB55T&0Zj#$%G)<*-XE|t~`^tggPSty?D|6+w&0Ps(rZo3r(x=3BFPn#GVHg zmaNB!eOc1k5UUS0!m=07&&?SAIMg7lJa1Cl`vDF6lMb54tc%IKg76$jv}JnY_8v3v3;%(*l?rV z$D@;yl6LKJ5HGD(;~4WVrOf8e@3A7jeyhn_&9;FA-qHsMKx=xq9me$kdi2~(d@QK4 zH5?GwT`bFw^jCET*5~8SXm*mofv7x5EZvQXIn-m^`GFYH{pwvQE+W;n@+o^UOTg#b z65Qz9w(>xIvjgppW~!l@kEb;2sN>drkBA76l*fQnOK>z0rG3J+CC#>Wskt?O2@&^G zx_-k!?%$qXU+1dJR5WoBFyU;V(wd#1)Y9m~4#^b*encl+(f!`BEsM9QPe$*&q+S)jg*=p_qe?+^77N7SziOJFU#{yzQ|Cl+kqj|8ay)9uAawk zWj)7;q{O461uHd!4&PYg63rxN=X>Fnzc*uQ9q6_k*<0DX#R*%A^p(hy1LfWQJ(H{_ ze-_6`si9inC`TFsZ5U=?Ah0#r^J^wi{qm_T=L{*WiC9s)M!7sEKBBz^-tMAh@Kagv z@s?AF_5_xDJl&djV_9<80pTQsc{T0wBo(eGgCSHAD7I)cmwOwqh(-&|PyI9>#ATW2 zF~GG4XKfAED>`tCSMlL|T7}csVWfe3Nd5tx#M&T)Ur?s53And=yL^H+ z7sl8n-gfO_bYm~@o)Ak;3G&&L5}`+OfannO94~woC<-3Y<}Ih<-C2~Vy=74Vn>hZ2 z2$U^8AtH<}StK&VkKo&aq=6@Z1MN^fE-PC66~erB(f0;8>ZD>@Lk`z(+X0Y8FHcXf zQ{_;<>j3Z0@*Uvgt%qc-wXv*;YK^a*&#_yp+p4Q z7;Gc2j+H{z{DN$V-auW)3_2x_>BpIZsbQQGCkWf&d^`A!5$$27PQoh$dx=lBq1Z7W z{6S}}N=$0$jH7@Vt0gBi$&RGIfA&qUj^0&}z0V^LXktO(*e^XE!-k4(PIw}}d?E4p zi5o0nqYD3T#7cR?wuom07zu8hD`@3GtjKbrJeK&!_4OQ;}2eOp3kGt z1x=!&;Qf}*&<@J-`zJ1Bv>;h`sK!g_Y)S>l2G4@3=^v;75bEsI0mf5iGf8k%T>L1p z36B#Lh*R|?8e{t$khhH)P76UJy(w}ld?frOwXwkn%<-azRI^)>K90DkhQe7m4)9-K z4d&kwJY!+yaV=pj2KR)%W}g@@oT1LbZ3EM5Zoi{Bl`rrRfE@46!tK*eibEq~3z;+_ zrTSaAvbH6vntlT)?eR(0cB>5)mnOybfLvIkYZ$WX@Em}A%}!v4y)jVg?#w1|?5q@> z)CNN$*WSmaY+^*^)6=!sg7@h#|cOE~5ws7VB353Oq?%(@&lf z)Y-2rvHRceu8wA?jmT}l6vmA*tD&~#x?7t16r+9JwQZ2WD=Q#4)Z)fNTrbn_0pBc3 ztTR-xI#4#Kin=u`9C03qQwzka2p)(b+Rbi}9X(a{lnAVXkt%%gTIeFUiw3oKI*_H4 zUpC!0i8ZHGVEv-A!T9Fi5Nf)DHTZ@&8r2#|iaMB`f+|R3ST1o?6P*M%L9a17Qof1Z1i(u+3PBbq^X1)vlS{xO1jADW0%h9R(?hYouhi zQDSE7$IDY84CMu%kd2ry_WHrv<4JK(HNizOR(f0Ajpn2`dYw=j+j#D)5BFQd5FL9j zW+Y|K7JfTHtPy->`sLsfy=t$r@TF1*+>=z)!vYbXfrSBdQ|XN)iWeCCuiJbHacHRb ztzUoGMqf|?QxI=(CWxn+XY%f_UoTQ1V{i7I&8gy0L-pkNcJKDK9IkiG2w4=uja`s< z{Eew34`24@4GSYN*GP0lYt;yp>^E;}EAt;-!_qfMMAkwnbQVb{%6&``#wSI`u2b|B zB{f(KVbs3Um+A)x^(2J0X4uYE-+BS*81_PqIVv$%rw89XzcN==2xWBWwjQ1?VD+mk z`H%IXb%97+e)UNxtZU*p2xNS#9mq--55Dmq%E^iDKd-O9UwceYPj2v<#C@QIKw?Ft znLu9&-90{L@P7ohpER)Z7D#);+z<7~4mYL^>Go-D=0G!z3Z5>`QcV&V=T>N;0rikBV z6H}jHEiS-VHt9Xf-k>^qi^NJe>E(sDy*_FLhAa5i*<&MuP3z9J^x-a=Zo#FVCr-Po zelMJ6B;wBlgW(H-`VX%+J^g*zN-$XD{b^q_zhmP=>^Ed4jV+x>$N~{)r&U;Y}IYNT8Ghu)y5vbUN^z_)i3Nnb+Y&>97z~<%BYe+BsHA z`T|^GC#E%+V=#9@?u&jIARq;Kz&Z#=cQzuMXuYh5>0!qJF8Id~ga~ugHN%I^RA96( zD5ym#eo?(wK3rdWuC{(F5Wy7Y=;l-h?-E4KZqe|{^&G?-M}8!@-_y|&e$>!9iWX45 zKFSQEN4!Pl)|e4>2cO}V++y$J;YJB=XG|8a)N1qSqWFui66+~ zgj=swnTNxN+A^wH6ykMXxh5aG=ub-GA4U$@yjh$+l0o!A z0q^M<$u#^+El`cZ62)rcD_!E_srG{+TO}dEgQGLVKI-gI4&Kp^=v3x+7!faxQjbSua^z;bFB@4SE}B`~I-7 zGU!jyh)YXm<}=TT0d5S1vy;uq5Uu->zLPCYjha}W>TG2m$0%Q)?_U_tAZ%s6i2i7d zLBoLOE0|qhTSK|t1Zbp3m_hN`rgMY++72!Nil6aPywq!PG_fDIZSjwUSB#a>uD;$* z&|RvJ8X41q1RpfisvsdOG{>om+fZ7vYrZK;6wT&UgiMrNr_)#R8|!#6hwab)h8rVj z*86mGb##1-xjXU$an!hh4Aq8h5b2yRK~pCAC~7%|e3@_oxyECQm)0s6R`GxpFLa*BC=hFk{qYzu>CsCv98{ zw>o%g7=yQ^m} zp~GCQS69OAXwiO*k#{d)2qjkR&%fN=L6&uO*A@76;ykXo5IywVZas3ozTFs5@o0hT zxRfa%);&3Ubd1nTqlkbpE)h!?o9eZttVHmGZTA-7T~LUZT7ccrYqU0;xn~w0X%Ciq z3?Kd(4;nLZf)o5bzZ30RLw}D0(0Ewua98FBj^i)OL7WnE7u5|n=|g=1H1*CLnXtt z)popndHqEh5Dpbgr7z0qqSbX;372-|u&dhw`VCskgwNCcpJ!eUi_`LdyxmmrVtPou z?ZErPL$&x<8q(JQ_|F|t-d~+(dBUDq$J0xArMAUGf)-ONzZf}k{lbPYp;^bpblf`c^DE!_JSDDM9^*E+G7tZ4&3IBxe+MR?htjhv>fLfWJ;J1F!`&)S`SuGjfj!^d7=lIF z4{|J^FAdT=RJUR@8u5m+EjdeWyvgPE`VKbY|M+xS>;l1_k6am!=VKKnV6(3g4zjJRhNMCftZ20Y_& zzZa@IqOGfWhA_CwYfWKLNgSr1d!zI^8l?2ZB-9TIP4?9nF~g1i+8<;g9KUdIkSvrGgR$dG>e|B@u<)dfwY*-l94g#51%UU*_sRW2N(xG=>i^ZGW zMcl(mh44t=*cm|uN;%ZLh~DedXnaTt>F=}*;SPQISG2}Su#{mMRWF!P3qL9zQipy& zGp`UQz?bUD<@5d>B&!!377)nY2G@+f6BRGpOd7kqjEfhPtDm`(IpqRTs7UGO46~K2 zP)BFZtvZhp}3F@x``ttX;qy}(KFKvf$q2skO zUc|woYV7UKz}HKBoT?~D81FNh%rME4b8e#7tW7_;Td~nqmTYFNLldMX_s?EfxC(zexy1O!(EhOpIYZgA91)70 z5YiFpZih~kb;w&T7LCNGenISwouzd?@8_^^5ZQ+w&fIxEzWG6pja#*SH%;p(q}L^8^65{ zI(x3>z&ZCHaG-y>#qjX_&khp;|JGsRA2|50?hpUBT5#4SE?yoUb#%LnCm0x5a&j10 zDsn0q@FfW7qv8{+SdZdYOqh2{Gad^6hmrhKmi}+!_@|0hhw4xB4P~l7RqD!AfADJb zw?BPt{?!|VljEQ6A?VH+|3909py8;0?+C(0#sBABIA9rWD*oTYLPO&+JXC)mv;U<} z2q(us*cy5me=i>=$8T)y{|={Dst(Dva1gPZJX9Am1|Nz&WEuAEvQNx5kMD-P|MIX! zi)h9}ndnX1_K;K~3g~5c20%45dF=sDjDMIIz1Msm2w4-Lu0EY{cyiA_k1ACp4WCDR zBJ5Er&xP}&j9(^~D(Oz{TCb`w>9AfacRSL`z1h}$a(pYcpc*Eumh2EUBWNV7)JY-> zEw5i{U$0TedLsF8Li_Ddoor}wORGR~cz|%4F-m|-STDO_{5#9;Wl7vyVo%FEfGYtS zZ)qm(5F&YnT9V7CI`t=!xH^*=zqGw&{H*0Mql<|xlQP+}+KFw}>mB61{fa-NjSF*y zV!9A5ANL$w;60Fim{sCXItchto-4y2~xO= zguO8&FO76ywJpaSKSc87T`{{1cNOP4q2~!-Fh1BA)l!mjZ!EiH=GohBga*C3iFKen ztA|;)+^r2!Uz8sS@%%}zFvsMb`|b$Vrr6 zWTF4NhW;hQ-)AS>0(}2~rkouASP)v;{~0yq7Wkj2Dd+zq)RdFsFMa&4uqnIahWz8` z&Td&cLapBw*68}`-=Jx@qWYg7(SPf8?)uo9XzN^I<;i4Rak>PnqNX!m>EBA0k*;8) z{@*`o6lY2`5YSNYC$q$=HWvy}UJ=hK^OOLM(p%ql{Bn{|&Qa0K~+X0yCy?zBb{HE!dS*Pq%l(=A{%8Ck(tNLAnP9uZPsh# z-nqx>lF$@1c{OAdzqVOJ?5+ulMf+_63K$f9Ir@D&o#L}hJ$dOue?o$~50%>6PC_|~ z^akn`lN}vWU?((bEXm+_*Qm6v<-{D8MW8|%YLIeO8oc00H;FEo`B3Ur@GQ+ohcB^j zdRhl;*5fGH@-4P!5X*d;#2t!Q+n+TN;p_OO0diwQ-(n=g_51!4#{cGQI#SMK{@sK7 ztSDxJ{s)Oq z*7DrVATm}E@~5c!SHiJ~%#Zs=#JLjH4UMN-&x{8S*GtA8YW|YHUk6EBv^>MI;bqDm zi7#A7UHO;@e_LqPC{U|NxGVe_+|bZaHO+p~6+Ov))HM(Ll`?tfLhte+_a*Ox`;)%& zR8|m2G!=*+^7{PnL85{8TKlFw0>bumtaz?V>@aYvJ1}%FjfgeaefvEVp2rgFm%>k< z4@qWzV%)ucE%Gbzt0)!roKMiPsY@&o%>!wSK^ZT_dbw)1e2g3~9bH;~9fPkH&5uOB z5>qQZ9=YO5WVc(8XBgvo4f^g8Nnj6rD9~s3S^4}{CzZ2GP+IjuzNv-6%Ji2(54T1@yUECtf}L>p11FJmCu389y?g0> z6j=lv{QhM0TMVT)$p_}w`ico@_KJj&b>Zb_dk?lSTkq>SklpAvo$KyG7-fp+@XXQB zV+?;FkxIMsWBPy$4vyp@rKedN0DQh3(2LEWc7(wwu=Eu)NiZVQDOW{iVG;J==Yq=n zUsR>(xkrQf1CwM!QT>9Bd0Si_f^ZH}^*wrteKJ-n9+y^`f$nsjqI9WyLum?JOuU4x-|bXCAz#R%h#5lbh5B_=H~M4xP{Qvp*~=-GE>dUH#^ zx~Gs9Nm>MMWp{~lOpp6mJ;ngTPOJPD;WPkJ zN;t9|K94IiQ`=y}{dGyT^Z4w{FRvl>$@SBUXuA3)x#_*0o%>tkYxIN>eMh+OF%*~y z)00?V;B|7k^&024*BR&DKTgIr01Qv3-?;5mxJR5( z&AxHl6XXwO)-l9Y+-Eu=xZ&I^>&CDwpX=0HBd!j6oHjc5D-*_w>urDvmU;nMZeR`sDpM#Evq*Gsyx2Nt5n z;)tx)JCSr%pY@wgM{U=I_A}Mmii2(~X*H8h(;uU7mO7Kvfr2mHIrw0Ms~a+P4X?V( zb**O1?~GYbe^t9Lj3Pso;Zb}}CNqr<>xp*EF&vd0-UBz#nST`xG?t~=zufH0WQ-Rz zzR@vl((`xTqxj;f9ZX;ood?Y!HA@C*gz8mi&W1|axV~)l^eX0UoQ&+D{WcK%vAo1a_{-=0^od`C5{gYm z1|U?6caqQdSLo}`;yhCW5j1DX`w3oUw%ug-e4nus2)WSL-lP0iE4uLBVuwy_vhy}c zm6r7uA=oJbCE(jTJ-+y8HqvRwf~`an3ZV0bYrbd zlK&YAuAjeS5P7G0^T@VjLky+b!sgd&LrcDEg=&j z!NU~rDTOSURwNLIyvdtJem!fjbgM+)5Qf^Ciyqt70ez1)2i-q-69VIsY+5D&_ z#XJ{2nrj$8bq+NjmyTimTuN(B#t`FuJ-r7PN*yBaUoj59R~E@PenlT2u`ta5o)3K@Be6mHg)0In&X!US1R?>Co;;!!L5^?6C!yAi+w}^={^W5MeBJM0Z!DX~oL-^91l^7Tvm?*~*cFuklx>?`wF; z0&sk{nadoyOTxFxzLO*G^AtX~M`wD6-Sd9AK3?bSkG0YDEh{{Z?)<4vxL?GmrH#^D z?6YR|2tl*)p~$dzXrJg>m}!mmvGZ0U#6s z@ILkP#>i)M4VP_w7vTXkdK>)n*l;jK`B-9RP!xz$NE$-Y$lTRfyT7`cat2H(SD4v; z^dn4CF~TO|+mah{cKc=`61O+d+4A$36S`~8;R?41&7}=q=u|NuOUd=k!>sH!j0g68 zK^oQO16OX#jGvK^2e%En3tP(*&0!rW7Kd`3OX4p{-*>6M_N+rrS*13+bPaUws>0ZeL#bbsxJ_+j@^6w6Wo2@{R)V(b83`$QMM=?e-_2rSUC7=e9uz&EI}# z76AeA;Uf1gXdcls>wU997SO#E;I80eOh@ZP=Ez~e^mD?VTGPxY?q0h_=kD8y8ZBvz z;R2O9K~b69KZ919H8y3Sh5hwubo0#fjob6o;vL4L{?uVv=>2{Mr}94^qI1ZL7wayhW*Dng17J2^;Ht#sIAHTc?zz2Nlb08{E^f%p5v~DIsS^ zV!tgYtZiqb>mng7s;xGgl;uv&79tH)52~yCY$m~oj+EZo(I#BdDPa=NF(Mp2Sr%Np z3>C{&Aw21%J3mSj6VmR%%Hycs(Fk}kkTVHj4#6_EkFoA~@g}}l7=Q0^@s57IEV{$c zI$m#K5O?hH&9wD|89+~``2jv3O)mH=#~Sy(M`@K4F0obT)v_4Kh@YC8qQT3jGpbnnc6S#)eCl-ERXvTm_ z<3PJpx|cWCmEV?h-K#7;19Z4Y-{gbSjU}$X8ISirqdul9=mXnX`X%H#lt_uFJTwotGa5rO}yJzoLB;hSOKThVzn?k5ySwnYF8Z`{~?O`U{Nbh0lIw z_)~t^D6f-q!4yE)Ih*G<_=geSe9M$H-Y<5KaJ0gAHA($TW!Jrk4K2uiOFzWvH7qW<}a?nQbYL8evVSL%wZ zeJW##Df}P2^_E?+33?2X%yMDxe;nYYKVuaZ;I@=$6tq!CcNFbz{#GgQvt!X*KcyLa zl<3T*aSq>10`stEe((C^WV}vf;aIiL#ty?2+e~8mICCweWqx?Gtv3)>T{WBIHnn?u zU&vn9WMp4}@e_I%l;9{0=h580!j)O)P{-LgmB!nu zA&Zo6CAT~dd?dK*;}_1%R=i{hkCe%ooh!B3V_ENZd@A|WV`vbHp@$KBK0}T^WYaE; z6}bFe6Hh0f>1{={AhZoW0lO(v$oLRF;WcO>7 zj)?Lw8B-abur9oo^0~gIu$kqxK^~stRuvkyx*Y!_yGp5Xo9F*p9hte-8bHjDR9$Dy?^ zk8zhVEGS$!V^i)`SP`htS)=>AzO>WHn|}WUdD42Ac)Td6&irxJjusajIOa7&dn(-B!NeB!Q zvMpx~pjx_VOHt8^r8H}I;-(9P*dl>Gj~)_B#wy>rG&J7` z+Mn=Z*Jc0pW!ZT#v4>nt++ZP$rWEMG>b#XO?vAFa=+;2h4Egg=pGfj_^1kC_+hSLl z;)`MMt@#d-JzcS8&rsuxFvjqGXV^A>jFS@fv)>$x>W)dh)H~8wMh5H?#{LrTZ@#rW zrjj-U3k_Dh_ngIq85bpbjN`DntEj0>jqJ7dWyx{7>GkF!4?cxO9e5VI=-_WpyJ-U1 z%Sgs0w$d>w8gkOh^pp*g8P->XsVhsiL(-LRzUTU}o|+!_EIo#B$|&on&>ug4_s!{` zLZ&i0t8@57w`W&B=Rpf{J$cRtWg^rL{Xj5 ztp7le(scIAXjt+$8rj+1Eo)3)ZkqsbO-tdqrn8pWrC>ghI3`I2>lj;6k!l~B>8C{@cyFhCl)#+o_o?4ZWe0G zY&~&YpRT$3U6szv(10cTi*6%1IezT0u(G2NtTI+ttIoyx;8C zl-2&Sl6UcL)$udTL|GqnpRL9}&m?*2c8aDs;*d|zt;iP9@tjG;_qp7Dn(9^AImX{g z(&*iQ^~q+YVQv?^XqVsi&bU%Rwt%bm#cQ~JWAfud2k48`_d6iMt8h041}Dany%^l38@BmuZdPF@LV@FiNSF007T1mZD+ zl?~MVpKDX|MlCXdJMw{&=ghtEdcJ|a%$aW9qx0Om*vDutc~vTgVt?-bugfr zv^m{WR(2;+K?Vzc9epHFH9Krs9mEebY?cIv|Io;v*WDmynDczq$~E0 zFp!to?L%^p=^<^9@o%}rr>x+8HA-R3OXgn)#&H1k>QBCM2U|@S)w|DUhSS=V^Aruh z-N6-UjOxq8uHO$(ULypj{hYtG|Ljpb;1s&kjg!0pkCY_)j*EmHfm+q==gY@?5%0o} z1c9bfTZJ!vk*uO{{V*Jt;oDu#;Xnn!53TX)w7{L}UN-|Z!7jLa3e5><0SW5`_;8Xm z&_@1#=0O|#Dm>CDl%GrP3&k?ZqF_^9xDWoIlhe#_LsL@6KG?zFKKfYc7{%Ld#SXq2 z-qgf3G=Twq>jVdT%3MKKP}4eg1XMG+A6bfBA~H=O6lwow#GxoijdOK6uLS&w@A6tq z=-KW4A{1m&8Nq7lwz{AT64neorpXSaA4}5stqvfAX+Sl&VwWXpR4>7%=9MlV&`Hk} zS;`Vqzn7X>4j5+_qhh*sxvLKh z5PPX~mCl?L_d0&79(gJUkTQyv_Yfts>OA6>2V)|f|6X~Dn*B3SEs!u%sFpR---YpD z8>^WFcW#Iu=%l{Ttya@D``zc4dS+u5G>-1f2`~z&IkcZB{OAD^eoVTpB=5fTMy^r1U{i2DT!bh?Y6NKe>)wF0hAn~yKQ|1X%cE5{SHtP3c^S)byyF0d? zN9bgs0@8p_pYo5f9`TF|Ed&Fd-dSz_o=l*h{%f5Nc7tw2`}fHYee`ktcGu{DRoXj} zy87t$B2NLhyoq>j!FH`nWM8%*H>&8V%b+K6T)z?Xw>+y^Uc*|q>CY}N-I36YSe8P{ zp^OqkOXc6oAB{Lw5Sv&7P134do zrh5h83aBQrgU>Ol;FI(@hrr4ok)*Al`UAiD+JE7?hb=-0KHD5Ll7}x}C(L~zGJHOZ zvx?erRqGP$n-mJQLt8%oD-5u(rc`05bKSK)!lz%ks!p>IM(1{l8X9>dTF+EoXzR(uI&-5ArsXd&ZIJ<>bzCB_F(~X)eWZzG7pRR zkrC&FDkwLn{Ou0#zL&9lL_~LU0f6%Te1tlM=6m)Xw$RBs!-5!fv>C|x9JiH;Ly1F+ zsyzRGi&6~Wa#MYjh+kQipGanZ9`;N;ttYAuO|jSkIpgM6uSOF=^}|VVAH26!W0?(< zTO}0XGtphPJ5JCmvvf<35i0={0@bsgpr4FhB3He?1wFWm3I*t1+M{n30E434-l)wp z&1P4A4H|pR~#K z-&O)~d1v?|BlA&;hAWu-wA^EHpYU3QUQj1jc8rD%D;l49(LEGDE=WH5NUT$D z(NfK#jy)dhkKT=U1lpl!2XN;FkWw}6^>fT{8YjGtv(3@PY90LpqW5Y4{ALnVM6U~dI?yoyJOkh?It9~i4+1^%VvI|$UdsXTwQ7^?@-S@Yb{|K6~ zO>^&puu8Gj!&v!>?3!WYT<@KB)vx;CUs}qjJ9Di$t$Y7k`*%TfKcVV)a4%4>X+ix2QrAgSFNhx zj7M}I-i5~f8RmgRxs=pQM(Mb?F4!t>61`>6HLF7LtjF52qE49>KK!j52HyZeOpcj$ z3#V@N7xF=KmnbK%U6Y`nm#C)=r}1b8Qgw&<7{ech_@bO>pB8t*l=o85uyXZ}<1B~N zpxbli!qOkwDeHyqY6~=2F7vZ(q!h8DWl(m^3EW9G3jMtzwga3ezqhjQU2q|=<9(Itv`C40&re~1lGfb-2`f5XvbGk zX$$Za1jj|gO!r(DT#fjtmWOoT^_kP5yL_Z1TJ?kHsZsz1NF%AjW0cHhpSBzIxR4<$ z0w(9Yl>y|PkM=V%7@}XIL@au`}3Yr^c_0S=?0Kwsb)=-bMnlMCq_)I_Aco5N>!u-CPrl zui+d=UbVl8iTOuC>UXMsy@A{b|@ zv>FkQW0p#=oEgq!nSda-bXp;ktw4~AN(d~%4-uuF>7BpL-CG{CwUzAy53R{uYKR_0 z-;L66^@w@9>`&eSYgQ$k4{dYEHrgNaK|M78X)~cFQDOip1Us56;`hTX?FvJ2CZcC+ zK{ZHP&=x=^cQp;_;qXUSjcPas;1v7|2N9QzxuG|1^r>|vkuRj2}hs?Re@XANquOu{XFaY4R?*F1_5KAh0*eVVX`{Iwi5Urp>Z6T!5 zD<~HE`aLvNnZP?_DM#mkEI(x!w+XEA76A#pxHriU9QV&Ny=q33QkQHLphW?hz^Zm` zT`9Lr0FeD1^8HvsB?}owLR^qgim(SSfIn^caz+R+UI5$0u43;koI*&jHawBvoa)|Y zON&s>Tk*cwM;*8+z5qJy`cDC3e}$7(JGS=4WY6*&&JH^kLHOq;f5&4~=zz!;!c0*n z0}5t7cNBz=y-ZP56<9ojewPwNS2OTGWRHQ8=;?xhB~pF}L+N6HVWOrO1We0v#b1;P zSiBV12bs*G4Sm*SqY7$Jx`fpH9q0{4qpgb~F&gsm6FXBHm>2{~+#9W)CiPw1*RV?0wzEvt7uKZg>cEJNc$C4A;S+nVmS!d=^qbhbk@0qx)wxF`D`qaKOq<=XPNV% zO?<>d>~DS9F1QyQ^%iuRoWNVrqUB&PB zUS|E!K}OfG;gxo_BsR`aY*cq{xv;6d!Lu3ni}x-kD8hm4V*b7;xJy3-!=3tO>-mpITI%K(f*P0{s7sjEWokLud zt{5z>i!xNNgwYWANFhgsyxGoyuiahPu)FB9u3 zLXxwS;ed~T+-^tdE(tZnQ$OorUu5%b8Th78nSeG;oUe)f>W3fQJ&Ns-;CjP!5J_3u z#erHFQ;hev{Up_X*7mhp?x*o{iDoO_mOJ*$RVQr=15<`~*q{1dUb^*stka#_ecV@_ z*z#+-?a`HxTUj=R(ldMHf<7j8%mV9!#6DS%7sAOWBy#ujt+rTzAoAaCk>A@IY-PY2 z>e_zF(1E&_Q;GR9gpA)X4`p&Qk%g8tTiI0M{_4Q$;q*2RyJT35KgIFFW*A4k!970* zc!{_x=Towkk-OHV8mjy~&!ySFxi;QLS7}&O*9!!1!1egPFhf#9Q)d@g5PS_{W{i=X zAz3&Ym=!4OG9F+KDb*Edr346E=I_J_qN zPyW*kgmUig0D~# z;(>2K-f&wjd!Yv&nB8EvV7|n+E7BOF2dESBGG}Pb-Hx@-mfxOx@wLXP^2^w=G&GBH zpv2*~?(;1M_Cx1QMS_fPVQ4UDQ)FQl9x7>~9&nbqys0fu35rA3VPN z2qa#K6x`P-iaCph2JGqXqfTRikBGr+*S4ML;BB8Xw->G}xL}%~iLF{XWhH5(Jg-zs z>sF=7mhdtSPsaL}zbFH692Trlb0f8Ndb#c7fPmwY(&%h!L8lzCH-P_*yB_ig6%Y%Ova~mN{dWY;0Cf80pKN_Cm|PqwQ_D&$fo~)z zt&3Le()=-o0CYPmR`gA3-zac|hfUhCM2wDQ*9LgRQb9&TPxQTyS{*=VF?4qfiB z&9>R>ZbN@2ZZ%D8z3Qwkt>2{rs+0ZB3*^vyg^7_I5|8=zdB+0KYx)9siL9imV{1z{X4uuN2J{pW|%}HH|V@Y8=gTD8#-fy#WyuL|>^?+yj0b$7%)L1hpdYQJTkM_$HykRMnq zp0>ph(mG*`E!d}!r^Z}@A$27Rw2|ldlL9dvpd+(}xxZYjbt#0{+v7}DSU+&6rJ^t*Uq97|crVW((DyfLwFpN#wC0j6 z?td29)2IqxfDaW<+zAW7-71Y5Ew|fMzPSrU?~zCu4GNEsAB!d|YsuqOHD3-3Z5+sk zPW|4J^_hXZu|qbqc2KBP2_){BE_hvDzDV$Qr09&|bhk!8=1X)yu?8d~1k1{)QbaEr zpM8AzHxwcZ7JlqJQ_~yz6A4?UT=!6sfQULlOhwxPiF+2_w4kzv7?ES z7j1v%wp;Lzc4)II)t2sQe{>Ocit=41fp7@uMe8qx3ejomkw-@K{Rt1tA(L;@e7PmY z++MIQhMyVpxhC1XFA?z*vCHn$^z^ixpZun?{}SlLknKv^CqHIU*^AU04SB$i?yzv5 zCVTsD)PWrdlUwzvt{h}$PAF=Jzg3#QeO|UvKE+z<)rvMBf{d=jilKd_ofWH)V`Skm z{lcG|;+=!Rsl+aQ&{K-Cck>$^(=c?7mwJj~u#s4V2zSFTuh)9HfDqnsp0cfa<|jh-=>4%xC6H&)>*r+I+;(~?_v-e;!%w>yv(VG8HIkBlrroiRbf*r zmzqkfn8n>ygH|Jec7MTp9-yuA!R8PaxmJ+9!J!>6^lba}n2Z0YqR zKy>j1%LaGfQN|cI3nUKUtf@Z*z!z&(BMx1>Wr4_bPxH9b>|ix>ePvlp{c#GoAltl; zc^31^BQxY8!E=fKK+69ZQR=P^-QbxP`~hwxq^3n&BOQeYDaDh5Oa?<>?EvQpfQDeKVmI6!IO35KJ0vc% z-3^$2GSVJ8Spp$xq|BdEPj%K^b1KXa&IfO7ri*e{OckCh02MhX!<4KK97+wIN)n&b zn2!|=uIFE5)!Mf*AW9MBQfPLn8$l#+r*!gS8LkHX7(q+P4CM!P!CA9rBrONf_(n`= z_j_O#{+AYX&&rRNZb-4bTbX^iw@g>lB;Z=lU@=oR?PHQ+u_Iz5TrQ&_$Hg%V?s)li zwzoMdFU}}$wm9=50nW;6Yj2{jKr#07F*+6Lo(={f4(Iid8YxcpO;!^}9P;s?y?3{fg+!qfLgkEOC5{YD|mk-DhJ?j?;JzyLlw!LOw?#UR9% zdV^iBqW{WwqHkZzN7B*t-rp2PL|rSjO-z|zC%(*o9|_=Na-?U0Ff{}?3uSS$STO_D z1D94;QT?r>nN>ug*6#BshChA+@SRK))#AHX>c%m}?2!$5!6HCOu~d2=eg!=)B%q2% zj#*eYx|Lg0>3jsP^5#j!=CSPRs$3JPEuPooYjvJ}_j#`B;Q)uc1JpocMy~Bw05Gt; zEqV9@vQ*~N*xoo8Y!$7sEm2>-7jw)2tx@i_t~R9+f%&YfF`U1XVpChON30?D=3z}O zo_)%>=M5{?6|OHPE#H{O0dggYMk0cI1p;o08%K_O9=pfhnmd zcoYuYkTK|ea3BR%&*MV((fx+F`4IdRaK&(>#|+f;3f+)pM`ek9-$m(B7iou*&&z3_Vd@CqVe+G z4S@@OhAzUCO0Bsp={Xdrgtt;M0p0J9vff$Zo}1Ft!Am4eSVw`#ap~xQ?+6a$@!*Kp zZ6_NRdt~NU95WmvNy$9iVHvs45TBSgx=i2(!IXi0`g6L}_mc1tU985&cMj*DjoX(m zvF7AHiLmfebmhW+0(9&_4A8SuCFq^c`(U>hiG53aHoSSkrrpmdc+z5WLyn%vxjT$#bRuOR@sB5<>Km?#PBR3E zpRfBbpQmxZW=W#9NQ=jUJuV0EF4QY=wjMS$F{GQyJ~C^dm=h1CfBhE!jQHyXi27w{ z6sp+Xphes)6{W26WDLu;nrsly_wbx2W#DJq>8-ah@Gxc~{L-|d0ir0CWc z9>zp)nv$|D2Tyymc}c-|?kV!THLNKt$fkP4qS7i74MmwR{uBoKIhuBQfB9tePG=A2 ziki4xr8$CbmnC=?E7m{Wh48U*@kZR4&4X&`?Kkp?$8XFlS2T}N4EKIecd$6j3zw++ zh{46!S!9*r`|KnIisH)vy*B&^=%jJER3%_k_NG11G9D*(8AS@da?{1Rz;|fHt1rdG zp*YtPV26m^iIMBFcxKEHl4^N#UUJW#G--qVMrQ)xyrcfieI5-5UETYT285Pqyu;1w zSs+X48Je5Zf$Xbt}iwC^x_mXIDSRL9Z1)QzYcvs zh281*Wr$5d1Xm(rCj@*h=_~$dHM|QhIzTXSj52wVZ)H`vE}$+`Dh@UAPPm`{%V1C| z*gPRe+|8ht4{NMI8ya^r9n|ptd$xUq5vZHAi1P^b!Hn6WWwU;E=lz2r!?MgvwT^Wu ztB$i>CZxWFQysiKzbEps-@f!z2~$V)^hd6E53exA9MhHEdOx#j zSI$K_2EG!<@ew0SIQ|qF9}q}K*MM2I*hnI63quox;hu({lNOtT3c#g`81~! zJ8Jf}?{=ar-SFhD+<>^MQQa^!4ESX){61zBn8TUr}L*Da`h{gE!bfQRMxtMQ#dsRTW7 z-^`vGi?`~BeRlgCJ<76Ie){UJV;TO{rzi^XW;8|^kw_=C+Q7X>IKIG1D?pA zGjm9e22KaUced8O%APUqyMa?j1N)AW3g~^#Z069JhXqj)f`+Qg7+n|3vP~o|6iD`r(xp4BMcnFE_ zA?bsRSsU)|Wi+s+#3m+X;R{vFQ85C`vlRBgU)UjjkQbUZTX&|_re$Gs$Zd^Fj z(siHAXb8#8FTBslWd1 z4q_mIPgs{>^CSTh$GbEVrPzP5Cmr45f*b$vXi4mSAy_(D*ZRX$G3{Vrt?`PP7rs zN|+MZu-xPQyjHvDRcYokUVpY~%71d}mqUc1cK z!CGVG9<#cJI5r%7{x!Xv_e!mdPfkfKZZf*w-Jr6S9+f7HNmwTM)<{@$nQo2cHYi_u zK&B_)fHQwPx*&)YERP{PX3Ql(9!i9m1lhH-Bpst9w)+4YV{dMYGKOM2yx#NeX+;03HC{f=9pb+}%HoXDzq94-%(g;NcH# z7^iP}Q@XfM$LCTLu9WaFO=QS&NS{ql>RL0|d7dA+<~KJzR-*%5M#5A*k+5$X@UQ44 zrz4$~bJj@Jec(7m&n~ON$&)&+bQX0aEhlBg&nZN%V0h1qGifre(}umsJO)sZE3E4h zw(BICGa~Si=9s7pLgFPd`ywj;V&CN{F?J+VJ^AChC!G#nJ@AP$Wdo3hFqAhj8L5R6 zZL*+DNAu%N9ZivslT<-n1>F)2HHkEt)hHma+38{pPuq%!Pe5;Em!eRv|d0+ zK4|i~PZYg*;b2f-d@jT|c(eM3i7R~j#;5yrpsKO!n>mV^a9w?bUlW;OX#-!pFq!>DVHf-LF1N z%m>PdMF@@$e03+K_PZ2&TXVYPPqu`@Jk$#OT6D|!iH*b6m~>%X>nJGP97n|Ea_nd2g3*;f z9}V1bK)qZznWep_kf82MdDfU;9w2#}wxtj)-r@t)6eLb$0W!e)K|ZmNj+HAVQBv`t z$Wo)m&kSNC;XB-B@+V7u9-|$vrvmhsc<7t0gbep9?loJ~g=*?v!*+efkPng_*w80{ zR-8*DcLn7DL-4C8@@)3JgPKC!h09?zfmSBH-YTN z{?E4kg@N3uM6ZH4NkOzE%tqYcnI?irpr=fJ+c(w8rSQPk36f2nUFhW9qe?k91DL!(&XES-oBHsidicFfa zV%TlX>VjLc-+5N0fP|%;#RwV_>Q(YRoc=ebV!CO+v|vjakI_gp z@?a!yo0xE^iFYj&*15bL%Fw6BGd($U5y*SKxVVG0wG*TJx9g z`ZOcFHQxEhxM%iJ?5~066J>M@tCee8Lsx6`hjw&+F%~pvgsoK zFjTL$56=20FAOX}{L)Uy!reQB@2jIMc__oyF$4j&9Pe9-_*s;9o9Z2r(2*l>F3b;tv=J~}w0GM}RX^dX3{r>8J>MJG;5C2D zbw$qXL*_RSO>WW)$HtjgHN|H0Z@KvlJNYrsfKqr^s9>5xr#NOz}nhcp|I21#k@lI~9F z4(Sl2ySoIW;a{ld-gEAM$NkPf#y=bm9eb~Ly?ed$jk)H0o@dIo=!=1|iE@ZPvt$i< zrmX24`mm07z=s8AgP__i%cVAO??^oE=y#J37<;X6X`7yw4iow2({1KAFiUIrzP1B^ zXDW-lU*#?On;7~|Osc{}beeQ*$d}AW=ocj*E8{z_lz{jCyBoJUowWL6ZM&X0RjpI6 zx+G+^RE#iKwU+b2ZZ&4+{n2OWJRsSwBs!PV&Xsg${j66r84yOBi`@4@Txn%z^?qw7 zuxoCIxkzKe+nk1SqO{42^(YJ=3ZHLBP(B&q5}gLgYRb_eo}|tDY%_-PLg~+rI1aWq zJ{uNl3)oZH!WP!G@`sTvTGqHiE^8Ov?=U^6$CJLfsZNMm;B9pzMJvtF3^d-a=f|rh zhm>tre^1Flp#B%e&yN643p-1BAR~LQYukNpUmvFp z{#+(Py5Z|5eFir&^JhwH1cp!FI+zRB4*{$GCs|>GPMv+nS|m4`Ph&#=>TS>2F0I(% zixr&rD9lmIY|W@wL@_D_O~5OeqSXv?avdN|!NC-_maiC|gEYROcCA%M-UBbl;BkPr z8C)c4!IE1hZ*kr4-~E|PTUl&l{s{fYdb-~57P8$)5gZ^bXM|r*(}KHyAy#4C8nyQ` zYd-D=$>qaU_moAx+9D9M_M0Jn(%NQH)t)%GgvhMGr|T@w^lpsFpfEh=w3!w(70rz{ zw_v%Hnyp&Xr1?UeUOJ5@lTy8^Hw&jH6ta>n=>wc|o2zs0EaJkn)H^E{A^Op?#&xSn z$`ZEZKoU(2Vtr6j7nhP|QiXMllm|e(fOxxe_62H|rg=umRPyOnaO;^fk0ISwTLlxb z>Qd~*)L6UI4MdJ#=2DnFonCom$b|Or^73|c#RQNA)0tk^DXtXv^d`-fd8-&tPJU>v z$diG5dj9HHk_x7R#^I>H|3-XZYT*3+{Vq!E4{`z_AOyWW=Qp!VU#kA4k$ z%fpY_`!O{!$jSNTH?h3Yv$48%9$o>psTzv(?Xf&8&va!7x4WKk_xit#Is4F%$b9PD zT7JE^S2X&t*<(CL7a`6Wmp*5WpGpt5Ut#*4sPYK>5ymg9qx&gO__Jr*WE570e3#6k z*1}J#?56ic20j3`2%_`CZDrk_GXyRkSyE+T29g1ep}~cu{Evf57|lVuiUYC^&EG;#Pz6v3G${E zmh|E*4nnMzRtL$y2;hs;7mKOIckH;FGcNyd zn5Cl`(ih;*Go&S60k_kNOVTX})+$f^VXN914Z*h<#NnalxJXklk~C`By1}2>rq{=J zs1Dgv7aiRjyKConRM*M6^qsNe<5%a_s2T*qBYJodE0p!^!q!n;8|JKZ5XQsk1>KM2 zHF><1ai7z5u{Y?*>FSb(kq(!LtGTUwCBWxs2Xlcw7{5=)lLGiLp$re8<%#@@+wD5_)(^2Shw8=q&2B( zF+2~9u7JF5qc|pl#m$#$ui;;1X|rDCe$q+M*mPZ45r5}fGj|uH1!OZ-Xn@RLz^teO z72o9!^lKbqH@9arO|KBqd^EA(FbuB0N8IpwBN>MJ=8b<)#+7~BPk&8PCn&+J~U<%1R|^zqC_ z4)t1XKMyZj?G^AKo;x80eR4Yh44I&3#q*&`2W-BQ)OB(46ckc_xjqTTefNBc1MQe} z+2^EM-}#UYR60Pb`*c%^{xkmrMW$9h>#+Tbm@9LIHxGD`LIF!Hm7p`TFg3UdH2L{6 zd{xZ0|4J8?|27ZkP+oLntD!IrUGsf$OtESBK&1rTxzK zGDQSk%$<)I8%#iq=JpYMj~~GPvh4k(K?RaWbP_F2bjW*2DR8lMe-r>72r+HZkt>AL zDY3PsMpNC70g}@T_Jey`>i(h@fcv1-IA~m}=)PB4_byjddq|NbZ{YaA+;fa&=+@!e z)w5^>bTW>DN}8XaLnP#gzoW4neiQC<4WC%k4&$yz{g@hUy!Js8+Xu0oYb7=Vw0Lwe zcL0A6l1W&F1`~+1pVZ}TY{!EFgi~Lp+uy{=uefDkNMWbSQr6BT)|h*EdG(cFI?RS> zU6SEcklNWg@U7`NG^S8Q2LZX@Cq3}8-RWx>=QWx%xG7BDrCtIOIhgS8Pr_V?iv@-{KA;wt%q4i$aE`a&>iC0ESR0uiqdl zvDs10jf{ia9|9j1fV37F(k)03kzV6mo`k$~^ucy=N4?M%L(8OU$6}!W11u<5n%FW7 zECA_4)-CIaoMwIP$9?p1Z=*xoZXd>8AG^gu<|}ga)Oh~jNkUX`Zqk?00Xh*#!*1yu zINqXjr{|6Qz++%b+Vw}rc0nIqu`0jhQUBPn8|}^a=W}NruGGYJDlb_`)S1H*w>mCk zI~c^rtTbrNXxHHmJR%%!XF(~YYC^ecMaCFfEB?AG;9GmwKKz5oaKnaqjw-R)e%>6- zO#pDkX6wVM`a%$_k!8LKh<7vTeHGUdHN6b>_)53o)HqHD^76#gID5hhsv*lYE>oCM zuThIM-}XU)arXM@$q74+_(bLOp2EZJnQk^;W+edq*eO_@#75H_MzOU3w?hCua>bt3NlGll!KM6j9(W50? z89N-f2(hGS0RZ`2NP#}_mR7kTqZP`j)^z!N;>hQTO&}+J;8$XaWcpKz7r^pPuBVRK zp{T-chs4x|U5Wzf#{ff^5&!7oeuw>!fmvXeSVrAQKo7nv5g=(D+^cxM&v98w|G>Y= z%j&2#Q!{7Us$)YLjd+)skbru-yUa7I9xGX^SUcSUQuHWbp^ix5h+J)E^amnM$rW>+ zm#XCEG7J!%3569DA`uVF76Gr9s$lFIdpYxK=7mST3U(oid0@t*d{GW-HYn`NS9#s_ zx}5%W@o$;!rdj)#41p0q7QO~Ty}x4=yi`<7melav5HY-31U&w|@p7?yvtqX1Hqg5J zLJNrfP_Huz!-y5H;V&FMr;%oFX2DtNnEU{;9;x`S)$!a*%s9NqaIMNO9)0QlCdm6T z6YISg)zqbxl}Zw4JP_9Yh9l98@t&gR41X80QfarvtMldCa*5;?EbW>)vEvDUI&?_4 z0U30of-7j=Uq#!tYIoHi6sH%f?~KF>I+T>Q0zk=uneof{-DneepyBzTqRuh3fqHjQ z`g!c0eF+_MfS)zB$9nxu%722CO)nNes`E?(P`pd1Ta9v>Fl0KFgbUr#RDz{F=N0iZ z zSTS)o@7G8M<{$wn;rR2vu<;sH1&MaX+-YUirjAUBpJhQ@RQ&Yosn$--0}*^j3exzH z!Fb@e9OL~gmh^?zT=z#TaVm9sKV%|8zhBpOKcnUR^i}vNe~RqOF4B|s0^IREXg+aj zkf7xN8u#*V{71k2iVl*ttUtF_cKWbzIn6CadqR|4UNpcjsQ)bC zw8oJ+ca`b2V9C!5h{1hbJFBwng5*BGb@p4W`aUjK8kAW?)0nl@(<aH#x=`}MUpdF4E$EQ84tJp*vefArjs6iI}Be~vjt{;O_3*28mN6C+H$~(Uqt^44#LpqpMu>`sKs#rurPH)Mn*Qw zHhS+^!&iKku-QC4zBqF>2>WM+Vp!M%0D6tucO?U*9G-_s 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 S&vVgA3mzVJ7i;d^|firQX1! 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 I)3{_X|vOpW}69n+Y{# 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

4RUT#uGc`f|wIHtUm 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 Date: Thu, 29 Aug 2024 14:13:37 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20AiVectorFactory=20=E8=B4=9F?= =?UTF-8?q?=E8=B4=A3=E7=AE=A1=E7=90=86=E4=B8=8D=E5=90=8C=20EmbeddingModel?= =?UTF-8?q?=20=E5=AF=B9=E5=BA=94=E7=9A=84=20VectorStore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dataobject/knowledge/AiKnowledgeDO.java | 2 +- .../knowledge/AiKnowledgeSegmentDO.java | 8 ++- .../AiKnowledgeDocumentServiceImpl.java | 37 ++++++++---- .../service/knowledge/AiKnowledgeService.java | 14 ++++- .../knowledge/AiKnowledgeServiceImpl.java | 9 +-- .../ai/service/model/AiApiKeyService.java | 18 ++++++ .../ai/service/model/AiApiKeyServiceImpl.java | 19 ++++++ .../ai/config/YudaoAiAutoConfiguration.java | 58 ++++++++++--------- .../ai/core/factory/AiModelFactory.java | 13 +++++ .../ai/core/factory/AiModelFactoryImpl.java | 32 +++++++++- .../ai/core/factory/AiVectorFactory.java | 27 +++++++++ .../ai/core/factory/AiVectorFactoryImpl.java | 51 ++++++++++++++++ .../RedisVectorStoreAutoConfiguration.java | 3 +- 13 files changed, 239 insertions(+), 52 deletions(-) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java index 89e7486dc..756d8cdb3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java +++ b/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 { @TableField(typeHandler = JacksonTypeHandler.class) private List visibilityPermissions; /** - * 嵌入模型编号,高质量模式时维护 + * 嵌入模型编号 */ private Long modelId; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index 2032bfd5e..6d5da06ea 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/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 { * 向量库的编号 */ private String vectorId; - // TODO @新:knowledgeId 加个,会方便点 + /** + * 知识库编号 + * 关联 {@link AiKnowledgeDO#getId()} + */ + private Long knowledgeId; /** * 文档编号 - * + *

* 关联 {@link AiKnowledgeDocumentDO#getId()} */ private Long documentId; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 2af8b9d90..9be3dd1af 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/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; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.tokenizer.TokenCountEstimator; import org.springframework.ai.transformer.splitter.TokenTextSplitter; -import org.springframework.ai.vectorstore.RedisVectorStore; +import org.springframework.ai.vectorstore.VectorStore; import org.springframework.core.io.ByteArrayResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Objects; /** * AI 知识库-文档 Service 实现类 @@ -42,9 +45,14 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Resource private TokenTextSplitter tokenTextSplitter; @Resource - private TokenCountEstimator TOKEN_COUNT_ESTIMATOR; + private TokenCountEstimator tokenCountEstimator; + @Resource - private RedisVectorStore vectorStore; + private AiApiKeyService apiKeyService; + @Resource + private AiKnowledgeService knowledgeService; + @Resource + private AiChatModelService chatModelService; // TODO 芋艿:需要 review 下,代码格式; @@ -53,18 +61,18 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { // 1.1 下载文档 String url = createReqVO.getUrl(); - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url)); // 1.2 加载文档 + TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url)); List documents = loader.get(); Document document = CollUtil.getFirst(documents); - // TODO @xin:是不是不存在,就抛出异常呀;厚泽 return 呀; - Integer tokens = Objects.nonNull(document) ? TOKEN_COUNT_ESTIMATOR.estimate(document.getContent()) : 0; - Integer wordCount = Objects.nonNull(document) ? document.getContent().length() : 0; + String content = document.getContent(); + Integer tokens = tokenCountEstimator.estimate(content); + Integer wordCount = content.length(); + // 1.3 文档记录入库 AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) .setTokens(tokens).setWordCount(wordCount) .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); - // 1.2 文档记录入库 documentMapper.insert(documentDO); Long documentId = documentDO.getId(); if (CollUtil.isEmpty(documents)) { @@ -75,11 +83,16 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic List segments = tokenTextSplitter.apply(documents); // 2.2 分段内容入库 List segmentDOList = CollectionUtils.convertList(segments, - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) - .setTokens(TOKEN_COUNT_ESTIMATOR.estimate(segment.getContent())).setWordCount(segment.getContent().length()) + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()) + .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); segmentMapper.insertBatch(segmentDOList); - // 3 向量化并存储 + + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); + AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); + // 3.1 获取向量存储实例 + VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); + // 3.2 向量化并存储 vectorStore.add(segments); return documentId; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java index 91b0c9b3e..bf7e8886a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.ai.service.knowledge; + import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; /** * AI 知识库-基础信息 Service 接口 @@ -13,7 +15,7 @@ public interface AiKnowledgeService { * 创建【我的】知识库 * * @param createReqVO 创建信息 - * @param userId 用户编号 + * @param userId 用户编号 * @return 编号 */ Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId); @@ -23,8 +25,16 @@ public interface AiKnowledgeService { * 创建【我的】知识库 * * @param updateReqVO 更新信息 - * @param userId 用户编号 + * @param userId 用户编号 */ void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId); + + /** + * 校验知识库是否存在 + * + * @param id 记录编号 + */ + AiKnowledgeDO validateKnowledgeExists(Long id); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 5889bcef7..70442936e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/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 { private AiChatModelService chatModalService; @Resource - private AiKnowledgeMapper knowledgeBaseMapper; + private AiKnowledgeMapper knowledgeMapper; @Override public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { @@ -39,7 +39,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { // 2. 插入知识库 AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); - knowledgeBaseMapper.insert(knowledgeBase); + knowledgeMapper.insert(knowledgeBase); return knowledgeBase.getId(); } @@ -56,11 +56,12 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { // 2. 更新知识库 AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); updateDO.setModel(model.getModel()); - knowledgeBaseMapper.updateById(updateDO); + knowledgeMapper.updateById(updateDO); } + @Override public AiKnowledgeDO validateKnowledgeExists(Long id) { - AiKnowledgeDO knowledgeBase = knowledgeBaseMapper.selectById(id); + AiKnowledgeDO knowledgeBase = knowledgeMapper.selectById(id); if (knowledgeBase == null) { throw exception(KNOWLEDGE_NOT_EXISTS); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java index fe8fdd194..603325da6 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java +++ b/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 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; import jakarta.validation.Valid; import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; +import org.springframework.ai.vectorstore.VectorStore; import java.util.List; @@ -83,6 +85,14 @@ public interface AiApiKeyService { */ ChatModel getChatModel(Long id); + /** + * 获得 EmbeddingModel 对象 + * + * @param id 编号 + * @return EmbeddingModel 对象 + */ + EmbeddingModel getEmbeddingModel(Long id); + /** * 获得 ImageModel 对象 * @@ -111,4 +121,12 @@ public interface AiApiKeyService { */ SunoApi getSunoApi(); + /** + * 获得 vector 对象 + * + * @param id 编号 + * @return VectorStore 对象 + */ + VectorStore getOrCreateVectorStore(Long id); + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index 590b10a4c..d3e9b7cfb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/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; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactory; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -13,7 +14,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; import jakarta.annotation.Resource; import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; +import org.springframework.ai.vectorstore.VectorStore; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -36,6 +39,8 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @Resource private AiModelFactory modelFactory; + @Resource + private AiVectorFactory vectorFactory; @Override public Long createApiKey(AiApiKeySaveReqVO createReqVO) { @@ -104,6 +109,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl()); } + @Override + public EmbeddingModel getEmbeddingModel(Long id) { + AiApiKeyDO apiKey = validateApiKey(id); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl()); + } + @Override public ImageModel getImageModel(AiPlatformEnum platform) { AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); @@ -132,4 +144,11 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { } return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); } + + @Override + public VectorStore getOrCreateVectorStore(Long id) { + AiApiKeyDO apiKey = validateApiKey(id); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); + } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 8566a0941..50eacd00e 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/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; 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.AiVectorFactory; +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactoryImpl; 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,22 +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 自动配置 @@ -43,6 +38,12 @@ public class YudaoAiAutoConfiguration { return new AiModelFactoryImpl(); } + @Bean + public AiVectorFactory aiVectorFactory() { + return new AiVectorFactoryImpl(); + } + + // ========== 各种 AI Client 创建 ========== @Bean @@ -85,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); +// } /** - * TODO @xin 抽离出去,根据具体模型走 + * 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 芋艿:临时注释,避免无法启动 diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java index b6d7b3dd0..6f628ea4d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java +++ b/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; 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; /** @@ -25,6 +26,18 @@ public interface AiModelFactory { */ ChatModel getOrCreateChatModel(AiPlatformEnum platform, 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); + /** * 基于默认配置,获得 ChatModel 对象 * diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index c9b04dc1e..5c3524e66 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/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; 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; @@ -97,6 +99,21 @@ public class AiModelFactoryImpl implements AiModelFactory { }); } + @Override + public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) { + String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url); + return Singleton.get(cacheKey, (Func0) () -> { + // TODO @xin 先测试一个 + switch (platform) { + case TONG_YI: + return buildTongYiEmbeddingModel(apiKey); + default: + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); + } + }); + } + + @Override public ChatModel getDefaultChatModel(AiPlatformEnum platform) { //noinspection EnhancedSwitchMigration @@ -239,7 +256,7 @@ public class AiModelFactoryImpl implements AiModelFactory { /** * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel( - * ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} + *ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} */ private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); @@ -249,7 +266,7 @@ public class AiModelFactoryImpl implements AiModelFactory { /** * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel( - * ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} + *ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} */ private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); @@ -315,4 +332,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); + } + } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java new file mode 100644 index 000000000..5e43c9bab --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java @@ -0,0 +1,27 @@ +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; + +/** + * AI Vector 模型工厂的接口类 + * @author xiaoxin + */ +public interface AiVectorFactory { + + + /** + * 基于指定配置,获得 VectorStore 对象 + *

+ * 如果不存在,则进行创建 + * + * @param embeddingModel 嵌入模型 + * @param platform 平台 + * @param apiKey API KEY + * @param url API URL + * @return VectorStore 对象 + */ + VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url); + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java new file mode 100644 index 000000000..d16b595f3 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java @@ -0,0 +1,51 @@ +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 AiVectorFactoryImpl implements AiVectorFactory { + + @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) () -> { + // TODO 芋艿 @xin 这两个配置取哪好呢 + // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上 + 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, "_")); + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java index 615b05f78..a72d50c4a 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java +++ b/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; 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; @@ -38,7 +39,7 @@ import redis.clients.jedis.JedisPooled; */ @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 { From abf5a22cd09304e106ba733eb5e615293a021afa Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 29 Aug 2024 14:16:23 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E9=87=8D=E5=91=BD=E5=90=8D=20Ai?= =?UTF-8?q?VectorFactory=20->=20AiVectorStoreFactory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/service/model/AiApiKeyServiceImpl.java | 4 ++-- .../framework/ai/config/YudaoAiAutoConfiguration.java | 8 ++++---- .../{AiVectorFactory.java => AiVectorStoreFactory.java} | 2 +- ...ctorFactoryImpl.java => AiVectorStoreFactoryImpl.java} | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/{AiVectorFactory.java => AiVectorStoreFactory.java} (94%) rename yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/{AiVectorFactoryImpl.java => AiVectorStoreFactoryImpl.java} (96%) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index d3e9b7cfb..25eec75b7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.service.model; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; -import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactory; +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -40,7 +40,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @Resource private AiModelFactory modelFactory; @Resource - private AiVectorFactory vectorFactory; + private AiVectorStoreFactory vectorFactory; @Override public Long createApiKey(AiApiKeySaveReqVO createReqVO) { diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 50eacd00e..79a1f345b 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -2,8 +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.AiVectorFactory; -import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactoryImpl; +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; @@ -39,8 +39,8 @@ public class YudaoAiAutoConfiguration { } @Bean - public AiVectorFactory aiVectorFactory() { - return new AiVectorFactoryImpl(); + public AiVectorStoreFactory aiVectorFactory() { + return new AiVectorStoreFactoryImpl(); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java similarity index 94% rename from yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java rename to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java index 5e43c9bab..3e138cbd3 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactory.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java @@ -8,7 +8,7 @@ import org.springframework.ai.vectorstore.VectorStore; * AI Vector 模型工厂的接口类 * @author xiaoxin */ -public interface AiVectorFactory { +public interface AiVectorStoreFactory { /** diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java similarity index 96% rename from yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java rename to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java index d16b595f3..b3291c6b7 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java @@ -18,7 +18,7 @@ import redis.clients.jedis.JedisPooled; * * @author xiaoxin */ -public class AiVectorFactoryImpl implements AiVectorFactory { +public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory { @Override public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) { From 7d53a6dd527d0bc381f12abe51977679320d99c3 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 29 Aug 2024 15:56:26 +0800 Subject: [PATCH 09/14] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E5=90=91=E9=87=8F=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E8=A1=A5=E5=85=85=20knowledgeId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dataobject/knowledge/AiKnowledgeSegmentDO.java | 2 ++ .../knowledge/AiKnowledgeDocumentServiceImpl.java | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index 6d5da06ea..fc758ce31 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -15,6 +15,8 @@ import lombok.Data; @Data public class AiKnowledgeSegmentDO extends BaseDO { + public static final String FIELD_KNOWLEDGE_ID = "knowledgeId"; + /** * 编号 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 9be3dd1af..bcfb64c55 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -27,6 +27,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Map; /** * AI 知识库-文档 Service 实现类 @@ -83,16 +84,22 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic List segments = tokenTextSplitter.apply(documents); // 2.2 分段内容入库 List segmentDOList = CollectionUtils.convertList(segments, - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()) + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); segmentMapper.insertBatch(segmentDOList); + // 3.1 document 补充源数据 + segments.forEach(segment -> { + Map metadata = segment.getMetadata(); + metadata.put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()); + }); + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); - // 3.1 获取向量存储实例 + // 3.2 获取向量存储实例 VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); - // 3.2 向量化并存储 + // 3.3 向量化并存储 vectorStore.add(segments); return documentId; } From 4208339d4d3206f7f011582100ea295d01f54491 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 29 Aug 2024 16:03:20 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?= =?UTF-8?q?:=20=E5=88=A0=E9=99=A4=20AiChatRoleEnum#role?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/enums/AiChatRoleEnum.java | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java index 811189efe..029961bf3 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java @@ -1,11 +1,8 @@ package cn.iocoder.yudao.module.ai.enums; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; -import java.util.Arrays; - /** * AI 内置聊天角色的枚举 * @@ -13,16 +10,16 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum AiChatRoleEnum implements IntArrayValuable { +public enum AiChatRoleEnum { - AI_WRITE_ROLE(1, "写作助手", """ + AI_WRITE_ROLE("写作助手", """ 你是一位出色的写作助手,能够帮助用户生成创意和灵感,并在用户提供场景和提示词时生成对应的回复。你的任务包括: 1. 撰写建议:根据用户提供的主题或问题,提供详细的写作建议、情节发展方向、角色设定以及背景描写,确保内容结构清晰、有逻辑。 2. 回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。 """), - AI_MIND_MAP_ROLE(2, "导图助手", """ + AI_MIND_MAP_ROLE("导图助手", """ 你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子: # Geek-AI 助手 ## 完整的开源系统 @@ -39,11 +36,6 @@ public enum AiChatRoleEnum implements IntArrayValuable { 除此之外不要任何解释性语句。 """); - // TODO @xin:这个 role 是不是删除掉好点哈。= = 目前主要是没做角色枚举。这里多了 role 反倒容易误解哈 - /** - * 角色 - */ - private final Integer role; /** * 角色名 */ @@ -54,11 +46,4 @@ public enum AiChatRoleEnum implements IntArrayValuable { */ private final String systemMessage; - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiChatRoleEnum::getRole).toArray(); - - @Override - public int[] array() { - return ARRAYS; - } - } From 96fb953730433d999486bac28594c0b0300b4418 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Fri, 30 Aug 2024 15:10:53 +0800 Subject: [PATCH 11/14] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E5=A2=9E=E5=8A=A0=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=AE=A1=E7=90=86=E7=B1=BB=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 4 +- .../knowledge/AiKnowledgeController.java | 24 +++++++-- .../AiKnowledgeDocumentController.java | 54 +++++++++++++++++++ .../AiKnowledgeSegmentController.java | 51 ++++++++++++++++++ .../AiKnowledgeDocumentPageReqVO.java | 13 +++++ .../document/AiKnowledgeDocumentRespVO.java | 37 +++++++++++++ .../AiKnowledgeDocumentUpdateReqVO.java | 23 ++++++++ .../AiKnowledgeCreateMyReqVO.java | 2 +- .../AiKnowledgeDocumentCreateReqVO.java | 8 ++- .../vo/knowledge/AiKnowledgeRespVO.java | 25 +++++++++ .../AiKnowledgeUpdateMyReqVO.java | 4 +- .../segment/AiKnowledgeSegmentPageReqVO.java | 21 ++++++++ .../vo/segment/AiKnowledgeSegmentRespVO.java | 33 ++++++++++++ .../AiKnowledgeSegmentUpdateReqVO.java | 17 ++++++ .../AiKnowledgeSegmentUpdateStatusReqVO.java | 17 ++++++ .../knowledge/AiKnowledgeDocumentMapper.java | 10 ++++ .../mysql/knowledge/AiKnowledgeMapper.java | 11 ++++ .../knowledge/AiKnowledgeSegmentMapper.java | 11 ++++ .../knowledge/AiKnowledgeDocumentService.java | 21 +++++++- .../AiKnowledgeDocumentServiceImpl.java | 34 +++++++++++- .../knowledge/AiKnowledgeSegmentService.java | 27 ++++++++++ .../AiKnowledgeSegmentServiceImpl.java | 27 ++++++++++ .../service/knowledge/AiKnowledgeService.java | 14 ++++- .../knowledge/AiKnowledgeServiceImpl.java | 11 +++- 24 files changed, 479 insertions(+), 20 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/{ => knowledge}/AiKnowledgeCreateMyReqVO.java (98%) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/{ => knowledge}/AiKnowledgeDocumentCreateReqVO.java (90%) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/{ => knowledge}/AiKnowledgeUpdateMyReqVO.java (94%) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 714d49adb..c3158a1aa 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; /** * AI 错误码枚举类 - * + *

* ai 系统,使用 1-040-000-000 段 */ public interface ErrorCodeConstants { @@ -55,5 +55,7 @@ public interface ErrorCodeConstants { // ========== API 知识库 1-022-008-000 ========== ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!"); + ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!"); + ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!"); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index 9eae6b70c..a7b49b413 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -1,13 +1,19 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -19,19 +25,27 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti public class AiKnowledgeController { @Resource - private AiKnowledgeService knowledgeBaseService; + private AiKnowledgeService knowledgeService; + + @GetMapping("/my-page") + @Operation(summary = "获取【我的】知识库分页") + public CommonResult> getKnowledgePageMy(@Validated PageParam pageReqVO) { + PageResult pageResult = knowledgeService.getKnowledgePageMy(getLoginUserId(), pageReqVO); + return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); + } + @PostMapping("/create-my") @Operation(summary = "创建【我的】知识库") public CommonResult createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) { - return success(knowledgeBaseService.createKnowledgeMy(createReqVO, getLoginUserId())); + return success(knowledgeService.createKnowledgeMy(createReqVO, getLoginUserId())); } @PutMapping("/update-my") @Operation(summary = "更新【我的】知识库") public CommonResult updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) { - knowledgeBaseService.updateKnowledgeMy(updateReqVO, getLoginUserId()); + knowledgeService.updateKnowledgeMy(updateReqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java new file mode 100644 index 000000000..58a53a19c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - AI 知识库-文档") +@RestController +@RequestMapping("/ai/knowledge/document") +public class AiKnowledgeDocumentController { + + @Resource + private AiKnowledgeDocumentService documentService; + + + @PostMapping("/create") + @Operation(summary = "新建文档") + public CommonResult createKnowledgeDocument(@Validated AiKnowledgeDocumentCreateReqVO reqVO) { + Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); + return success(knowledgeDocumentId); + } + + + @GetMapping("/page") + @Operation(summary = "获取文档分页") + public CommonResult> getKnowledgeDocumentPageMy(@Validated AiKnowledgeDocumentPageReqVO pageReqVO) { + PageResult pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); + } + + + @PutMapping("/update") + @Operation(summary = "更新文档") + public CommonResult updateKnowledgeDocument(@Validated @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { + documentService.updateKnowledgeDocument(reqVO); + return success(true); + } + + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java new file mode 100644 index 000000000..a19f38eb7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - AI 知识库-段落") +@RestController +@RequestMapping("/ai/knowledge/segment") +public class AiKnowledgeSegmentController { + + @Resource + private AiKnowledgeSegmentService segmentService; + + @GetMapping("/page") + @Operation(summary = "获取段落分页") + public CommonResult> getKnowledgeSegmentPageMy(@Validated AiKnowledgeSegmentPageReqVO pageReqVO) { + PageResult pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); + } + + + @PutMapping("/update") + @Operation(summary = "更新段落内容") + public CommonResult updateKnowledgeSegment(@Validated @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) { + segmentService.updateKnowledgeSegment(reqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "启禁用段落内容") + public CommonResult updateKnowledgeSegmentStatus(@Validated @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { + segmentService.updateKnowledgeSegmentStatus(reqVO); + return success(true); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java new file mode 100644 index 000000000..c1e7947f5 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库-文档 分页 Request VO") +@Data +public class AiKnowledgeDocumentPageReqVO extends PageParam { + + @Schema(description = "文档名称", example = "Java 开发手册") + private String name; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java new file mode 100644 index 000000000..94a022363 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库-文档 Response VO") +@Data +public class AiKnowledgeDocumentRespVO extends PageParam { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long id; + + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long knowledgeId; + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") + private String name; + + @Schema(description = "内容", example = "Java 是一门面向对象的语言.....") + private String content; + + @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") + private String url; + + @Schema(description = "token 数量", example = "1024") + private Integer tokens; + + @Schema(description = "字符数", example = "1008") + private Integer wordCount; + + @Schema(description = "切片状态", example = "1") + private Integer sliceStatus; + + @Schema(description = "文档状态", example = "1") + private Integer status; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java new file mode 100644 index 000000000..6fb42c774 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + + +@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO") +@Data +public class AiKnowledgeDocumentUpdateReqVO { + + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") + @NotNull(message = "编号不能为空") + private Long id; + + @Schema(description = "是否启用", example = "1") + private Integer status; + + @Schema(description = "名称", example = "Java 开发手册") + private String name; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java similarity index 98% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java index ac94a4c15..44a5e87ee 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo; +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java similarity index 90% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java index 10ad036b2..660c573ba 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo; +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -6,10 +6,8 @@ import jakarta.validation.constraints.NotNull; import lombok.Data; import org.hibernate.validator.constraints.URL; -/** - * @author xiaoxin - */ -@Schema(description = "管理后台 - AI 知识库【创建文档】 Request VO") + +@Schema(description = "管理后台 - AI 知识库创建【文档】 Request VO") @Data public class AiKnowledgeDocumentCreateReqVO { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java new file mode 100644 index 000000000..2eb08717e --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - AI 知识库 Response VO") +@Data +public class AiKnowledgeRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long id; + + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") + private String name; + + @Schema(description = "知识库描述", example = "ruoyi-vue-pro 用户指南") + private String description; + + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") + private Long modelId; + + @Schema(description = "模型标识", example = "qwen-72b-chat") + private String model; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java similarity index 94% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java index e1f6a31af..987c9bf4a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo; +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -7,7 +7,7 @@ import lombok.Data; import java.util.List; -@Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO") +@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO") @Data public class AiKnowledgeUpdateMyReqVO { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java new file mode 100644 index 000000000..125cb80b1 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库分页 Request VO") +@Data +public class AiKnowledgeSegmentPageReqVO extends PageParam { + + + @Schema(description = "分段状态", example = "1") + private Integer status; + + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer documentId; + + @Schema(description = "分段内容关键字", example = "Java 开发") + private String keyword; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java new file mode 100644 index 000000000..d8411618b --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库-文档 Response VO") +@Data +public class AiKnowledgeSegmentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long id; + + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long documentId; + + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long knowledgeId; + + @Schema(description = "向量库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1858496a-1dde-4edf-a43e-0aed08f37f8c") + private String vectorId; + + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") + private String content; + + @Schema(description = "token 数量", example = "1024") + private Integer tokens; + + @Schema(description = "字符数", example = "1008") + private Integer wordCount; + + @Schema(description = "文档状态", example = "1") + private Integer status; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java new file mode 100644 index 000000000..23b1461e2 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO") +@Data +public class AiKnowledgeSegmentUpdateReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long id; + + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") + private String content; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java new file mode 100644 index 000000000..409ce0146 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO") +@Data +public class AiKnowledgeSegmentUpdateStatusReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long id; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java index af55f545a..7692d1ced 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import org.apache.ibatis.annotations.Mapper; @@ -11,4 +14,11 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface AiKnowledgeDocumentMapper extends BaseMapperX { + + default PageResult selectPage(AiKnowledgeDocumentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName()) + .orderByDesc(AiKnowledgeDocumentDO::getId)); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java index 41e71ccad..2bf23411a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import org.apache.ibatis.annotations.Mapper; @@ -11,4 +15,11 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface AiKnowledgeMapper extends BaseMapperX { + + default PageResult selectPageByMy(Long userId, PageParam pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eq(AiKnowledgeDO::getUserId, userId) + .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) + .orderByDesc(AiKnowledgeDO::getId)); + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index 5043ee0ca..912d18cbc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import org.apache.ibatis.annotations.Mapper; @@ -11,4 +14,12 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface AiKnowledgeSegmentMapper extends BaseMapperX { + + default PageResult selectPage(AiKnowledgeSegmentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId()) + .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus()) + .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword()) + .orderByDesc(AiKnowledgeSegmentDO::getId)); + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 82c4f7b91..3de0ac01d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.ai.service.knowledge; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; /** * AI 知识库-文档 Service 接口 @@ -17,4 +21,19 @@ public interface AiKnowledgeDocumentService { */ Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); + + /** + * 获取文档分页 + * + * @param pageReqVO 分页参数 + * @return 文档分页 + */ + PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO); + + /** + * 更新文档 + * + * @param reqVO 更新信息 + */ + void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index bcfb64c55..3758c3bfd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -3,9 +3,12 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.collection.CollUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; @@ -29,6 +32,9 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS; + /** * AI 知识库-文档 Service 实现类 * @@ -104,6 +110,32 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return documentId; } + @Override + public PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) { + return documentMapper.selectPage(pageReqVO); + } + + @Override + public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { + validateKnowledgeDocumentExists(reqVO.getId()); + AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); + documentMapper.updateById(document); + } + + /** + * 校验文档是否存在 + * + * @param id 文档编号 + * @return 文档信息 + */ + private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { + AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id); + if (knowledgeDocument == null) { + throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS); + } + return knowledgeDocument; + } + private org.springframework.core.io.Resource downloadFile(String url) { try { byte[] bytes = HttpUtil.downloadBytes(url); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 7caea9ff4..22f634907 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -1,5 +1,11 @@ package cn.iocoder.yudao.module.ai.service.knowledge; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; + /** * AI 知识库分片 Service 接口 * @@ -7,4 +13,25 @@ package cn.iocoder.yudao.module.ai.service.knowledge; */ public interface AiKnowledgeSegmentService { + /** + * 获取段落分页 + * + * @param pageReqVO 分页查询 + * @return 文档分页 + */ + PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO); + + /** + * 更新段落内容 + * + * @param reqVO 更新内容 + */ + void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO); + + /** + * 更新状态 + * + * @param reqVO 更新内容 + */ + void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 226c5f8fb..7f751b176 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -1,5 +1,13 @@ package cn.iocoder.yudao.module.ai.service.knowledge; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -12,4 +20,23 @@ import org.springframework.stereotype.Service; @Slf4j public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { + @Resource + private AiKnowledgeSegmentMapper segmentMapper; + + @Override + public PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { + return segmentMapper.selectPage(pageReqVO); + } + + @Override + public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) { + segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class)); + // TODO @xin 重新向量化 + } + + @Override + public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) { + segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class)); + // TODO @xin 1.禁用删除向量 2.启用重新向量化 + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java index bf7e8886a..9f43c5328 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.ai.service.knowledge; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; /** @@ -37,4 +39,12 @@ public interface AiKnowledgeService { */ AiKnowledgeDO validateKnowledgeExists(Long id); + /** + * 获得【我的】知识库分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页查询 + * @return 知识库分页 + */ + PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 70442936e..1948bb00e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; @@ -68,4 +70,9 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { return knowledgeBase; } + @Override + public PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO) { + return knowledgeMapper.selectPageByMy(userId, pageReqVO); + } + } From e30795fb69ccbb1f77271abafa4b8bc75e35d3d7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 31 Aug 2024 14:18:35 +0800 Subject: [PATCH 12/14] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91AI=20=E5=A4=A7=E6=A8=A1=E5=9E=8B=EF=BC=9A?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 1 - .../knowledge/AiKnowledgeController.java | 4 +- .../AiKnowledgeDocumentController.java | 15 +++----- .../AiKnowledgeSegmentController.java | 12 +++--- .../AiKnowledgeDocumentPageReqVO.java | 3 +- .../document/AiKnowledgeDocumentRespVO.java | 11 +++--- .../AiKnowledgeDocumentUpdateReqVO.java | 3 ++ .../AiKnowledgeDocumentCreateReqVO.java | 2 +- .../vo/knowledge/AiKnowledgeRespVO.java | 5 ++- .../segment/AiKnowledgeSegmentPageReqVO.java | 5 +-- .../vo/segment/AiKnowledgeSegmentRespVO.java | 7 ++-- .../AiKnowledgeSegmentUpdateStatusReqVO.java | 7 +++- .../knowledge/AiKnowledgeSegmentDO.java | 5 ++- .../AiKnowledgeDocumentServiceImpl.java | 38 ++++++++----------- .../knowledge/AiKnowledgeSegmentService.java | 7 ++-- .../ai/service/model/AiApiKeyService.java | 18 ++++----- .../ai/service/model/AiApiKeyServiceImpl.java | 15 ++++---- .../ai/core/factory/AiModelFactory.java | 24 ++++++------ .../ai/core/factory/AiModelFactoryImpl.java | 35 ++++++++--------- .../ai/core/factory/AiVectorStoreFactory.java | 3 +- .../factory/AiVectorStoreFactoryImpl.java | 3 +- 21 files changed, 111 insertions(+), 112 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index c3158a1aa..e1dd1a956 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -52,7 +52,6 @@ public interface ErrorCodeConstants { // ========== API 思维导图 1-040-008-000 ========== ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!"); - // ========== API 知识库 1-022-008-000 ========== ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!"); ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!"); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index a7b49b413..dc2c8e3ae 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -22,6 +22,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti @Tag(name = "管理后台 - AI 知识库") @RestController @RequestMapping("/ai/knowledge") +@Validated public class AiKnowledgeController { @Resource @@ -34,14 +35,12 @@ public class AiKnowledgeController { return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); } - @PostMapping("/create-my") @Operation(summary = "创建【我的】知识库") public CommonResult createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) { return success(knowledgeService.createKnowledgeMy(createReqVO, getLoginUserId())); } - @PutMapping("/update-my") @Operation(summary = "更新【我的】知识库") public CommonResult updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) { @@ -49,5 +48,4 @@ public class AiKnowledgeController { return success(true); } - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index 58a53a19c..d86210556 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -12,43 +12,40 @@ import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - AI 知识库-文档") +@Tag(name = "管理后台 - AI 知识库文档") @RestController @RequestMapping("/ai/knowledge/document") +@Validated public class AiKnowledgeDocumentController { @Resource private AiKnowledgeDocumentService documentService; - @PostMapping("/create") @Operation(summary = "新建文档") - public CommonResult createKnowledgeDocument(@Validated AiKnowledgeDocumentCreateReqVO reqVO) { + public CommonResult createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) { Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); return success(knowledgeDocumentId); } - @GetMapping("/page") @Operation(summary = "获取文档分页") - public CommonResult> getKnowledgeDocumentPageMy(@Validated AiKnowledgeDocumentPageReqVO pageReqVO) { + public CommonResult> getKnowledgeDocumentPageMy(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) { PageResult pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); } - @PutMapping("/update") @Operation(summary = "更新文档") - public CommonResult updateKnowledgeDocument(@Validated @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { + public CommonResult updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { documentService.updateKnowledgeDocument(reqVO); return success(true); } - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index a19f38eb7..a0d0952a8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -12,15 +12,16 @@ import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - AI 知识库-段落") +@Tag(name = "管理后台 - AI 知识库段落") @RestController @RequestMapping("/ai/knowledge/segment") +@Validated public class AiKnowledgeSegmentController { @Resource @@ -28,22 +29,21 @@ public class AiKnowledgeSegmentController { @GetMapping("/page") @Operation(summary = "获取段落分页") - public CommonResult> getKnowledgeSegmentPageMy(@Validated AiKnowledgeSegmentPageReqVO pageReqVO) { + public CommonResult> getKnowledgeSegmentPageMy(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) { PageResult pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); } - @PutMapping("/update") @Operation(summary = "更新段落内容") - public CommonResult updateKnowledgeSegment(@Validated @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) { + public CommonResult updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) { segmentService.updateKnowledgeSegment(reqVO); return success(true); } @PutMapping("/update-status") @Operation(summary = "启禁用段落内容") - public CommonResult updateKnowledgeSegmentStatus(@Validated @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { + public CommonResult updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { segmentService.updateKnowledgeSegmentStatus(reqVO); return success(true); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java index c1e7947f5..76c001bd3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java @@ -4,10 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - AI 知识库-文档 分页 Request VO") +@Schema(description = "管理后台 - AI 知识库文档的分页 Request VO") @Data public class AiKnowledgeDocumentPageReqVO extends PageParam { @Schema(description = "文档名称", example = "Java 开发手册") private String name; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java index 94a022363..96ca61b3d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java @@ -17,21 +17,22 @@ public class AiKnowledgeDocumentRespVO extends PageParam { @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") private String name; - @Schema(description = "内容", example = "Java 是一门面向对象的语言.....") + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....") private String content; @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") private String url; - @Schema(description = "token 数量", example = "1024") + @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Integer tokens; - @Schema(description = "字符数", example = "1008") + @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") private Integer wordCount; - @Schema(description = "切片状态", example = "1") + @Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer sliceStatus; - @Schema(description = "文档状态", example = "1") + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer status; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java index 6fb42c774..2cc6a32f3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -15,6 +17,7 @@ public class AiKnowledgeDocumentUpdateReqVO { private Long id; @Schema(description = "是否启用", example = "1") + @InEnum(CommonStatusEnum.class) private Integer status; @Schema(description = "名称", example = "Java 开发手册") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java index 660c573ba..9cc5290ab 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java @@ -7,7 +7,7 @@ import lombok.Data; import org.hibernate.validator.constraints.URL; -@Schema(description = "管理后台 - AI 知识库创建【文档】 Request VO") +@Schema(description = "管理后台 - AI 知识库文档的创建 Request VO") @Data public class AiKnowledgeDocumentCreateReqVO { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java index 2eb08717e..3ff8a1c75 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java @@ -14,12 +14,13 @@ public class AiKnowledgeRespVO { @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") private String name; - @Schema(description = "知识库描述", example = "ruoyi-vue-pro 用户指南") + @Schema(description = "知识库描述", example = "帮助你快速构建系统") private String description; @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") private Long modelId; - @Schema(description = "模型标识", example = "qwen-72b-chat") + @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat") private String model; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java index 125cb80b1..8be3db501 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java @@ -4,15 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - AI 知识库分页 Request VO") +@Schema(description = "管理后台 - AI 知识库分段的分页 Request VO") @Data public class AiKnowledgeSegmentPageReqVO extends PageParam { - @Schema(description = "分段状态", example = "1") private Integer status; - @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "文档编号", example = "1") private Integer documentId; @Schema(description = "分段内容关键字", example = "Java 开发") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java index d8411618b..5e3f2d8cb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java @@ -22,12 +22,13 @@ public class AiKnowledgeSegmentRespVO { @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") private String content; - @Schema(description = "token 数量", example = "1024") + @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Integer tokens; - @Schema(description = "字符数", example = "1008") + @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") private Integer wordCount; - @Schema(description = "文档状态", example = "1") + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer status; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java index 409ce0146..2516c7dfb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java @@ -1,10 +1,13 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; -@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO") +@Schema(description = "管理后台 - AI 知识库段落的更新状态 Request VO") @Data public class AiKnowledgeSegmentUpdateStatusReqVO { @@ -12,6 +15,8 @@ public class AiKnowledgeSegmentUpdateStatusReqVO { private Long id; @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "是否启用不能为空") + @InEnum(CommonStatusEnum.class) private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index fc758ce31..84f7de654 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -28,12 +28,13 @@ public class AiKnowledgeSegmentDO extends BaseDO { private String vectorId; /** * 知识库编号 + * * 关联 {@link AiKnowledgeDO#getId()} */ private Long knowledgeId; /** * 文档编号 - *

+ * * 关联 {@link AiKnowledgeDocumentDO#getId()} */ private Long documentId; @@ -51,7 +52,7 @@ public class AiKnowledgeSegmentDO extends BaseDO { private Integer tokens; /** * 状态 - *

+ * * 枚举 {@link CommonStatusEnum} */ private Integer status; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 3758c3bfd..99f0621c8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -30,13 +30,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS; /** - * AI 知识库-文档 Service 实现类 + * AI 知识库文档 Service 实现类 * * @author xiaoxin */ @@ -61,24 +60,21 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Resource private AiChatModelService chatModelService; - - // TODO 芋艿:需要 review 下,代码格式; @Override @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { + // 0. 校验 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); + AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); + // 1.1 下载文档 - String url = createReqVO.getUrl(); - // 1.2 加载文档 - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url)); + TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl())); List documents = loader.get(); Document document = CollUtil.getFirst(documents); + // 1.2 文档记录入库 String content = document.getContent(); - Integer tokens = tokenCountEstimator.estimate(content); - Integer wordCount = content.length(); - - // 1.3 文档记录入库 AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) - .setTokens(tokens).setWordCount(wordCount) + .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); documentMapper.insert(documentDO); Long documentId = documentDO.getId(); @@ -90,22 +86,16 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic List segments = tokenTextSplitter.apply(documents); // 2.2 分段内容入库 List segmentDOList = CollectionUtils.convertList(segments, - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) + .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); segmentMapper.insertBatch(segmentDOList); - // 3.1 document 补充源数据 - segments.forEach(segment -> { - Map metadata = segment.getMetadata(); - metadata.put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()); - }); - - AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); - // 3.2 获取向量存储实例 + // 3.1 获取向量存储实例 VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); - // 3.3 向量化并存储 + // 3.2 向量化并存储 + segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId())); vectorStore.add(segments); return documentId; } @@ -117,7 +107,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Override public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { + // 1. 校验文档是否存在 validateKnowledgeDocumentExists(reqVO.getId()); + // 2. 更新文档 AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); documentMapper.updateById(document); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 22f634907..8ecb2d24a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; /** - * AI 知识库分片 Service 接口 + * AI 知识库段落 Service 接口 * * @author xiaoxin */ @@ -22,16 +22,17 @@ public interface AiKnowledgeSegmentService { PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO); /** - * 更新段落内容 + * 更新段落的内容 * * @param reqVO 更新内容 */ void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO); /** - * 更新状态 + * 更新段落的状态 * * @param reqVO 更新内容 */ void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java index 603325da6..f5f881349 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java @@ -85,14 +85,6 @@ public interface AiApiKeyService { */ ChatModel getChatModel(Long id); - /** - * 获得 EmbeddingModel 对象 - * - * @param id 编号 - * @return EmbeddingModel 对象 - */ - EmbeddingModel getEmbeddingModel(Long id); - /** * 获得 ImageModel 对象 * @@ -122,7 +114,15 @@ public interface AiApiKeyService { SunoApi getSunoApi(); /** - * 获得 vector 对象 + * 获得 EmbeddingModel 对象 + * + * @param id 编号 + * @return EmbeddingModel 对象 + */ + EmbeddingModel getEmbeddingModel(Long id); + + /** + * 获得 VectorStore 对象 * * @param id 编号 * @return VectorStore 对象 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index 25eec75b7..bf11ec218 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -109,13 +109,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl()); } - @Override - public EmbeddingModel getEmbeddingModel(Long id) { - AiApiKeyDO apiKey = validateApiKey(id); - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); - return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl()); - } - @Override public ImageModel getImageModel(AiPlatformEnum platform) { AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); @@ -145,10 +138,18 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); } + @Override + public EmbeddingModel getEmbeddingModel(Long id) { + AiApiKeyDO apiKey = validateApiKey(id); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl()); + } + @Override public VectorStore getOrCreateVectorStore(Long id) { AiApiKeyDO apiKey = validateApiKey(id); AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); } + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java index 6f628ea4d..7e8465375 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java @@ -26,18 +26,6 @@ public interface AiModelFactory { */ ChatModel getOrCreateChatModel(AiPlatformEnum platform, 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); - /** * 基于默认配置,获得 ChatModel 对象 * @@ -92,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); + } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 5c3524e66..aa46c45f2 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -99,21 +99,6 @@ public class AiModelFactoryImpl implements AiModelFactory { }); } - @Override - public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) { - String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url); - return Singleton.get(cacheKey, (Func0) () -> { - // TODO @xin 先测试一个 - switch (platform) { - case TONG_YI: - return buildTongYiEmbeddingModel(apiKey); - default: - throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); - } - }); - } - - @Override public ChatModel getDefaultChatModel(AiPlatformEnum platform) { //noinspection EnhancedSwitchMigration @@ -192,6 +177,20 @@ public class AiModelFactoryImpl implements AiModelFactory { return Singleton.get(cacheKey, (Func0) () -> 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) () -> { + // 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(); @@ -255,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); @@ -265,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); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java index 3e138cbd3..dad58a2c0 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java @@ -4,13 +4,14 @@ 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 对象 *

diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java index b3291c6b7..ec04c5e88 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java @@ -26,6 +26,7 @@ public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory { return Singleton.get(cacheKey, (Func0) () -> { // TODO 芋艿 @xin 这两个配置取哪好呢 // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上 + // TODO 回复:好的哈 String index = "default-index"; String prefix = "default:"; var config = RedisVectorStore.RedisVectorStoreConfig.builder() @@ -41,11 +42,11 @@ public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory { }); } - private static String buildClientCacheKey(Class clazz, Object... params) { if (ArrayUtil.isEmpty(params)) { return clazz.getName(); } return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_")); } + } From 34231bc5f8a7346b4f2fdb525cc8aba36d7b2d64 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 1 Sep 2024 08:55:58 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91AI=20=E5=A4=A7=E6=A8=A1=E5=9E=8B=EF=BC=9Atran?= =?UTF-8?q?sformer=20=E7=9A=84=20onnx=E3=80=81tokenizer=20=E8=B5=B0=20CDN?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=20github?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-server/src/main/resources/application.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 23c622ea9..8df1268c5 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -157,6 +157,12 @@ spring: redis: index: default-index prefix: "default:" + embedding: + transformer: + onnx: + model-uri: http://test.yudao.iocoder.cn/model.onnx + tokenizer: + uri: http://test.yudao.iocoder.cn/tokenizer.json qianfan: # 文心一言 api-key: x0cuLZ7XsaTCU08vuJWO87Lg secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK From 059f64acb0e9b77d249d3909de48c08630efd515 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 1 Sep 2024 08:58:40 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91AI=20=E5=A4=A7=E6=A8=A1=E5=9E=8B=EF=BC=9A?= =?UTF-8?q?=E5=B0=86=20spring-ai=20=E8=B0=83=E6=95=B4=E6=88=90=20group.spr?= =?UTF-8?q?ingframework.ai=EF=BC=8C=E8=A7=A3=E5=86=B3=20spring-ai=20?= =?UTF-8?q?=E6=9A=82=E6=97=B6=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91=20maven=20=E5=8A=A0=E9=80=9F=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-spring-boot-starter-ai/pom.xml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index 69a3cdbda..85996cb82 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -14,49 +14,50 @@ ${project.artifactId} AI 大模型拓展,接入国内外大模型 - 1.0.0-M1 + group.springframework.ai + 1.1.0 - org.springframework.ai + ${spring-ai.groupId} spring-ai-zhipuai-spring-boot-starter ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-openai-spring-boot-starter ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-azure-openai-spring-boot-starter ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-ollama-spring-boot-starter ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-stability-ai-spring-boot-starter ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-transformers-spring-boot-starter ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-tika-document-reader ${spring-ai.version} - org.springframework.ai + ${spring-ai.groupId} spring-ai-redis-store ${spring-ai.version} @@ -71,11 +72,10 @@ yudao-common - - group.springframework.ai + ${spring-ai.groupId} spring-ai-qianfan-spring-boot-starter - 1.1.0 + ${spring-ai.version}