Commit 89d079349c987eaa1065883433cd0e2ab6fc5bfe

Authored by YunaiV
1 parent 3f460dc6

【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型

Showing 36 changed files with 617 additions and 554 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
... ... @@ -12,31 +12,25 @@ public interface ErrorCodeConstants {
12 12 // ========== API 密钥 1-040-000-000 ==========
13 13 ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
14 14 ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
15   - ErrorCode API_KEY_MIDJOURNEY_NOT_FOUND = new ErrorCode(1_040_000_900, "Midjourney 模型不存在");
16   - ErrorCode API_KEY_SUNO_NOT_FOUND = new ErrorCode(1_040_000_901, "Suno 模型不存在");
17   - ErrorCode API_KEY_IMAGE_NODE_FOUND = new ErrorCode(1_040_000_902, "平台({}) 图片模型未配置");
18 15  
19   - // ========== API 聊天模型 1-040-001-000 ==========
20   - ErrorCode CHAT_MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
21   - ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
22   - ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型");
  16 + // ========== API 模型 1-040-001-000 ==========
  17 + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
  18 + ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
  19 + ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型");
23 20  
24 21 // ========== API 聊天角色 1-040-002-000 ==========
25 22 ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
26 23 ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
27 24  
28 25 // ========== API 聊天会话 1-040-003-000 ==========
29   -
30 26 ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
31 27 ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
32 28  
33 29 // ========== API 聊天消息 1-040-004-000 ==========
34   -
35 30 ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
36 31 ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
37 32  
38 33 // ========== API 绘画 1-040-005-000 ==========
39   -
40 34 ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!");
41 35 ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}");
42 36 ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}");
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
2 2  
3   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  3 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
4 4 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
5 5 import com.fhs.core.trans.anno.Trans;
6 6 import com.fhs.core.trans.constant.TransType;
... ... @@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO {
31 31 private Long roleId;
32 32  
33 33 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
34   - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName")
  34 + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
35 35 private Long modelId;
36 36  
37 37 @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
... ... @@ -14,18 +14,15 @@ import java.util.Map;
14 14 @Data
15 15 public class AiImageDrawReqVO {
16 16  
17   - @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
18   - private String platform; // 参见 AiPlatformEnum 枚举
  17 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + @NotNull(message = "模型编号不能为空")
  19 + private Long modelId;
19 20  
20 21 @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
21 22 @NotEmpty(message = "提示词不能为空")
22 23 @Size(max = 1200, message = "提示词最大 1200")
23 24 private String prompt;
24 25  
25   - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
26   - @NotEmpty(message = "模型不能为空")
27   - private String model;
28   -
29 26 /**
30 27 * 1. dall-e-2 模型:256x256、512x512、1024x1024
31 28 * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
... ... @@ -6,9 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
7 7 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
8 8 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
11   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
12 11 import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
13 12 import io.swagger.v3.oas.annotations.Operation;
14 13 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -76,9 +75,9 @@ public class AiApiKeyController {
76 75  
77 76 @GetMapping("/simple-list")
78 77 @Operation(summary = "获得 API 密钥分页列表")
79   - public CommonResult<List<AiChatModelRespVO>> getApiKeySimpleList() {
  78 + public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() {
80 79 List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
81   - return success(convertList(list, key -> new AiChatModelRespVO().setId(key.getId()).setName(key.getName())));
  80 + return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));
82 81 }
83 82  
84 83 }
85 84 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java deleted
1   -package cn.iocoder.yudao.module.ai.controller.admin.model;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
7   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
9   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
10   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
11   -import io.swagger.v3.oas.annotations.Operation;
12   -import io.swagger.v3.oas.annotations.Parameter;
13   -import io.swagger.v3.oas.annotations.tags.Tag;
14   -import jakarta.annotation.Resource;
15   -import jakarta.validation.Valid;
16   -import org.springframework.security.access.prepost.PreAuthorize;
17   -import org.springframework.validation.annotation.Validated;
18   -import org.springframework.web.bind.annotation.*;
19   -
20   -import java.util.List;
21   -
22   -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
23   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
24   -
25   -@Tag(name = "管理后台 - AI 聊天模型")
26   -@RestController
27   -@RequestMapping("/ai/chat-model")
28   -@Validated
29   -public class AiChatModelController {
30   -
31   - @Resource
32   - private AiChatModelService chatModelService;
33   -
34   - @PostMapping("/create")
35   - @Operation(summary = "创建聊天模型")
36   - @PreAuthorize("@ss.hasPermission('ai:chat-model:create')")
37   - public CommonResult<Long> createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) {
38   - return success(chatModelService.createChatModel(createReqVO));
39   - }
40   -
41   - @PutMapping("/update")
42   - @Operation(summary = "更新聊天模型")
43   - @PreAuthorize("@ss.hasPermission('ai:chat-model:update')")
44   - public CommonResult<Boolean> updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) {
45   - chatModelService.updateChatModel(updateReqVO);
46   - return success(true);
47   - }
48   -
49   - @DeleteMapping("/delete")
50   - @Operation(summary = "删除聊天模型")
51   - @Parameter(name = "id", description = "编号", required = true)
52   - @PreAuthorize("@ss.hasPermission('ai:chat-model:delete')")
53   - public CommonResult<Boolean> deleteChatModel(@RequestParam("id") Long id) {
54   - chatModelService.deleteChatModel(id);
55   - return success(true);
56   - }
57   -
58   - @GetMapping("/get")
59   - @Operation(summary = "获得聊天模型")
60   - @Parameter(name = "id", description = "编号", required = true, example = "1024")
61   - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
62   - public CommonResult<AiChatModelRespVO> getChatModel(@RequestParam("id") Long id) {
63   - AiChatModelDO chatModel = chatModelService.getChatModel(id);
64   - return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class));
65   - }
66   -
67   - @GetMapping("/page")
68   - @Operation(summary = "获得聊天模型分页")
69   - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
70   - public CommonResult<PageResult<AiChatModelRespVO>> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) {
71   - PageResult<AiChatModelDO> pageResult = chatModelService.getChatModelPage(pageReqVO);
72   - return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class));
73   - }
74   -
75   - @GetMapping("/simple-list")
76   - @Operation(summary = "获得聊天模型列表")
77   - @Parameter(name = "status", description = "状态", required = true, example = "1")
78   - public CommonResult<List<AiChatModelRespVO>> getChatModelSimpleList(@RequestParam("status") Integer status) {
79   - List<AiChatModelDO> list = chatModelService.getChatModelListByStatus(status);
80   - return success(convertList(list, model -> new AiChatModelRespVO().setId(model.getId())
81   - .setName(model.getName()).setModel(model.getModel())));
82   - }
83   -
84   -}
85 0 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  11 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.tags.Tag;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.*;
  20 +
  21 +import java.util.List;
  22 +
  23 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  25 +
  26 +@Tag(name = "管理后台 - AI 模型")
  27 +@RestController
  28 +@RequestMapping("/ai/model")
  29 +@Validated
  30 +public class AiModelController {
  31 +
  32 + @Resource
  33 + private AiModelService modelService;
  34 +
  35 + @PostMapping("/create")
  36 + @Operation(summary = "创建模型")
  37 + @PreAuthorize("@ss.hasPermission('ai:model:create')")
  38 + public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {
  39 + return success(modelService.createModel(createReqVO));
  40 + }
  41 +
  42 + @PutMapping("/update")
  43 + @Operation(summary = "更新模型")
  44 + @PreAuthorize("@ss.hasPermission('ai:model:update')")
  45 + public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {
  46 + modelService.updateModel(updateReqVO);
  47 + return success(true);
  48 + }
  49 +
  50 + @DeleteMapping("/delete")
  51 + @Operation(summary = "删除模型")
  52 + @Parameter(name = "id", description = "编号", required = true)
  53 + @PreAuthorize("@ss.hasPermission('ai:model:delete')")
  54 + public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
  55 + modelService.deleteModel(id);
  56 + return success(true);
  57 + }
  58 +
  59 + @GetMapping("/get")
  60 + @Operation(summary = "获得模型")
  61 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  62 + @PreAuthorize("@ss.hasPermission('ai:model:query')")
  63 + public CommonResult<AiModelRespVO> getModel(@RequestParam("id") Long id) {
  64 + AiModelDO model = modelService.getModel(id);
  65 + return success(BeanUtils.toBean(model, AiModelRespVO.class));
  66 + }
  67 +
  68 + @GetMapping("/page")
  69 + @Operation(summary = "获得模型分页")
  70 + @PreAuthorize("@ss.hasPermission('ai:model:query')")
  71 + public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {
  72 + PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);
  73 + return success(BeanUtils.toBean(pageResult, AiModelRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/simple-list")
  77 + @Operation(summary = "获得模型列表")
  78 + @Parameter(name = "type", description = "类型", required = true, example = "1")
  79 + @Parameter(name = "platform", description = "平台", example = "midjourney")
  80 + public CommonResult<List<AiModelRespVO>> getModelSimpleList(
  81 + @RequestParam("type") Integer type,
  82 + @RequestParam(value = "platform", required = false) String platform) {
  83 + List<AiModelDO> list = modelService.getModelListByStatusAndType(
  84 + CommonStatusEnum.ENABLE.getStatus(), type, platform);
  85 + return success(convertList(list, model -> new AiModelRespVO().setId(model.getId())
  86 + .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform())));
  87 + }
  88 +
  89 +}
0 90 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
2 2  
3   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  3 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
4 4 import com.fhs.core.trans.anno.Trans;
5 5 import com.fhs.core.trans.constant.TransType;
6 6 import com.fhs.core.trans.vo.VO;
... ... @@ -20,7 +20,7 @@ public class AiChatRoleRespVO implements VO {
20 20 private Long userId;
21 21  
22 22 @Schema(description = "模型编号", example = "17640")
23   - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"})
  23 + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"})
24 24 private Long modelId;
25 25 @Schema(description = "模型名字", example = "张三")
26 26 private String modelName;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2  
3 3 import lombok.*;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 6  
7   -@Schema(description = "管理后台 - API 聊天模型分页 Request VO")
  7 +@Schema(description = "管理后台 - API 模型分页 Request VO")
8 8 @Data
9   -public class AiChatModelPageReqVO extends PageParam {
  9 +public class AiModelPageReqVO extends PageParam {
10 10  
11 11 @Schema(description = "模型名字", example = "张三")
12 12 private String name;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
7 7  
8   -@Schema(description = "管理后台 - AI 聊天模型 Response VO")
  8 +@Schema(description = "管理后台 - AI 模型 Response VO")
9 9 @Data
10   -public class AiChatModelRespVO {
  10 +public class AiModelRespVO {
11 11  
12 12 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
13 13 private Long id;
... ... @@ -24,6 +24,9 @@ public class AiChatModelRespVO {
24 24 @Schema(description = "模型平台", example = "OpenAI")
25 25 private String platform;
26 26  
  27 + @Schema(description = "模型类型", example = "1")
  28 + private Integer type;
  29 +
27 30 @Schema(description = "排序", example = "1")
28 31 private Integer sort;
29 32  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
2 2  
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
  4 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
3 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 6 import cn.iocoder.yudao.framework.common.validation.InEnum;
5 7 import io.swagger.v3.oas.annotations.media.Schema;
6   -import lombok.*;
7   -import jakarta.validation.constraints.*;
  8 +import jakarta.validation.constraints.NotEmpty;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import lombok.Data;
8 11  
9   -@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO")
  12 +@Schema(description = "管理后台 - API 模型新增/修改 Request VO")
10 13 @Data
11   -public class AiChatModelSaveReqVO {
  14 +public class AiModelSaveReqVO {
12 15  
13 16 @Schema(description = "编号", example = "2630")
14 17 private Long id;
... ... @@ -27,8 +30,14 @@ public class AiChatModelSaveReqVO {
27 30  
28 31 @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
29 32 @NotEmpty(message = "模型平台不能为空")
  33 + @InEnum(AiPlatformEnum.class)
30 34 private String platform;
31 35  
  36 + @Schema(description = "模型类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  37 + @NotNull(message = "模型类型不能为空")
  38 + @InEnum(AiModelTypeEnum.class)
  39 + private Integer type;
  40 +
32 41 @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
33 42 @NotNull(message = "排序不能为空")
34 43 private Integer sort;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
6 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
7 7 import com.baomidou.mybatisplus.annotation.KeySequence;
8 8 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -76,13 +76,13 @@ public class AiChatConversationDO extends BaseDO {
76 76 /**
77 77 * 模型编号
78 78 *
79   - * 关联 {@link AiChatModelDO#getId()} 字段
  79 + * 关联 {@link AiModelDO#getId()} 字段
80 80 */
81 81 private Long modelId;
82 82 /**
83 83 * 模型标志
84 84 *
85   - * 冗余 {@link AiChatModelDO#getModel()} 字段
  85 + * 冗余 {@link AiModelDO#getModel()} 字段
86 86 */
87 87 private String model;
88 88  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
6 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
7 7 import com.baomidou.mybatisplus.annotation.KeySequence;
8 8 import com.baomidou.mybatisplus.annotation.TableField;
... ... @@ -83,13 +83,13 @@ public class AiChatMessageDO extends BaseDO {
83 83 /**
84 84 * 模型标志
85 85 *
86   - * 冗余 {@link AiChatModelDO#getModel()}
  86 + * 冗余 {@link AiModelDO#getModel()}
87 87 */
88 88 private String model;
89 89 /**
90 90 * 模型编号
91 91 *
92   - * 关联 {@link AiChatModelDO#getId()} 字段
  92 + * 关联 {@link AiModelDO#getId()} 字段
93 93 */
94 94 private Long modelId;
95 95  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
6 6 import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
7 7 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
8 8 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -52,11 +52,16 @@ public class AiImageDO extends BaseDO {
52 52 * 枚举 {@link cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum}
53 53 */
54 54 private String platform;
55   - // TODO @芋艿:modelId?
56 55 /**
57   - * 模型
  56 + * 模型编号
58 57 *
59   - * 冗余 {@link AiChatModelDO#getModel()}
  58 + * 关联 {@link AiModelDO#getId()}
  59 + */
  60 + private Long modelId;
  61 + /**
  62 + * 模型标识
  63 + *
  64 + * 冗余 {@link AiModelDO#getModel()}
60 65 */
61 66 private String model;
62 67  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
6 6 import com.baomidou.mybatisplus.annotation.KeySequence;
7 7 import com.baomidou.mybatisplus.annotation.TableId;
8 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -35,13 +35,13 @@ public class AiKnowledgeDO extends BaseDO {
35 35 /**
36 36 * 向量模型编号
37 37 *
38   - * 关联 {@link AiChatModelDO#getId()}
  38 + * 关联 {@link AiModelDO#getId()}
39 39 */
40 40 private Long embeddingModelId;
41 41 /**
42 42 * 模型标识
43 43 *
44   - * 冗余 {@link AiChatModelDO#getModel()}
  44 + * 冗余 {@link AiModelDO#getModel()}
45 45 */
46 46 private String embeddingModel;
47 47  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
5 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -36,7 +37,12 @@ public class AiMindMapDO extends BaseDO {
36 37 * 枚举 {@link AiPlatformEnum}
37 38 */
38 39 private String platform;
39   - // TODO @芋艿:modelId?
  40 + /**
  41 + * 模型编号
  42 + *
  43 + * 关联 {@link AiModelDO#getId()}
  44 + */
  45 + private Long modelId;
40 46 /**
41 47 * 模型
42 48 */
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
... ... @@ -58,7 +58,7 @@ public class AiChatRoleDO extends BaseDO {
58 58 /**
59 59 * 模型编号
60 60 *
61   - * 关联 {@link AiChatModelDO#getId()} 字段
  61 + * 关联 {@link AiModelDO#getId()} 字段
62 62 */
63 63 private Long modelId;
64 64  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.model;
2 2  
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
3 4 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 6 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
... ... @@ -8,23 +9,22 @@ import com.baomidou.mybatisplus.annotation.TableId;
8 9 import com.baomidou.mybatisplus.annotation.TableName;
9 10 import lombok.*;
10 11  
11   -// TODO @芋艿,需要改造,增加 type
12 12 /**
13   - * AI 聊天模型 DO
  13 + * AI 模型 DO
14 14 *
15   - * 默认聊天模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
  15 + * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
16 16 *
17 17 * @author fansili
18 18 * @since 2024/4/24 19:39
19 19 */
20   -@TableName("ai_chat_model")
21   -@KeySequence("ai_chat_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  20 +@TableName("ai_model")
  21 +@KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
22 22 @Data
23 23 @EqualsAndHashCode(callSuper = true)
24 24 @Builder
25 25 @NoArgsConstructor
26 26 @AllArgsConstructor
27   -public class AiChatModelDO extends BaseDO {
  27 +public class AiModelDO extends BaseDO {
28 28  
29 29 /**
30 30 * 编号
... ... @@ -51,6 +51,12 @@ public class AiChatModelDO extends BaseDO {
51 51 * 枚举 {@link AiPlatformEnum}
52 52 */
53 53 private String platform;
  54 + /**
  55 + * 类型
  56 + *
  57 + * 枚举 {@link AiModelTypeEnum}
  58 + */
  59 + private Integer type;
54 60  
55 61 /**
56 62 * 排序值
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java
... ... @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  6 +import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
5 7 import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
6 8 import com.baomidou.mybatisplus.annotation.KeySequence;
7 9 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -44,7 +46,12 @@ public class AiWriteDO extends BaseDO {
44 46 * 枚举 {@link AiPlatformEnum}
45 47 */
46 48 private String platform;
47   - // TODO @芋艿:modelId?
  49 + /**
  50 + * 模型编号
  51 + *
  52 + * 关联 {@link AiModelDO#getId()}
  53 + */
  54 + private Long modelId;
48 55 /**
49 56 * 模型
50 57 */
... ... @@ -67,25 +74,25 @@ public class AiWriteDO extends BaseDO {
67 74 /**
68 75 * 长度提示词
69 76 *
70   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH}
  77 + * 字典:{@link DictTypeConstants#AI_WRITE_LENGTH}
71 78 */
72 79 private Integer length;
73 80 /**
74 81 * 格式提示词
75 82 *
76   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT}
  83 + * 字典:{@link DictTypeConstants#AI_WRITE_FORMAT}
77 84 */
78 85 private Integer format;
79 86 /**
80 87 * 语气提示词
81 88 *
82   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE}
  89 + * 字典:{@link DictTypeConstants#AI_WRITE_TONE}
83 90 */
84 91 private Integer tone;
85 92 /**
86 93 * 语言提示词
87 94 *
88   - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE}
  95 + * 字典:{@link DictTypeConstants#AI_WRITE_LANGUAGE}
89 96 */
90 97 private Integer language;
91 98  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.dal.mysql.model;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import javax.annotation.Nullable;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * API 模型 Mapper
  16 + *
  17 + * @author fansili
  18 + */
  19 +@Mapper
  20 +public interface AiChatMapper extends BaseMapperX<AiModelDO> {
  21 +
  22 + default AiModelDO selectFirstByStatus(Integer type, Integer status) {
  23 + return selectOne(new QueryWrapperX<AiModelDO>()
  24 + .eq("type", type)
  25 + .eq("status", status)
  26 + .limitN(1)
  27 + .orderByAsc("sort"));
  28 + }
  29 +
  30 + default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {
  31 + return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()
  32 + .likeIfPresent(AiModelDO::getName, reqVO.getName())
  33 + .eqIfPresent(AiModelDO::getModel, reqVO.getModel())
  34 + .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform())
  35 + .orderByAsc(AiModelDO::getSort));
  36 + }
  37 +
  38 + default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type,
  39 + @Nullable String platform) {
  40 + return selectList(new LambdaQueryWrapperX<AiModelDO>()
  41 + .eq(AiModelDO::getStatus, status)
  42 + .eq(AiModelDO::getType, type)
  43 + .eqIfPresent(AiModelDO::getPlatform, platform)
  44 + .orderByAsc(AiModelDO::getSort));
  45 + }
  46 +
  47 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java deleted
1   -package cn.iocoder.yudao.module.ai.dal.mysql.model;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
7   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
8   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
9   -import org.apache.ibatis.annotations.Mapper;
10   -
11   -import java.util.Collection;
12   -import java.util.List;
13   -
14   -/**
15   - * API 聊天模型 Mapper
16   - *
17   - * @author fansili
18   - */
19   -@Mapper
20   -public interface AiChatModelMapper extends BaseMapperX<AiChatModelDO> {
21   -
22   - default AiChatModelDO selectFirstByStatus(Integer status) {
23   - return selectOne(new QueryWrapperX<AiChatModelDO>()
24   - .eq("status", status)
25   - .limitN(1)
26   - .orderByAsc("sort"));
27   - }
28   -
29   - default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) {
30   - return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>()
31   - .likeIfPresent(AiChatModelDO::getName, reqVO.getName())
32   - .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel())
33   - .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform())
34   - .orderByAsc(AiChatModelDO::getSort));
35   - }
36   -
37   - default List<AiChatModelDO> selectList(Integer status) {
38   - return selectList(new LambdaQueryWrapperX<AiChatModelDO>()
39   - .eq(AiChatModelDO::getStatus, status)
40   - .orderByAsc(AiChatModelDO::getSort));
41   - }
42   -
43   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
... ... @@ -4,17 +4,18 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.util.ObjUtil;
6 6 import cn.hutool.core.util.ObjectUtil;
  7 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 10 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
10 11 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
11 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
12 13 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
13   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  14 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
14 15 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
15 16 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
16 17 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
17   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  18 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
18 19 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
19 20 import jakarta.annotation.Resource;
20 21 import lombok.extern.slf4j.Slf4j;
... ... @@ -44,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
44 45 private AiChatConversationMapper chatConversationMapper;
45 46  
46 47 @Resource
47   - private AiChatModelService chatModalService;
  48 + private AiModelService chatModalService;
48 49 @Resource
49 50 private AiChatRoleService chatRoleService;
50 51 @Resource
... ... @@ -54,9 +55,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
54 55 public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
55 56 // 1.1 获得 AiChatRoleDO 聊天角色
56 57 AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
57   - // 1.2 获得 AiChatModelDO 聊天模型
58   - AiChatModelDO model = role != null && role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId())
59   - : chatModalService.getRequiredDefaultChatModel();
  58 + // 1.2 获得 AiModelDO 聊天模型
  59 + AiModelDO model = role != null && role.getModelId() != null ? chatModalService.validateModel(role.getModelId())
  60 + : chatModalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
60 61 Assert.notNull(model, "必须找到默认模型");
61 62 validateChatModel(model);
62 63  
... ... @@ -86,9 +87,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
86 87 throw exception(CHAT_CONVERSATION_NOT_EXISTS);
87 88 }
88 89 // 1.2 校验模型是否存在(修改模型的情况)
89   - AiChatModelDO model = null;
  90 + AiModelDO model = null;
90 91 if (updateReqVO.getModelId() != null) {
91   - model = chatModalService.validateChatModel(updateReqVO.getModelId());
  92 + model = chatModalService.validateModel(updateReqVO.getModelId());
92 93 }
93 94  
94 95 // 1.3 校验知识库是否存在
... ... @@ -139,7 +140,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
139 140 chatConversationMapper.deleteById(id);
140 141 }
141 142  
142   - private void validateChatModel(AiChatModelDO model) {
  143 + private void validateChatModel(AiModelDO model) {
143 144 if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
144 145 return;
145 146 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
... ... @@ -15,13 +15,12 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage
15 15 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
16 16 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
17 17 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
18   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  18 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
19 19 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
20 20 import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
21 21 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
22 22 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
23   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
24   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  23 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
25 24 import jakarta.annotation.Resource;
26 25 import lombok.extern.slf4j.Slf4j;
27 26 import org.springframework.ai.chat.messages.Message;
... ... @@ -63,9 +62,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
63 62 @Resource
64 63 private AiChatConversationService chatConversationService;
65 64 @Resource
66   - private AiChatModelService chatModalService;
67   - @Resource
68   - private AiApiKeyService apiKeyService;
  65 + private AiModelService modalService;
69 66 @Resource
70 67 private AiKnowledgeSegmentService knowledgeSegmentService;
71 68  
... ... @@ -78,8 +75,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
78 75 }
79 76 List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
80 77 // 1.2 校验模型
81   - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
82   - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  78 + AiModelDO model = modalService.validateModel(conversation.getModelId());
  79 + ChatModel chatModel = modalService.getChatModel(model.getKeyId());
83 80  
84 81 // 2. 插入 user 发送消息
85 82 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
... ... @@ -112,8 +109,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
112 109 }
113 110 List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
114 111 // 1.2 校验模型
115   - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
116   - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  112 + AiModelDO model = modalService.validateModel(conversation.getModelId());
  113 + StreamingChatModel chatModel = modalService.getChatModel(model.getKeyId());
117 114  
118 115 // 2. 插入 user 发送消息
119 116 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
... ... @@ -161,8 +158,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
161 158 return null;
162 159 }
163 160  
164   - private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,List<AiKnowledgeSegmentDO> segmentList,
165   - AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
  161 + private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages, List<AiKnowledgeSegmentDO> segmentList,
  162 + AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
166 163 // 1. 构建 Prompt Message 列表
167 164 List<Message> chatMessages = new ArrayList<>();
168 165  
... ... @@ -232,7 +229,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
232 229 }
233 230  
234 231 private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
235   - AiChatModelDO model, Long userId, Long roleId,
  232 + AiModelDO model, Long userId, Long roleId,
236 233 MessageType messageType, String content, Boolean useContext) {
237 234 AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
238 235 .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
... ... @@ -12,13 +12,17 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
12 12 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
13 13 import cn.iocoder.yudao.framework.common.pojo.PageResult;
14 14 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
15   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;
  15 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
  16 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
  17 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
  18 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
16 19 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
17 20 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
18 21 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
  22 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
19 23 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
20 24 import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
21   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  25 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
22 26 import cn.iocoder.yudao.module.infra.api.file.FileApi;
23 27 import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
24 28 import jakarta.annotation.Resource;
... ... @@ -55,13 +59,13 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
55 59 public class AiImageServiceImpl implements AiImageService {
56 60  
57 61 @Resource
58   - private AiImageMapper imageMapper;
  62 + private AiModelService modelService;
59 63  
60 64 @Resource
61   - private FileApi fileApi;
  65 + private AiImageMapper imageMapper;
62 66  
63 67 @Resource
64   - private AiApiKeyService apiKeyService;
  68 + private FileApi fileApi;
65 69  
66 70 @Override
67 71 public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
... ... @@ -88,23 +92,31 @@ public class AiImageServiceImpl implements AiImageService {
88 92  
89 93 @Override
90 94 public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
91   - // 1. 保存数据库
92   - AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
93   - .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
  95 + // 1. 校验模型
  96 + AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
  97 +
  98 + // 2. 保存数据库
  99 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId)
  100 + .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel())
  101 + .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
94 102 imageMapper.insert(image);
95   - // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果
96   - getSelf().executeDrawImage(image, drawReqVO);
  103 +
  104 + // 3. 异步绘制,后续前端通过返回的 id 进行轮询结果
  105 + getSelf().executeDrawImage(image, drawReqVO, model);
97 106 return image.getId();
98 107 }
99 108  
100 109 @Async
101   - public void executeDrawImage(AiImageDO image, AiImageDrawReqVO req) {
  110 + public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
102 111 try {
103 112 // 1.1 构建请求
104   - ImageOptions request = buildImageOptions(req);
  113 + ImageOptions request = buildImageOptions(reqVO, model);
105 114 // 1.2 执行请求
106   - ImageModel imageModel = apiKeyService.getImageModel(AiPlatformEnum.validatePlatform(req.getPlatform()));
107   - ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request));
  115 + ImageModel imageModel = modelService.getImageModel(model.getId());
  116 + ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request));
  117 + if (response.getResult() == null) {
  118 + throw new IllegalArgumentException("生成结果为空");
  119 + }
108 120  
109 121 // 2. 上传到文件服务
110 122 String b64Json = response.getResult().getOutput().getB64Json();
... ... @@ -116,25 +128,25 @@ public class AiImageServiceImpl implements AiImageService {
116 128 imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
117 129 .setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
118 130 } catch (Exception ex) {
119   - log.error("[doDall][image({}) 生成异常]", image, ex);
  131 + log.error("[executeDrawImage][image({}) 生成异常]", image, ex);
120 132 imageMapper.updateById(new AiImageDO().setId(image.getId())
121 133 .setStatus(AiImageStatusEnum.FAIL.getStatus())
122 134 .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
123 135 }
124 136 }
125 137  
126   - private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) {
127   - if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
  138 + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) {
  139 + if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
128 140 // https://platform.openai.com/docs/api-reference/images/create
129   - return OpenAiImageOptions.builder().withModel(draw.getModel())
  141 + return OpenAiImageOptions.builder().withModel(model.getModel())
130 142 .withHeight(draw.getHeight()).withWidth(draw.getWidth())
131 143 .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
132 144 .withResponseFormat("b64_json")
133 145 .build();
134   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
  146 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
135 147 // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
136 148 // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
137   - return StabilityAiImageOptions.builder().model(draw.getModel())
  149 + return StabilityAiImageOptions.builder().model(model.getModel())
138 150 .height(draw.getHeight()).width(draw.getWidth())
139 151 .seed(Long.valueOf(draw.getOptions().get("seed")))
140 152 .cfgScale(Float.valueOf(draw.getOptions().get("scale")))
... ... @@ -143,22 +155,22 @@ public class AiImageServiceImpl implements AiImageService {
143 155 .stylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
144 156 .clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
145 157 .build();
146   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
  158 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
147 159 return DashScopeImageOptions.builder()
148   - .withModel(draw.getModel()).withN(1)
  160 + .withModel(model.getModel()).withN(1)
149 161 .withHeight(draw.getHeight()).withWidth(draw.getWidth())
150 162 .build();
151   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
  163 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
152 164 return QianFanImageOptions.builder()
153   - .model(draw.getModel()).N(1)
  165 + .model(model.getModel()).N(1)
154 166 .height(draw.getHeight()).width(draw.getWidth())
155 167 .build();
156   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
  168 + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
157 169 return ZhiPuAiImageOptions.builder()
158   - .model(draw.getModel())
  170 + .model(model.getModel())
159 171 .build();
160 172 }
161   - throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform());
  173 + throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform());
162 174 }
163 175  
164 176 @Override
... ... @@ -206,7 +218,7 @@ public class AiImageServiceImpl implements AiImageService {
206 218 @Override
207 219 @Transactional(rollbackFor = Exception.class)
208 220 public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) {
209   - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
  221 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi();
210 222 // 1. 保存数据库
211 223 AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
212 224 .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
... ... @@ -237,7 +249,7 @@ public class AiImageServiceImpl implements AiImageService {
237 249  
238 250 @Override
239 251 public Integer midjourneySync() {
240   - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
  252 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi();
241 253 // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
242 254 List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform(
243 255 AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
... ... @@ -308,7 +320,7 @@ public class AiImageServiceImpl implements AiImageService {
308 320  
309 321 @Override
310 322 public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
311   - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
  323 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi();
312 324 // 1.1 检查 image
313 325 AiImageDO image = validateImageExists(reqVO.getId());
314 326 if (ObjUtil.notEqual(userId, image.getUserId())) {
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
... ... @@ -7,14 +7,17 @@ import cn.hutool.core.util.StrUtil;
7 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
  12 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
  13 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
11 14 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
12 15 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
13 16 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
14 17 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
15   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
16 18 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
17 19 import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
  20 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
18 21 import jakarta.annotation.Resource;
19 22 import lombok.extern.slf4j.Slf4j;
20 23 import org.springframework.ai.document.Document;
... ... @@ -33,8 +36,8 @@ import java.util.Objects;
33 36  
34 37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
35 38 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
36   -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
37 39 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG;
  40 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
38 41  
39 42 /**
40 43 * AI 知识库分片 Service 实现类
... ... @@ -58,7 +61,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
58 61 @Lazy // 延迟加载,避免循环依赖
59 62 private AiKnowledgeDocumentService knowledgeDocumentService;
60 63 @Resource
61   - private AiApiKeyService apiKeyService;
  64 + private AiModelService modelService;
62 65  
63 66 @Resource
64 67 private TokenCountEstimator tokenCountEstimator;
... ... @@ -180,7 +183,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
180 183 AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId());
181 184  
182 185 // 2.1 向量检索
183   - VectorStore vectorStore = apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId());
  186 + VectorStore vectorStore = getVectorStoreById(knowledge);
184 187 List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder()
185 188 .query(reqBO.getContent())
186 189 .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK()))
... ... @@ -251,11 +254,12 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
251 254 }
252 255  
253 256 private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) {
254   - return apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId());
  257 + return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId());
255 258 }
256 259  
257 260 private VectorStore getVectorStoreById(Long knowledgeId) {
258   - return getVectorStoreById(knowledgeService.validateKnowledgeExists(knowledgeId));
  261 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
  262 + return getVectorStoreById(knowledge);
259 263 }
260 264  
261 265 private static List<Document> splitContentByToken(String content, Integer segmentMaxTokens) {
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
... ... @@ -5,9 +5,9 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 5 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
6 6 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
7 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
8   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
9 9 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
10   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  10 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
11 11 import jakarta.annotation.Resource;
12 12 import lombok.extern.slf4j.Slf4j;
13 13 import org.springframework.stereotype.Service;
... ... @@ -28,12 +28,12 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
28 28 private AiKnowledgeMapper knowledgeMapper;
29 29  
30 30 @Resource
31   - private AiChatModelService chatModelService;
  31 + private AiModelService chatModelService;
32 32  
33 33 @Override
34 34 public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {
35 35 // 1. 校验模型配置
36   - AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getEmbeddingModelId());
  36 + AiModelDO model = chatModelService.validateModel(createReqVO.getEmbeddingModelId());
37 37  
38 38 // 2. 插入知识库
39 39 AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
... ... @@ -47,7 +47,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
47 47 // 1.1 校验知识库存在
48 48 validateKnowledgeExists(updateReqVO.getId());
49 49 // 1.2 校验模型配置
50   - AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getEmbeddingModelId());
  50 + AiModelDO model = chatModelService.validateModel(updateReqVO.getEmbeddingModelId());
51 51  
52 52 // 2. 更新知识库
53 53 AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class)
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.mindmap;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
6 7 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
7 8 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
8 9 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
... ... @@ -12,14 +13,13 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 13 import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
13 14 import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
14 15 import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
15   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
16 16 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  17 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
17 18 import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper;
18 19 import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
19 20 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
20   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
21   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
22 21 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  22 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
23 23 import jakarta.annotation.Resource;
24 24 import lombok.extern.slf4j.Slf4j;
25 25 import org.springframework.ai.chat.messages.Message;
... ... @@ -50,9 +50,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_E
50 50 public class AiMindMapServiceImpl implements AiMindMapService {
51 51  
52 52 @Resource
53   - private AiApiKeyService apiKeyService;
54   - @Resource
55   - private AiChatModelService chatModalService;
  53 + private AiModelService modalService;
56 54 @Resource
57 55 private AiChatRoleService chatRoleService;
58 56  
... ... @@ -65,17 +63,17 @@ public class AiMindMapServiceImpl implements AiMindMapService {
65 63 AiChatRoleDO role = CollUtil.getFirst(
66 64 chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));
67 65 // 1.1 获取导图执行模型
68   - AiChatModelDO model = getModel(role);
  66 + AiModelDO model = getModel(role);
69 67 // 1.2 获取角色设定消息
70 68 String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())
71 69 ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
72 70 // 1.3 校验平台
73 71 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
74   - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  72 + ChatModel chatModel = modalService.getChatModel(model.getId());
75 73  
76 74 // 2. 插入思维导图信息
77   - AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class,
78   - mindMap -> mindMap.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()));
  75 + AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId)
  76 + .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
79 77 mindMapMapper.insert(mindMapDO);
80 78  
81 79 // 3.1 构建 Prompt,并进行调用
... ... @@ -103,7 +101,7 @@ public class AiMindMapServiceImpl implements AiMindMapService {
103 101  
104 102 }
105 103  
106   - private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) {
  104 + private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
107 105 // 1. 构建 message 列表
108 106 List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
109 107 // 2. 构建 options 对象
... ... @@ -123,13 +121,13 @@ public class AiMindMapServiceImpl implements AiMindMapService {
123 121 return chatMessages;
124 122 }
125 123  
126   - private AiChatModelDO getModel(AiChatRoleDO role) {
127   - AiChatModelDO model = null;
  124 + private AiModelDO getModel(AiChatRoleDO role) {
  125 + AiModelDO model = null;
128 126 if (role != null && role.getModelId() != null) {
129   - model = chatModalService.getChatModel(role.getModelId());
  127 + model = modalService.getModel(role.getModelId());
130 128 }
131 129 if (model == null) {
132   - model = chatModalService.getRequiredDefaultChatModel();
  130 + model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
133 131 }
134 132 Assert.notNull(model, "[AI] 获取不到模型");
135 133 return model;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
1 1 package cn.iocoder.yudao.module.ai.service.model;
2 2  
3   -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4   -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
5   -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
6 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 4 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
8 5 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
9 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
10 7 import jakarta.validation.Valid;
11   -import org.springframework.ai.chat.model.ChatModel;
12   -import org.springframework.ai.image.ImageModel;
13   -import org.springframework.ai.vectorstore.VectorStore;
14 8  
15 9 import java.util.List;
16 10  
... ... @@ -74,50 +68,13 @@ public interface AiApiKeyService {
74 68 */
75 69 List<AiApiKeyDO> getApiKeyList();
76 70  
77   - // ========== 与 spring-ai 集成 ==========
78   -
79   - /**
80   - * 获得 ChatModel 对象
81   - *
82   - * @param id 编号
83   - * @return ChatModel 对象
84   - */
85   - ChatModel getChatModel(Long id);
86   -
87 71 /**
88   - * 获得 ImageModel 对象
89   - *
90   - * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择
  72 + * 获得默认的 API 密钥
91 73 *
92 74 * @param platform 平台
93   - * @return ImageModel 对象
94   - */
95   - ImageModel getImageModel(AiPlatformEnum platform);
96   -
97   - /**
98   - * 获得 MidjourneyApi 对象
99   - *
100   - * TODO 可优化点:目前默认获取 Midjourney 对应的第一个开启的配置用于绘画;后续可以支持配置选择
101   - *
102   - * @return MidjourneyApi 对象
103   - */
104   - MidjourneyApi getMidjourneyApi();
105   -
106   - /**
107   - * 获得 SunoApi 对象
108   - *
109   - * TODO 可优化点:目前默认获取 Suno 对应的第一个开启的配置用于音乐;后续可以支持配置选择
110   - *
111   - * @return SunoApi 对象
112   - */
113   - SunoApi getSunoApi();
114   -
115   - /**
116   - * 获得 VectorStore 对象
117   - *
118   - * @param modelId 编号
119   - * @return VectorStore 对象
  75 + * @param status 状态
  76 + * @return API 密钥
120 77 */
121   - VectorStore getOrCreateVectorStoreByModelId(Long modelId);
  78 + AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status);
122 79  
123 80 }
124 81 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.model;
2 2  
3   -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4   -import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
5   -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
6   -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
7 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 6 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
11 7 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
12 8 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
13   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
14 9 import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
15 10 import jakarta.annotation.Resource;
16   -import org.springframework.ai.chat.model.ChatModel;
17   -import org.springframework.ai.embedding.EmbeddingModel;
18   -import org.springframework.ai.image.ImageModel;
19   -import org.springframework.ai.vectorstore.SimpleVectorStore;
20   -import org.springframework.ai.vectorstore.VectorStore;
21   -import org.springframework.context.annotation.Lazy;
22 11 import org.springframework.stereotype.Service;
23 12 import org.springframework.validation.annotation.Validated;
24 13  
25 14 import java.util.List;
26 15  
27 16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
28   -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
  17 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE;
  18 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS;
29 19  
30 20 /**
31 21 * AI API 密钥 Service 实现类
... ... @@ -39,14 +29,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
39 29 @Resource
40 30 private AiApiKeyMapper apiKeyMapper;
41 31  
42   - // TODO @芋艿:后续要不要改?
43   - @Resource
44   - @Lazy // 延迟加载,解决渲染依赖
45   - private AiChatModelService chatModelService;
46   -
47   - @Resource
48   - private AiModelFactory modelFactory;
49   -
50 32 @Override
51 33 public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
52 34 // 插入
... ... @@ -105,57 +87,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
105 87 return apiKeyMapper.selectList();
106 88 }
107 89  
108   - // ========== 与 spring-ai 集成 ==========
109   -
110   - @Override
111   - public ChatModel getChatModel(Long id) {
112   - AiApiKeyDO apiKey = validateApiKey(id);
113   - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
114   - return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
115   - }
116   -
117   - @Override
118   - public ImageModel getImageModel(AiPlatformEnum platform) {
119   - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
120   - if (apiKey == null) {
121   - throw exception(API_KEY_IMAGE_NODE_FOUND, platform.getName());
122   - }
123   - return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
124   - }
125   -
126 90 @Override
127   - public MidjourneyApi getMidjourneyApi() {
128   - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(
129   - AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
  91 + public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) {
  92 + AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status);
130 93 if (apiKey == null) {
131   - throw exception(API_KEY_MIDJOURNEY_NOT_FOUND);
132   - }
133   - return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
134   - }
135   -
136   - @Override
137   - public SunoApi getSunoApi() {
138   - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(
139   - AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
140   - if (apiKey == null) {
141   - throw exception(API_KEY_SUNO_NOT_FOUND);
  94 + throw exception(API_KEY_NOT_EXISTS);
142 95 }
143   - return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
144   - }
145   -
146   - @Override
147   - public VectorStore getOrCreateVectorStoreByModelId(Long modelId) {
148   - // 获取模型 + 密钥
149   - AiChatModelDO chatModel = chatModelService.validateChatModel(modelId);
150   - AiApiKeyDO apiKey = validateApiKey(chatModel.getKeyId());
151   - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
152   -
153   - // 创建或获取 EmbeddingModel 对象
154   - EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(),
155   - apiKey.getUrl(), chatModel.getModel());
156   -
157   - // 创建或获取 VectorStore 对象
158   - return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel);
  96 + return apiKey;
159 97 }
160 98  
161 99 }
162 100 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java deleted
1   -package cn.iocoder.yudao.module.ai.service.model;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
5   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
6   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
7   -import jakarta.validation.Valid;
8   -
9   -import java.util.Collection;
10   -import java.util.List;
11   -
12   -import java.util.Set;
13   -
14   -/**
15   - * AI 聊天模型 Service 接口
16   - *
17   - * @author fansili
18   - * @since 2024/4/24 19:42
19   - */
20   -public interface AiChatModelService {
21   -
22   - /**
23   - * 创建聊天模型
24   - *
25   - * @param createReqVO 创建信息
26   - * @return 编号
27   - */
28   - Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO);
29   -
30   - /**
31   - * 更新聊天模型
32   - *
33   - * @param updateReqVO 更新信息
34   - */
35   - void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO);
36   -
37   - /**
38   - * 删除聊天模型
39   - *
40   - * @param id 编号
41   - */
42   - void deleteChatModel(Long id);
43   -
44   - /**
45   - * 获得聊天模型
46   - *
47   - * @param id 编号
48   - * @return 聊天模型
49   - */
50   - AiChatModelDO getChatModel(Long id);
51   -
52   - /**
53   - * 获得默认的聊天模型
54   - *
55   - * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常
56   - *
57   - * @return 聊天模型
58   - */
59   - AiChatModelDO getRequiredDefaultChatModel();
60   -
61   - /**
62   - * 获得聊天模型分页
63   - *
64   - * @param pageReqVO 分页查询
65   - * @return 聊天模型分页
66   - */
67   - PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO);
68   -
69   - /**
70   - * 校验聊天模型
71   - *
72   - * @param id 编号
73   - * @return 聊天模型
74   - */
75   - AiChatModelDO validateChatModel(Long id);
76   -
77   - /**
78   - * 获得聊天模型列表
79   - *
80   - * @param status 状态
81   - * @return 聊天模型列表
82   - */
83   - List<AiChatModelDO> getChatModelListByStatus(Integer status);
84   -
85   - /**
86   - * 获得聊天模型列表
87   - *
88   - * @param ids 编号数组
89   - * @return 模型列表
90   - */
91   - List<AiChatModelDO> getChatModelList(Collection<Long> ids);
92   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.model;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
  5 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  6 +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
4 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
9   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
10   -import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatModelMapper;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
  13 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  14 +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper;
11 15 import jakarta.annotation.Resource;
  16 +import org.springframework.ai.chat.model.ChatModel;
  17 +import org.springframework.ai.embedding.EmbeddingModel;
  18 +import org.springframework.ai.image.ImageModel;
  19 +import org.springframework.ai.vectorstore.SimpleVectorStore;
  20 +import org.springframework.ai.vectorstore.VectorStore;
12 21 import org.springframework.stereotype.Service;
13 22 import org.springframework.validation.annotation.Validated;
14 23  
15   -import java.util.Collection;
16 24 import java.util.List;
17 25  
18 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
19 27 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
20 28  
21 29 /**
22   - * AI 聊天模型 Service 实现类
  30 + * AI 模型 Service 实现类
23 31 *
24 32 * @author fansili
25 33 */
26 34 @Service
27 35 @Validated
28   -public class AiChatModelServiceImpl implements AiChatModelService {
  36 +public class AiChatModelServiceImpl implements AiModelService {
29 37  
30 38 @Resource
31 39 private AiApiKeyService apiKeyService;
32 40  
33 41 @Resource
34   - private AiChatModelMapper chatModelMapper;
  42 + private AiChatMapper modelMapper;
  43 +
  44 + @Resource
  45 + private AiModelFactory modelFactory;
35 46  
36 47 @Override
37   - public Long createChatModel(AiChatModelSaveReqVO createReqVO) {
  48 + public Long createModel(AiModelSaveReqVO createReqVO) {
38 49 // 1. 校验
39 50 AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
40 51 apiKeyService.validateApiKey(createReqVO.getKeyId());
41 52  
42 53 // 2. 插入
43   - AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class);
44   - chatModelMapper.insert(chatModel);
45   - return chatModel.getId();
  54 + AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class);
  55 + modelMapper.insert(model);
  56 + return model.getId();
46 57 }
47 58  
48 59 @Override
49   - public void updateChatModel(AiChatModelSaveReqVO updateReqVO) {
  60 + public void updateModel(AiModelSaveReqVO updateReqVO) {
50 61 // 1. 校验
51   - validateChatModelExists(updateReqVO.getId());
  62 + validateModelExists(updateReqVO.getId());
52 63 AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
53 64 apiKeyService.validateApiKey(updateReqVO.getKeyId());
54 65  
55 66 // 2. 更新
56   - AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class);
57   - chatModelMapper.updateById(updateObj);
  67 + AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class);
  68 + modelMapper.updateById(updateObj);
58 69 }
59 70  
60 71 @Override
61   - public void deleteChatModel(Long id) {
  72 + public void deleteModel(Long id) {
62 73 // 校验存在
63   - validateChatModelExists(id);
  74 + validateModelExists(id);
64 75 // 删除
65   - chatModelMapper.deleteById(id);
  76 + modelMapper.deleteById(id);
66 77 }
67 78  
68   - private AiChatModelDO validateChatModelExists(Long id) {
69   - AiChatModelDO model = chatModelMapper.selectById(id);
70   - if (chatModelMapper.selectById(id) == null) {
71   - throw exception(CHAT_MODEL_NOT_EXISTS);
  79 + private AiModelDO validateModelExists(Long id) {
  80 + AiModelDO model = modelMapper.selectById(id);
  81 + if (modelMapper.selectById(id) == null) {
  82 + throw exception(MODEL_NOT_EXISTS);
72 83 }
73 84 return model;
74 85 }
75 86  
76 87 @Override
77   - public AiChatModelDO getChatModel(Long id) {
78   - return chatModelMapper.selectById(id);
  88 + public AiModelDO getModel(Long id) {
  89 + return modelMapper.selectById(id);
79 90 }
80 91  
81 92 @Override
82   - public AiChatModelDO getRequiredDefaultChatModel() {
83   - AiChatModelDO model = chatModelMapper.selectFirstByStatus(CommonStatusEnum.ENABLE.getStatus());
  93 + public AiModelDO getRequiredDefaultModel(Integer type) {
  94 + AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus());
84 95 if (model == null) {
85   - throw exception(CHAT_MODEL_DEFAULT_NOT_EXISTS);
  96 + throw exception(MODEL_DEFAULT_NOT_EXISTS);
86 97 }
87 98 return model;
88 99 }
89 100  
90 101 @Override
91   - public PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO) {
92   - return chatModelMapper.selectPage(pageReqVO);
  102 + public PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO) {
  103 + return modelMapper.selectPage(pageReqVO);
93 104 }
94 105  
95 106 @Override
96   - public AiChatModelDO validateChatModel(Long id) {
97   - AiChatModelDO model = validateChatModelExists(id);
  107 + public AiModelDO validateModel(Long id) {
  108 + AiModelDO model = validateModelExists(id);
98 109 if (CommonStatusEnum.isDisable(model.getStatus())) {
99   - throw exception(CHAT_MODEL_DISABLE);
  110 + throw exception(MODEL_DISABLE);
100 111 }
101 112 return model;
102 113 }
103 114  
104 115 @Override
105   - public List<AiChatModelDO> getChatModelListByStatus(Integer status) {
106   - return chatModelMapper.selectList(status);
  116 + public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type,
  117 + String platform) {
  118 + return modelMapper.selectListByStatusAndType(status, type, platform);
  119 + }
  120 +
  121 + // ========== 与 Spring AI 集成 ==========
  122 +
  123 + @Override
  124 + public ChatModel getChatModel(Long id) {
  125 + AiModelDO model = validateModel(id);
  126 + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
  127 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  128 + return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
  129 + }
  130 +
  131 + @Override
  132 + public ImageModel getImageModel(Long id) {
  133 + AiModelDO model = validateModel(id);
  134 + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
  135 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  136 + return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
  137 + }
  138 +
  139 + @Override
  140 + public MidjourneyApi getMidjourneyApi() {
  141 + AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(
  142 + AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
  143 + return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
  144 + }
  145 +
  146 + @Override
  147 + public SunoApi getSunoApi() {
  148 + AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(
  149 + AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
  150 + return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
107 151 }
108 152  
109 153 @Override
110   - public List<AiChatModelDO> getChatModelList(Collection<Long> ids) {
111   - return chatModelMapper.selectBatchIds(ids);
  154 + public VectorStore getOrCreateVectorStore(Long id) {
  155 + // 获取模型 + 密钥
  156 + AiModelDO model = validateModel(id);
  157 + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
  158 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  159 +
  160 + // 创建或获取 EmbeddingModel 对象
  161 + EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(
  162 + platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
  163 +
  164 + // 创建或获取 VectorStore 对象
  165 + return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel);
112 166 }
113 167  
114 168 }
115 169 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
  4 +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
  9 +import jakarta.validation.Valid;
  10 +import org.springframework.ai.chat.model.ChatModel;
  11 +import org.springframework.ai.image.ImageModel;
  12 +import org.springframework.ai.vectorstore.VectorStore;
  13 +
  14 +import javax.annotation.Nullable;
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * AI 模型 Service 接口
  19 + *
  20 + * @author fansili
  21 + * @since 2024/4/24 19:42
  22 + */
  23 +public interface AiModelService {
  24 +
  25 + /**
  26 + * 创建模型
  27 + *
  28 + * @param createReqVO 创建信息
  29 + * @return 编号
  30 + */
  31 + Long createModel(@Valid AiModelSaveReqVO createReqVO);
  32 +
  33 + /**
  34 + * 更新模型
  35 + *
  36 + * @param updateReqVO 更新信息
  37 + */
  38 + void updateModel(@Valid AiModelSaveReqVO updateReqVO);
  39 +
  40 + /**
  41 + * 删除模型
  42 + *
  43 + * @param id 编号
  44 + */
  45 + void deleteModel(Long id);
  46 +
  47 + /**
  48 + * 获得模型
  49 + *
  50 + * @param id 编号
  51 + * @return 模型
  52 + */
  53 + AiModelDO getModel(Long id);
  54 +
  55 + /**
  56 + * 获得默认的模型
  57 + *
  58 + * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常
  59 + *
  60 + * @return 模型
  61 + */
  62 + AiModelDO getRequiredDefaultModel(Integer type);
  63 +
  64 + /**
  65 + * 获得模型分页
  66 + *
  67 + * @param pageReqVO 分页查询
  68 + * @return 模型分页
  69 + */
  70 + PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO);
  71 +
  72 + /**
  73 + * 校验模型是否可使用
  74 + *
  75 + * @param id 编号
  76 + * @return 模型
  77 + */
  78 + AiModelDO validateModel(Long id);
  79 +
  80 + /**
  81 + * 获得模型列表
  82 + *
  83 + * @param status 状态
  84 + * @param type 类型
  85 + * @param platform 平台,允许空
  86 + * @return 模型列表
  87 + */
  88 + List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type,
  89 + @Nullable String platform);
  90 +
  91 + // ========== 与 Spring AI 集成 ==========
  92 +
  93 + /**
  94 + * 获得 ChatModel 对象
  95 + *
  96 + * @param id 编号
  97 + * @return ChatModel 对象
  98 + */
  99 + ChatModel getChatModel(Long id);
  100 +
  101 + /**
  102 + * 获得 ImageModel 对象
  103 + *
  104 + * @param id 编号
  105 + * @return ImageModel 对象
  106 + */
  107 + ImageModel getImageModel(Long id);
  108 +
  109 + /**
  110 + * 获得 MidjourneyApi 对象
  111 + *
  112 + * @return MidjourneyApi 对象
  113 + */
  114 + MidjourneyApi getMidjourneyApi();
  115 +
  116 + /**
  117 + * 获得 SunoApi 对象
  118 + *
  119 + * @return SunoApi 对象
  120 + */
  121 + SunoApi getSunoApi();
  122 +
  123 + /**
  124 + * 获得 VectorStore 对象
  125 + *
  126 + * @param id 编号
  127 + * @return VectorStore 对象
  128 + */
  129 + VectorStore getOrCreateVectorStore(Long id);
  130 +
  131 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java
... ... @@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
16 16 import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
17 17 import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
18 18 import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
19   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  19 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
20 20 import cn.iocoder.yudao.module.infra.api.file.FileApi;
21 21 import jakarta.annotation.Resource;
22 22 import lombok.extern.slf4j.Slf4j;
... ... @@ -41,7 +41,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXIS
41 41 public class AiMusicServiceImpl implements AiMusicService {
42 42  
43 43 @Resource
44   - private AiApiKeyService apiKeyService;
  44 + private AiModelService modelService;
45 45  
46 46 @Resource
47 47 private AiMusicMapper musicMapper;
... ... @@ -53,7 +53,7 @@ public class AiMusicServiceImpl implements AiMusicService {
53 53 @Transactional(rollbackFor = Exception.class)
54 54 public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
55 55 // 1. 调用 Suno 生成音乐
56   - SunoApi sunoApi = apiKeyService.getSunoApi();
  56 + SunoApi sunoApi = modelService.getSunoApi();
57 57 List<SunoApi.MusicData> musicDataList;
58 58 if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
59 59 // 1.1 描述模式
... ... @@ -88,7 +88,7 @@ public class AiMusicServiceImpl implements AiMusicService {
88 88 log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size());
89 89  
90 90 // GET 请求,为避免参数过长,分批次处理
91   - SunoApi sunoApi = apiKeyService.getSunoApi();
  91 + SunoApi sunoApi = modelService.getSunoApi();
92 92 CollUtil.split(streamingTask, 36).forEach(chunkList -> {
93 93 Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);
94 94 List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.write;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
6 7 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
7 8 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
8 9 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
... ... @@ -11,17 +12,16 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 12 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 13 import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
13 14 import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
14   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
15 15 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
  16 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
16 17 import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
17 18 import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;
18 19 import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
19 20 import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
20 21 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
21 22 import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
22   -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
23   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
24 23 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
  24 +import cn.iocoder.yudao.module.ai.service.model.AiModelService;
25 25 import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
26 26 import jakarta.annotation.Resource;
27 27 import lombok.extern.slf4j.Slf4j;
... ... @@ -54,17 +54,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXIS
54 54 public class AiWriteServiceImpl implements AiWriteService {
55 55  
56 56 @Resource
57   - private AiApiKeyService apiKeyService;
58   - @Resource
59   - private AiChatModelService chatModalService;
  57 + private AiModelService chatModalService;
60 58 @Resource
61 59 private AiChatRoleService chatRoleService;
62 60  
63 61 @Resource
64   - private DictDataApi dictDataApi;
  62 + private AiWriteMapper writeMapper;
65 63  
66 64 @Resource
67   - private AiWriteMapper writeMapper;
  65 + private DictDataApi dictDataApi;
68 66  
69 67 @Override
70 68 public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
... ... @@ -72,17 +70,17 @@ public class AiWriteServiceImpl implements AiWriteService {
72 70 AiChatRoleDO writeRole = CollUtil.getFirst(
73 71 chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));
74 72 // 1.1 获取写作执行模型
75   - AiChatModelDO model = getModel(writeRole);
  73 + AiModelDO model = getModel(writeRole);
76 74 // 1.2 获取角色设定消息
77 75 String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage())
78 76 ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();
79 77 // 1.3 校验平台
80 78 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
81   - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  79 + StreamingChatModel chatModel = chatModalService.getChatModel(model.getKeyId());
82 80  
83 81 // 2. 插入写作信息
84   - AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class,
85   - write -> write.setUserId(userId).setPlatform(platform.getPlatform()).setModel(model.getModel()));
  82 + AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId)
  83 + .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
86 84 writeMapper.insert(writeDO);
87 85  
88 86 // 3.1 构建 Prompt,并进行调用
... ... @@ -109,19 +107,19 @@ public class AiWriteServiceImpl implements AiWriteService {
109 107 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
110 108 }
111 109  
112   - private AiChatModelDO getModel(AiChatRoleDO writeRole) {
113   - AiChatModelDO model = null;
  110 + private AiModelDO getModel(AiChatRoleDO writeRole) {
  111 + AiModelDO model = null;
114 112 if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
115   - model = chatModalService.getChatModel(writeRole.getModelId());
  113 + model = chatModalService.getModel(writeRole.getModelId());
116 114 }
117 115 if (model == null) {
118   - model = chatModalService.getRequiredDefaultChatModel();
  116 + model = chatModalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
119 117 }
120 118 Assert.notNull(model, "[AI] 获取不到模型");
121 119 return model;
122 120 }
123 121  
124   - private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) {
  122 + private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
125 123 // 1. 构建 message 列表
126 124 List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
127 125 // 2. 构建 options 对象
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiModelTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.core.enums;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.Getter;
  5 +import lombok.RequiredArgsConstructor;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * AI 模型类型的枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@RequiredArgsConstructor
  16 +public enum AiModelTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + CHAT(1, "对话"),
  19 + IMAGE(2, "图片"),
  20 + VOICE(3, "语音"),
  21 + VIDEO(4, "视频"),
  22 + EMBEDDING(5, "向量"),
  23 + RERANK(6, "重排序");
  24 +
  25 + /**
  26 + * 类型
  27 + */
  28 + private final Integer type;
  29 + /**
  30 + * 类型名
  31 + */
  32 + private final String name;
  33 +
  34 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getType).toArray(Integer[]::new);
  35 +
  36 + @Override
  37 + public Integer[] array() {
  38 + return ARRAYS;
  39 + }
  40 +
  41 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java
1 1 package cn.iocoder.yudao.framework.ai.core.enums;
2 2  
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
3 4 import lombok.AllArgsConstructor;
4 5 import lombok.Getter;
5 6  
  7 +import java.util.Arrays;
  8 +
6 9 /**
7 10 * AI 模型平台
8 11 *
... ... @@ -10,7 +13,7 @@ import lombok.Getter;
10 13 */
11 14 @Getter
12 15 @AllArgsConstructor
13   -public enum AiPlatformEnum {
  16 +public enum AiPlatformEnum implements ArrayValuable<String> {
14 17  
15 18 // ========== 国内平台 ==========
16 19  
... ... @@ -44,6 +47,8 @@ public enum AiPlatformEnum {
44 47 */
45 48 private final String name;
46 49  
  50 + public static final String[] ARRAYS = Arrays.stream(values()).map(AiPlatformEnum::getPlatform).toArray(String[]::new);
  51 +
47 52 public static AiPlatformEnum validatePlatform(String platform) {
48 53 for (AiPlatformEnum platformEnum : AiPlatformEnum.values()) {
49 54 if (platformEnum.getPlatform().equals(platform)) {
... ... @@ -53,4 +58,9 @@ public enum AiPlatformEnum {
53 58 throw new IllegalArgumentException("非法平台: " + platform);
54 59 }
55 60  
  61 + @Override
  62 + public String[] array() {
  63 + return ARRAYS;
  64 + }
  65 +
56 66 }
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -456,25 +456,4 @@ public class AiModelFactoryImpl implements AiModelFactory {
456 456 return vectorStore;
457 457 }
458 458  
459   - /**
460   - * 创建向量存储文件
461   - *
462   - * @param embeddingModel 嵌入模型
463   - * @return 向量存储文件
464   - */
465   - private File createVectorStoreFile(EmbeddingModel embeddingModel) {
466   - // 获取简单类名
467   - String simpleClassName = embeddingModel.getClass().getSimpleName();
468   - // 获取用户主目录
469   - String userHome = FileUtil.getUserHomePath();
470   - // 创建vector_store目录
471   - File vectorStoreDir = new File(userHome, "vector_store");
472   - if (!vectorStoreDir.exists()) {
473   - vectorStoreDir.mkdirs();
474   - }
475   -
476   - // 创建文件
477   - return new File(vectorStoreDir, "simple_" + simpleClassName + ".json");
478   - }
479   -
480 459 }
... ...